summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt272
-rw-r--r--sql/authors.h3
-rw-r--r--sql/backup.cc5
-rw-r--r--sql/bounded_queue.h10
-rw-r--r--sql/create_options.cc48
-rw-r--r--sql/datadict.cc15
-rw-r--r--sql/datadict.h6
-rw-r--r--sql/debug_sync.cc67
-rw-r--r--sql/debug_sync.h8
-rw-r--r--sql/derror.cc8
-rw-r--r--sql/discover.cc49
-rw-r--r--sql/discover.h4
-rw-r--r--sql/event_data_objects.cc51
-rw-r--r--sql/event_data_objects.h13
-rw-r--r--sql/event_db_repository.cc90
-rw-r--r--sql/event_queue.cc6
-rw-r--r--sql/event_scheduler.cc30
-rw-r--r--sql/events.cc45
-rw-r--r--sql/events.h2
-rw-r--r--sql/field.cc1622
-rw-r--r--sql/field.h2743
-rw-r--r--sql/field_conv.cc39
-rw-r--r--sql/filesort.cc1719
-rw-r--r--sql/filesort.h98
-rw-r--r--sql/filesort_utils.cc75
-rw-r--r--sql/filesort_utils.h218
-rw-r--r--sql/gcalc_slicescan.cc2
-rw-r--r--sql/gen_sql_yacc_ora_yy.cmake15
-rw-r--r--sql/grant.cc108
-rw-r--r--sql/grant.h99
-rw-r--r--sql/group_by_handler.cc13
-rw-r--r--sql/group_by_handler.h6
-rw-r--r--sql/gstream.cc8
-rw-r--r--sql/ha_partition.cc374
-rw-r--r--sql/ha_partition.h31
-rw-r--r--sql/ha_sequence.cc36
-rw-r--r--sql/ha_sequence.h4
-rw-r--r--sql/handle_connections_win.cc42
-rw-r--r--sql/handle_connections_win.h1
-rw-r--r--sql/handler.cc1784
-rw-r--r--sql/handler.h549
-rw-r--r--sql/hash_filo.h29
-rw-r--r--sql/hostname.cc13
-rw-r--r--sql/item.cc448
-rw-r--r--sql/item.h523
-rw-r--r--sql/item_cmpfunc.cc511
-rw-r--r--sql/item_cmpfunc.h167
-rw-r--r--sql/item_create.cc2105
-rw-r--r--sql/item_create.h130
-rw-r--r--sql/item_func.cc394
-rw-r--r--sql/item_func.h464
-rw-r--r--sql/item_geofunc.cc1308
-rw-r--r--sql/item_geofunc.h167
-rw-r--r--sql/item_inetfunc.cc967
-rw-r--r--sql/item_inetfunc.h226
-rw-r--r--sql/item_jsonfunc.cc344
-rw-r--r--sql/item_jsonfunc.h210
-rw-r--r--sql/item_row.cc19
-rw-r--r--sql/item_row.h3
-rw-r--r--sql/item_strfunc.cc358
-rw-r--r--sql/item_strfunc.h13
-rw-r--r--sql/item_subselect.cc167
-rw-r--r--sql/item_subselect.h179
-rw-r--r--sql/item_sum.cc333
-rw-r--r--sql/item_sum.h155
-rw-r--r--sql/item_timefunc.cc150
-rw-r--r--sql/item_timefunc.h39
-rw-r--r--sql/item_windowfunc.cc10
-rw-r--r--sql/item_windowfunc.h24
-rw-r--r--sql/item_xmlfunc.cc272
-rw-r--r--sql/item_xmlfunc.h40
-rw-r--r--sql/key.cc48
-rw-r--r--sql/key.h2
-rw-r--r--sql/keycaches.cc10
-rw-r--r--sql/lex.h26
-rw-r--r--sql/lex_string.h42
-rw-r--r--sql/lock.cc88
-rw-r--r--sql/lock.h10
-rw-r--r--sql/log.cc826
-rw-r--r--sql/log.h56
-rw-r--r--sql/log_event.cc11683
-rw-r--r--sql/log_event.h567
-rw-r--r--sql/log_event_client.cc3932
-rw-r--r--sql/log_event_old.cc18
-rw-r--r--sql/log_event_server.cc8524
-rw-r--r--sql/mdl.cc286
-rw-r--r--sql/mdl.h48
-rw-r--r--sql/multi_range_read.cc309
-rw-r--r--sql/my_decimal.h10
-rw-r--r--sql/my_json_writer.cc4
-rw-r--r--sql/mysql_install_db.cc4
-rw-r--r--sql/mysql_upgrade_service.cc6
-rw-r--r--sql/mysqld.cc1320
-rw-r--r--sql/mysqld.h196
-rw-r--r--sql/net_serv.cc32
-rw-r--r--sql/opt_index_cond_pushdown.cc16
-rw-r--r--sql/opt_range.cc395
-rw-r--r--sql/opt_range.h1
-rw-r--r--sql/opt_split.cc12
-rw-r--r--sql/opt_subselect.cc186
-rw-r--r--sql/opt_subselect.h1
-rw-r--r--sql/opt_trace.cc25
-rw-r--r--sql/partition_info.cc258
-rw-r--r--sql/partition_info.h135
-rw-r--r--sql/privilege.h738
-rw-r--r--sql/procedure.h22
-rw-r--r--sql/protocol.cc242
-rw-r--r--sql/protocol.h216
-rw-r--r--sql/proxy_protocol.cc3
-rw-r--r--sql/records.cc169
-rw-r--r--sql/records.h14
-rw-r--r--sql/repl_failsafe.cc10
-rw-r--r--sql/rowid_filter.cc15
-rw-r--r--sql/rpl_filter.cc34
-rw-r--r--sql/rpl_gtid.cc77
-rw-r--r--sql/rpl_gtid.h4
-rw-r--r--sql/rpl_injector.cc10
-rw-r--r--sql/rpl_injector.h3
-rw-r--r--sql/rpl_mi.cc16
-rw-r--r--sql/rpl_parallel.cc50
-rw-r--r--sql/rpl_parallel.h3
-rw-r--r--sql/rpl_reporting.cc4
-rw-r--r--sql/rpl_rli.cc36
-rw-r--r--sql/rpl_rli.h4
-rw-r--r--sql/rpl_tblmap.cc13
-rw-r--r--sql/rpl_utility.cc989
-rw-r--r--sql/rpl_utility.h4
-rw-r--r--sql/rpl_utility_server.cc1185
-rw-r--r--sql/scheduler.cc25
-rw-r--r--sql/scheduler.h20
-rw-r--r--sql/select_handler.cc83
-rw-r--r--sql/select_handler.h20
-rw-r--r--sql/semisync_master.cc6
-rw-r--r--sql/semisync_master.h2
-rw-r--r--sql/semisync_master_ack_receiver.cc2
-rw-r--r--sql/service_wsrep.cc60
-rw-r--r--sql/session_tracker.cc85
-rw-r--r--sql/session_tracker.h81
-rw-r--r--sql/set_var.cc102
-rw-r--r--sql/set_var.h23
-rw-r--r--sql/share/errmsg-utf8.txt192
-rw-r--r--sql/signal_handler.cc25
-rw-r--r--sql/slave.cc100
-rw-r--r--sql/sp.cc172
-rw-r--r--sql/sp.h54
-rw-r--r--sql/sp_cache.cc20
-rw-r--r--sql/sp_head.cc252
-rw-r--r--sql/sp_head.h98
-rw-r--r--sql/sp_pcontext.cc17
-rw-r--r--sql/sp_pcontext.h6
-rw-r--r--sql/sp_rcontext.cc1
-rw-r--r--sql/spatial.cc49
-rw-r--r--sql/spatial.h1
-rw-r--r--sql/sql_acl.cc1005
-rw-r--r--sql/sql_acl.h259
-rw-r--r--sql/sql_admin.cc31
-rw-r--r--sql/sql_alter.cc38
-rw-r--r--sql/sql_alter.h12
-rw-r--r--sql/sql_analyze_stmt.cc40
-rw-r--r--sql/sql_analyze_stmt.h170
-rw-r--r--sql/sql_array.h35
-rw-r--r--sql/sql_audit.cc2
-rw-r--r--sql/sql_audit.h29
-rw-r--r--sql/sql_base.cc634
-rw-r--r--sql/sql_base.h22
-rw-r--r--sql/sql_basic_types.h2
-rw-r--r--sql/sql_binlog.cc9
-rw-r--r--sql/sql_bitmap.h380
-rw-r--r--sql/sql_bootstrap.cc1
-rw-r--r--sql/sql_builtin.cc.in6
-rw-r--r--sql/sql_cache.cc19
-rw-r--r--sql/sql_cache.h2
-rw-r--r--sql/sql_class.cc725
-rw-r--r--sql/sql_class.h704
-rw-r--r--sql/sql_cmd.h22
-rw-r--r--sql/sql_connect.cc160
-rw-r--r--sql/sql_connect.h30
-rw-r--r--sql/sql_const.h7
-rw-r--r--sql/sql_cte.cc146
-rw-r--r--sql/sql_cte.h15
-rw-r--r--sql/sql_cursor.cc7
-rw-r--r--sql/sql_db.cc112
-rw-r--r--sql/sql_delete.cc128
-rw-r--r--sql/sql_delete.h3
-rw-r--r--sql/sql_derived.cc7
-rw-r--r--sql/sql_digest.cc2
-rw-r--r--sql/sql_error.cc121
-rw-r--r--sql/sql_error.h21
-rw-r--r--sql/sql_explain.cc5
-rw-r--r--sql/sql_explain.h4
-rw-r--r--sql/sql_expression_cache.h2
-rw-r--r--sql/sql_get_diagnostics.cc4
-rw-r--r--sql/sql_handler.cc21
-rw-r--r--sql/sql_help.cc25
-rw-r--r--sql/sql_hset.h23
-rw-r--r--sql/sql_i_s.h360
-rw-r--r--sql/sql_insert.cc681
-rw-r--r--sql/sql_insert.h19
-rw-r--r--sql/sql_join_cache.cc20
-rw-r--r--sql/sql_lex.cc1326
-rw-r--r--sql/sql_lex.h341
-rw-r--r--sql/sql_limit.h72
-rw-r--r--sql/sql_list.h3
-rw-r--r--sql/sql_load.cc50
-rw-r--r--sql/sql_locale.h4
-rw-r--r--sql/sql_manager.cc3
-rw-r--r--sql/sql_parse.cc1202
-rw-r--r--sql/sql_parse.h26
-rw-r--r--sql/sql_partition.cc324
-rw-r--r--sql/sql_partition.h7
-rw-r--r--sql/sql_partition_admin.cc92
-rw-r--r--sql/sql_plugin.cc210
-rw-r--r--sql/sql_plugin.h5
-rw-r--r--sql/sql_plugin_services.ic14
-rw-r--r--sql/sql_prepare.cc1063
-rw-r--r--sql/sql_prepare.h5
-rw-r--r--sql/sql_priv.h12
-rw-r--r--sql/sql_profile.cc75
-rw-r--r--sql/sql_profile.h9
-rw-r--r--sql/sql_reload.cc26
-rw-r--r--sql/sql_rename.cc165
-rw-r--r--sql/sql_rename.h3
-rw-r--r--sql/sql_repl.cc44
-rw-r--r--sql/sql_schema.h10
-rw-r--r--sql/sql_select.cc2170
-rw-r--r--sql/sql_select.h52
-rw-r--r--sql/sql_sequence.cc51
-rw-r--r--sql/sql_servers.cc106
-rw-r--r--sql/sql_show.cc2042
-rw-r--r--sql/sql_show.h12
-rw-r--r--sql/sql_signal.cc11
-rw-r--r--sql/sql_sort.h641
-rw-r--r--sql/sql_statistics.cc127
-rw-r--r--sql/sql_string.cc70
-rw-r--r--sql/sql_string.h106
-rw-r--r--sql/sql_table.cc1852
-rw-r--r--sql/sql_table.h20
-rw-r--r--sql/sql_tablespace.cc2
-rw-r--r--sql/sql_test.cc15
-rw-r--r--sql/sql_time.cc19
-rw-r--r--sql/sql_trigger.cc92
-rw-r--r--sql/sql_trigger.h2
-rw-r--r--sql/sql_truncate.cc34
-rw-r--r--sql/sql_tvc.cc66
-rw-r--r--sql/sql_tvc.h2
-rw-r--r--sql/sql_type.cc2591
-rw-r--r--sql/sql_type.h3405
-rw-r--r--sql/sql_type_geom.cc966
-rw-r--r--sql/sql_type_geom.h436
-rw-r--r--sql/sql_type_int.h54
-rw-r--r--sql/sql_type_json.cc6
-rw-r--r--sql/sql_type_string.cc104
-rw-r--r--sql/sql_type_string.h48
-rw-r--r--sql/sql_udf.cc15
-rw-r--r--sql/sql_union.cc1011
-rw-r--r--sql/sql_update.cc91
-rw-r--r--sql/sql_view.cc90
-rw-r--r--sql/sql_window.cc14
-rw-r--r--sql/sql_yacc.yy5130
-rw-r--r--sql/sql_yacc_ora.yy18429
-rw-r--r--sql/strfunc.cc18
-rw-r--r--sql/strfunc.h3
-rw-r--r--sql/structs.h3
-rw-r--r--sql/sys_vars.cc1050
-rw-r--r--sql/sys_vars.ic118
-rw-r--r--sql/table.cc710
-rw-r--r--sql/table.h283
-rw-r--r--sql/table_cache.cc349
-rw-r--r--sql/table_cache.h28
-rw-r--r--sql/temporary_tables.cc42
-rw-r--r--sql/thr_malloc.cc7
-rw-r--r--sql/thr_malloc.h4
-rw-r--r--sql/thread_cache.h210
-rw-r--r--sql/thread_pool_info.cc360
-rw-r--r--sql/threadpool.h23
-rw-r--r--sql/threadpool_common.cc81
-rw-r--r--sql/threadpool_generic.cc684
-rw-r--r--sql/threadpool_generic.h156
-rw-r--r--sql/threadpool_win.cc66
-rw-r--r--sql/transaction.cc131
-rw-r--r--sql/tztime.cc48
-rw-r--r--sql/tztime.h2
-rw-r--r--sql/uniques.cc106
-rw-r--r--sql/uniques.h7
-rw-r--r--sql/unireg.cc167
-rw-r--r--sql/unireg.h4
-rw-r--r--sql/upgrade_conf_file.cc11
-rw-r--r--sql/vers_string.h34
-rw-r--r--sql/vers_utils.h8
-rw-r--r--sql/wsrep_applier.cc84
-rw-r--r--sql/wsrep_applier.h37
-rw-r--r--sql/wsrep_binlog.cc6
-rw-r--r--sql/wsrep_client_service.cc4
-rw-r--r--sql/wsrep_dummy.cc7
-rw-r--r--sql/wsrep_high_priority_service.cc81
-rw-r--r--sql/wsrep_high_priority_service.h5
-rw-r--r--sql/wsrep_mysqld.cc479
-rw-r--r--sql/wsrep_mysqld.h164
-rw-r--r--sql/wsrep_schema.cc47
-rw-r--r--sql/wsrep_server_service.cc12
-rw-r--r--sql/wsrep_sst.cc34
-rw-r--r--sql/wsrep_thd.cc4
-rw-r--r--sql/wsrep_thd.h2
-rw-r--r--sql/wsrep_trans_observer.h119
-rw-r--r--sql/wsrep_utils.cc4
-rw-r--r--sql/wsrep_var.cc86
-rw-r--r--sql/wsrep_var.h9
-rw-r--r--sql/wsrep_xid.cc67
-rw-r--r--sql/wsrep_xid.h7
-rw-r--r--sql/xa.cc352
-rw-r--r--sql/xa.h14
311 files changed, 55959 insertions, 55940 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index e620c99cf8a..e4760e1daae 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -1,15 +1,15 @@
# Copyright (c) 2006, 2014, Oracle and/or its affiliates.
-# Copyright (c) 2010, 2018, MariaDB Corporation
-#
+# Copyright (c) 2010, 2020, MariaDB Corporation.
+#
# 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-1335 USA
@@ -36,31 +36,39 @@ IF(WITH_WSREP AND NOT EMBEDDED_LIBRARY)
wsrep_plugin.cc
service_wsrep.cc
)
- SET(WSREP_LIB wsrep-lib wsrep_api_v26)
+ MYSQL_ADD_PLUGIN(wsrep ${WSREP_SOURCES} MANDATORY NOT_EMBEDDED EXPORT_SYMBOLS LINK_LIBRARIES wsrep-lib wsrep_api_v26)
ELSE()
- SET(WSREP_SOURCES wsrep_dummy.cc)
+ ADD_LIBRARY(wsrep STATIC wsrep_dummy.cc)
+ ADD_DEPENDENCIES(wsrep GenError)
ENDIF()
INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/include
-${CMAKE_SOURCE_DIR}/sql
+${CMAKE_SOURCE_DIR}/sql
${PCRE_INCLUDES}
${ZLIB_INCLUDE_DIR}
${SSL_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/sql
+${CMAKE_SOURCE_DIR}/tpool
)
-
-
-
-
-
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
COMMAND gen_lex_token > lex_token.h
DEPENDS gen_lex_token
)
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.yy
+ COMMAND ${CMAKE_COMMAND}
+ "-DIN=${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy"
+ "-DOUT=${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.yy"
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/gen_sql_yacc_ora_yy.cmake
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy
+)
+
+ADD_CUSTOM_TARGET(gen_sql_yacc_ora_yy DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.yy)
+
ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER)
IF(SSL_DEFINES)
@@ -68,6 +76,8 @@ IF(SSL_DEFINES)
ENDIF()
SET (SQL_SOURCE
+ ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
../sql-common/client.c compat56.cc derror.cc des_key_file.cc
discover.cc ../sql-common/errmsg.c
field.cc field_conv.cc field_comp.cc
@@ -79,7 +89,8 @@ SET (SQL_SOURCE
item_create.cc item_func.cc item_geofunc.cc item_row.cc
item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc
key.cc log.cc lock.cc
- log_event.cc rpl_record.cc rpl_reporting.cc
+ log_event.cc log_event_server.cc
+ rpl_record.cc rpl_reporting.cc
log_event_old.cc rpl_record_old.cc
mf_iocache.cc my_decimal.cc
mysqld.cc net_serv.cc keycaches.cc
@@ -110,15 +121,17 @@ SET (SQL_SOURCE
rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc
event_queue.cc event_db_repository.cc
sql_tablespace.cc events.cc ../sql-common/my_user.c
- partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
+ partition_info.cc rpl_utility.cc rpl_utility_server.cc
+ rpl_injector.cc sql_locale.cc
rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc
sql_connect.cc scheduler.cc sql_partition_admin.cc
sql_profile.cc event_parse_data.cc sql_alter.cc
sql_signal.cc mdl.cc sql_admin.cc
transaction.cc sys_vars.cc sql_truncate.cc datadict.cc
- sql_reload.cc item_inetfunc.cc
+ sql_reload.cc
# added in MariaDB:
+ grant.cc
sql_explain.cc
sql_analyze_stmt.cc
sql_join_cache.cc
@@ -134,6 +147,8 @@ SET (SQL_SOURCE
semisync_master_ack_receiver.cc
sql_schema.cc
sql_type.cc sql_mode.cc sql_type_json.cc
+ sql_type_string.cc
+ sql_type_geom.cc
item_windowfunc.cc sql_window.cc
sql_cte.cc
item_vers.cc
@@ -142,14 +157,11 @@ SET (SQL_SOURCE
opt_split.cc
rowid_filter.cc rowid_filter.h
opt_trace.cc
- ${WSREP_SOURCES}
table_cache.cc encryption.cc temporary_tables.cc
proxy_protocol.cc backup.cc xa.cc
- ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
- ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
+ ${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)
@@ -164,10 +176,11 @@ IF ((CMAKE_SYSTEM_NAME MATCHES "Linux" OR
ENDIF()
SET(SQL_SOURCE ${SQL_SOURCE} threadpool_generic.cc)
SET(SQL_SOURCE ${SQL_SOURCE} threadpool_common.cc)
+ MYSQL_ADD_PLUGIN(thread_pool_info thread_pool_info.cc DEFAULT STATIC_ONLY NOT_EMBEDDED)
ENDIF()
IF(WIN32)
- SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc)
+ SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc nt_servc.cc)
ENDIF()
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
@@ -176,146 +189,116 @@ MYSQL_ADD_PLUGIN(sql_sequence ha_sequence.cc STORAGE_ENGINE MANDATORY STATIC_ONL
RECOMPILE_FOR_EMBEDDED)
ADD_LIBRARY(sql STATIC ${SQL_SOURCE})
+MAYBE_DISABLE_IPO(sql)
DTRACE_INSTRUMENT(sql)
-TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS}
- mysys mysys_ssl dbug strings vio pcre
+TARGET_LINK_LIBRARIES(sql
+ mysys mysys_ssl dbug strings vio pcre2-8
+ tpool
${LIBWRAP} ${LIBCRYPT} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}
- ${WSREP_LIB}
${SSL_LIBRARIES}
${LIBSYSTEMD})
+IF(TARGET pcre2)
+ ADD_DEPENDENCIES(sql pcre2)
+ENDIF()
+
+FOREACH(se aria partition perfschema sql_sequence wsrep)
+ # These engines are used directly in sql sources.
+ IF(TARGET ${se})
+ TARGET_LINK_LIBRARIES(sql ${se})
+ ENDIF()
+ENDFOREACH()
+
IF(WIN32)
- SET(MYSQLD_SOURCE main.cc nt_servc.cc message.rc)
- TARGET_LINK_LIBRARIES(sql psapi)
+ SET(MYSQLD_SOURCE main.cc message.rc)
ELSE()
SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL})
ENDIF()
+IF(MSVC)
+ SET(libs_to_export_symbols sql mysys dbug strings)
+ # Create shared library of already compiled object
+ # Export all symbols from selected libraries, to be used
+ # by plugins
+ ADD_LIBRARY(server SHARED
+ $<TARGET_OBJECTS:sql>
+ $<TARGET_OBJECTS:mysys>
+ $<TARGET_OBJECTS:dbug>
+ $<TARGET_OBJECTS:strings>
+ ${PROJECT_BINARY_DIR}/versioninfo_dll.rc
+ )
-IF(MSVC AND NOT WITHOUT_DYNAMIC_PLUGINS)
-
- # mysqld.exe must to export symbols from some specific libs.
- # These symbols are used by dynamic plugins, that "link" to mysqld.
- #
- # To do that, we
- #
- # 1. Generate mysqld_lib.def text file with all symbols from static
- # libraries mysys, dbug, strings, sql.
- # 2. Then we call
- # lib.exe /DEF:mysqld_lib.def ...
- # to create import library mysqld_lib.lib and export library mysqld_lib.exp
- # 3. mysqld.exe links with mysqld_lib.exp (exporting symbols)
- # 4. plugins link with mysqld_lib.lib (importing symbols)
- #
- # We do not not regenerate .def, .lib and .exp
- # without necessity.E.g source modifications, that do not
- # change list of exported symbols, will not result in a relink for plugins.
-
- SET(MYSQLD_DEF ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib${CMAKE_CFG_INTDIR}.def)
- SET(MYSQLD_EXP ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib${CMAKE_CFG_INTDIR}.exp)
- SET(MYSQLD_LIB ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib${CMAKE_CFG_INTDIR}.lib)
- SET(MYSQLD_CORELIBS sql mysys dbug strings)
- FOREACH (CORELIB ${MYSQLD_CORELIBS})
- SET (LIB_LOCATIONS ${LIB_LOCATIONS} $<TARGET_FILE:${CORELIB}>)
- ENDFOREACH (CORELIB)
-
- SET(_PLATFORM x86)
- IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
- SET(_PLATFORM x64)
- ENDIF()
- # Create a cmake script to generate import and export libs
- # from a .def file
- SET(CMAKE_CONFIGURABLE_FILE_CONTENT "
- IF ((mysqld_lib\${CFG}.def IS_NEWER_THAN mysqld_lib\${CFG}.lib) OR
- (mysqld_lib\${CFG}.def IS_NEWER_THAN mysqld_lib\${CFG}.exp))
- FILE(REMOVE mysqld_lib\${CFG}.lib mysqld_lib\${CFG}.exp)
- SET(ENV{VS_UNICODE_OUTPUT})
- EXECUTE_PROCESS (
- COMMAND \"${CMAKE_LINKER}\" /lib /NAME:mysqld.exe \"/DEF:${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib\${CFG}.def\" /MACHINE:${_PLATFORM}
- RESULT_VARIABLE ret)
- IF(NOT ret EQUAL 0)
- MESSAGE(FATAL_ERROR \"process failed ret=\${ret}\")
- ENDIF()
+ # We need to add all dependencies of sql/mysys/dbug/strings
+ # to link the shared library
+ SET(all_deps)
+ FOREACH(lib ${libs_to_export_symbols})
+ GET_TARGET_PROPERTY(deps ${lib} LINK_LIBRARIES)
+ IF(deps)
+ LIST(APPEND all_deps ${deps})
ENDIF()
- ")
-
- CONFIGURE_FILE(
- ${PROJECT_SOURCE_DIR}/cmake/configurable_file_content.in
- make_mysqld_lib.cmake
- @ONLY)
-
- IF(CMAKE_VERSION VERSION_GREATER "3.2.0")
- SET(MYSQLD_LIB_BYPRODUCTS BYPRODUCTS ${MYSQLD_DEF} ${MYSQLD_LIB} ${MYSQLD_EXP})
- ENDIF()
-
- ADD_CUSTOM_COMMAND(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib.stamp
- ${MYSQLD_LIB_BYPRODUCTS}
- COMMENT "Generating ${MYSQLD_DEF}, ${MYSQLD_LIB}, ${MYSQLD_EXP}"
- COMMAND cscript //nologo ${PROJECT_SOURCE_DIR}/win/create_def_file.js
- ${_PLATFORM} /forLib ${LIB_LOCATIONS} > ${MYSQLD_DEF}.tmp
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MYSQLD_DEF}.tmp ${MYSQLD_DEF}
- COMMAND ${CMAKE_COMMAND} -E remove ${MYSQLD_DEF}.tmp
- COMMAND ${CMAKE_COMMAND} "-DCFG=${CMAKE_CFG_INTDIR}" -P make_mysqld_lib.cmake
- COMMAND ${CMAKE_COMMAND} -E touch mysqld_lib.stamp
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${MYSQLD_CORELIBS}
+ ENDFOREACH()
+ LIST(REMOVE_DUPLICATES all_deps)
+ FOREACH(lib ${libs_to_export_symbols})
+ LIST(REMOVE_ITEM all_deps ${lib})
+ ENDFOREACH()
+
+ TARGET_LINK_LIBRARIES(server PRIVATE
+ ${all_deps}
+ sql_builtins
)
-
- ADD_CUSTOM_TARGET(gen_mysqld_lib DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/mysqld_lib.stamp)
- ADD_LIBRARY(mysqld_import_lib UNKNOWN IMPORTED GLOBAL)
- SET_TARGET_PROPERTIES(mysqld_import_lib PROPERTIES IMPORTED_LOCATION ${MYSQLD_LIB})
+ SET_TARGET_PROPERTIES(server PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+ MYSQL_INSTALL_TARGETS(server DESTINATION ${INSTALL_BINDIR} COMPONENT Server)
ENDIF()
-MYSQL_ADD_EXECUTABLE(mysqld ${MYSQLD_SOURCE} DESTINATION ${INSTALL_SBINDIR} COMPONENT Server)
+ADD_LIBRARY(sql_builtins STATIC ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc)
+TARGET_LINK_LIBRARIES(sql_builtins ${MYSQLD_STATIC_PLUGIN_LIBS})
-IF(APPLE)
- # Add CoreServices framework since some dloadable plugins may need it
- FIND_LIBRARY(CORESERVICES NAMES CoreServices)
- IF(CORESERVICES)
- TARGET_LINK_LIBRARIES(mysqld LINK_PRIVATE ${CORESERVICES})
- ENDIF()
-ENDIF()
+MYSQL_ADD_EXECUTABLE(mariadbd ${MYSQLD_SOURCE} DESTINATION ${INSTALL_SBINDIR} COMPONENT Server)
+
+IF(APPLE)
+ # Add CoreServices framework since some dloadable plugins may need it
+ FIND_LIBRARY(CORESERVICES NAMES CoreServices)
+ IF(CORESERVICES)
+ TARGET_LINK_LIBRARIES(mariadbd LINK_PRIVATE ${CORESERVICES})
+ ENDIF()
+ENDIF()
IF(NOT WITHOUT_DYNAMIC_PLUGINS)
IF(NOT MSVC)
- SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE)
+ SET_TARGET_PROPERTIES(mariadbd PROPERTIES ENABLE_EXPORTS TRUE)
ENDIF()
- GET_TARGET_PROPERTY(mysqld_link_flags mysqld LINK_FLAGS)
+ GET_TARGET_PROPERTY(mysqld_link_flags mariadbd LINK_FLAGS)
IF(NOT mysqld_link_flags)
SET(mysqld_link_flags)
ENDIF()
- IF (MINGW OR CYGWIN)
- SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} -Wl,--export-all-symbols")
- ENDIF()
- IF(MSVC)
- SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} \"${MYSQLD_EXP}\"")
- ADD_DEPENDENCIES(mysqld gen_mysqld_lib)
- ENDIF()
ENDIF(NOT WITHOUT_DYNAMIC_PLUGINS)
-TARGET_LINK_LIBRARIES(mysqld LINK_PRIVATE sql)
+IF(MSVC)
+ TARGET_LINK_LIBRARIES(mariadbd server)
+ELSE()
+ TARGET_LINK_LIBRARIES(mariadbd LINK_PRIVATE sql sql_builtins)
+ENDIF()
# Provide plugins with minimal set of libraries
SET(INTERFACE_LIBS ${LIBRT})
IF(INTERFACE_LIBS)
- TARGET_LINK_LIBRARIES(mysqld LINK_PUBLIC ${INTERFACE_LIBS})
+ TARGET_LINK_LIBRARIES(mariadbd LINK_PUBLIC ${INTERFACE_LIBS})
ENDIF()
# On Solaris, some extra effort is required in order to get dtrace probes
# from static libraries
-DTRACE_INSTRUMENT_STATIC_LIBS(mysqld
+DTRACE_INSTRUMENT_STATIC_LIBS(mariadbd
"sql;mysys;mysys_ssl;${MYSQLD_STATIC_PLUGIN_LIBS}")
SET(WITH_MYSQLD_LDFLAGS "" CACHE STRING "Additional linker flags for mysqld")
MARK_AS_ADVANCED(WITH_MYSQLD_LDFLAGS)
IF(WITH_MYSQLD_LDFLAGS)
- GET_TARGET_PROPERTY(MYSQLD_LINK_FLAGS mysqld LINK_FLAGS)
+ GET_TARGET_PROPERTY(MYSQLD_LINK_FLAGS mariadbd LINK_FLAGS)
IF(NOT MYSQLD_LINK_FLAGS)
SET(MYSQLD_LINK_FLAGS)
ENDIF()
- SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS
+ SET_TARGET_PROPERTIES(mariadbd PROPERTIES LINK_FLAGS
"${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LDFLAGS}")
ENDIF()
@@ -353,8 +336,7 @@ IF (NOT BISON_FOUND)
ELSE()
BISON_TARGET(gen_sql_yacc ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
COMPILE_FLAGS "-p MYSQL")
-
- BISON_TARGET(gen_sql_yacc_ora ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc_ora.yy ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
+ BISON_TARGET(gen_sql_yacc_ora ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.yy ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
COMPILE_FLAGS "-p ORA")
ENDIF()
@@ -370,11 +352,11 @@ ADD_CUSTOM_COMMAND(
DEPENDS gen_lex_hash
)
-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 mysys_ssl)
+MYSQL_ADD_EXECUTABLE(mariadb-tzinfo-to-sql tztime.cc COMPONENT Server)
+SET_TARGET_PROPERTIES(mariadb-tzinfo-to-sql PROPERTIES COMPILE_FLAGS "-DTZINFO2SQL")
+TARGET_LINK_LIBRARIES(mariadb-tzinfo-to-sql mysys mysys_ssl)
-ADD_CUSTOM_TARGET(
+ADD_CUSTOM_TARGET(
GenServerSource
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
@@ -382,6 +364,8 @@ ADD_CUSTOM_TARGET(
${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc
)
+ADD_DEPENDENCIES(GenServerSource gen_sql_yacc_ora_yy)
+
IF(WIN32 OR HAVE_DLOPEN AND NOT DISABLE_SHARED)
ADD_LIBRARY(udf_example MODULE udf_example.c udf_example.def)
SET_TARGET_PROPERTIES(udf_example PROPERTIES PREFIX "")
@@ -392,7 +376,7 @@ CONFIGURE_FILE(
${CMAKE_SOURCE_DIR}/cmake/make_dist.cmake.in
${CMAKE_BINARY_DIR}/make_dist.cmake @ONLY)
-ADD_CUSTOM_TARGET(dist
+ADD_CUSTOM_TARGET(dist
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/make_dist.cmake
DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc.hh
DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.hh
@@ -405,12 +389,9 @@ ADD_CUSTOM_TARGET(distclean
VERBATIM
)
-IF(INSTALL_LAYOUT STREQUAL "STANDALONE")
-
-# Install initial database on windows
-IF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
-
- IF(MSVC_IDE OR CMAKE_GENERATOR MATCHES "Xcode")
+# Install initial database (default on windows, optional target elsewhere)
+IF(TARGET mariadbd AND NOT CMAKE_CROSSCOMPILING)
+ IF(GENERATOR_IS_MULTI_CONFIG)
SET (CONFIG_PARAM -DCONFIG=${CMAKE_CFG_INTDIR})
ENDIF()
MAKE_DIRECTORY(${CMAKE_CURRENT_BINARY_DIR}/data)
@@ -422,22 +403,23 @@ IF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
${CONFIG_PARAM}
-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}"
-DBINDIR="${CMAKE_CURRENT_BINARY_DIR}"
- -DMYSQLD_EXECUTABLE="$<TARGET_FILE:mysqld>"
+ -DMYSQLD_EXECUTABLE="$<TARGET_FILE:mariadbd>"
-DCMAKE_CFG_INTDIR="${CMAKE_CFG_INTDIR}"
-P ${CMAKE_SOURCE_DIR}/cmake/create_initial_db.cmake
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/
- DEPENDS mysqld
+ DEPENDS mariadbd
)
+ IF(WIN32)
+ SET(ALL_ON_WINDOWS ALL)
+ ELSE()
+ SET(ALL_ON_WINDOWS)
+ ENDIF()
ADD_CUSTOM_TARGET(initial_database
- ALL
- DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
+ ${ALL_ON_WINDOWS}
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
)
-ELSE()
- # Not windows or cross compiling, just install an empty directory
- INSTALL(FILES ${DUMMY_FILE} DESTINATION data/mysql COMPONENT DataFiles)
-ENDIF(WIN32 AND TARGET mysqld AND NOT CMAKE_CROSSCOMPILING)
-ENDIF(INSTALL_LAYOUT STREQUAL "STANDALONE")
+ENDIF()
IF(WIN32)
SET(my_bootstrap_sql ${CMAKE_CURRENT_BINARY_DIR}/my_bootstrap.sql)
@@ -466,22 +448,22 @@ IF(WIN32)
DEPENDS comp_sql ${my_bootstrap_sql}
)
- MYSQL_ADD_EXECUTABLE(mysql_install_db
+ MYSQL_ADD_EXECUTABLE(mariadb-install-db
mysql_install_db.cc
${CMAKE_CURRENT_BINARY_DIR}/mysql_bootstrap_sql.c
COMPONENT Server
)
- SET_TARGET_PROPERTIES(mysql_install_db PROPERTIES COMPILE_FLAGS -DINSTALL_PLUGINDIR=${INSTALL_PLUGINDIR})
- TARGET_LINK_LIBRARIES(mysql_install_db mysys shlwapi)
+ SET_TARGET_PROPERTIES(mariadb-install-db PROPERTIES COMPILE_FLAGS -DINSTALL_PLUGINDIR=${INSTALL_PLUGINDIR})
+ TARGET_LINK_LIBRARIES(mariadb-install-db mysys shlwapi)
ADD_LIBRARY(winservice STATIC winservice.c)
TARGET_LINK_LIBRARIES(winservice shell32)
- MYSQL_ADD_EXECUTABLE(mysql_upgrade_service
+ MYSQL_ADD_EXECUTABLE(mariadb-upgrade-service
mysql_upgrade_service.cc
upgrade_conf_file.cc
COMPONENT Server)
- TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys winservice)
+ TARGET_LINK_LIBRARIES(mariadb-upgrade-service mysys winservice)
ENDIF(WIN32)
INSTALL(DIRECTORY . DESTINATION ${INSTALL_INCLUDEDIR}/server/private COMPONENT Development
diff --git a/sql/authors.h b/sql/authors.h
index 251ed2c38c3..cf0a4c5e51a 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -77,6 +77,9 @@ struct show_table_authors_st show_table_authors[]= {
{ "Federico Razolli", "Italy", "MariaDB documentation Italian translation"},
{ "Vinchen", "Shenzhen, China", "Instant ADD Column for InnoDB, Spider engine optimization, from Tencent Game DBA Team" },
{ "Willhan", "Shenzhen, China", "Big Column Compression, Spider engine optimization, from Tencent Game DBA Team" },
+ { "Anders Karlsson", "Ystad, Sweden", "Replication patch for enforcing triggers on slave"},
+ { "Otto Kekäläinen", "Tampere, Finland", "Debian packaging, install/upgrade engineering, QA pipelines, documentation"},
+ { "Daniel Black", "Canberra, Australia", "Modernising large page support, systemd, and bug fixes"},
/* People working on MySQL code base (not NDB) */
{ "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
diff --git a/sql/backup.cc b/sql/backup.cc
index cff14415d96..e89f9a108a7 100644
--- a/sql/backup.cc
+++ b/sql/backup.cc
@@ -160,7 +160,8 @@ static bool backup_start(THD *thd)
DBUG_RETURN(1);
}
- mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_START, MDL_EXPLICIT);
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_START,
+ MDL_EXPLICIT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
DBUG_RETURN(1);
@@ -203,7 +204,7 @@ static bool backup_flush(THD *thd)
Free unused tables and table shares so that mariabackup knows what
is safe to copy
*/
- tc_purge(false);
+ tc_purge();
tdc_purge(true);
DBUG_RETURN(0);
diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h
index fd733caa019..07ab6dbaab9 100644
--- a/sql/bounded_queue.h
+++ b/sql/bounded_queue.h
@@ -57,9 +57,10 @@ public:
@param to Where to put the key.
@param from The input data.
*/
- typedef void (*keymaker_function)(Sort_param *param,
+ typedef uint (*keymaker_function)(Sort_param *param,
Key_type *to,
- Element_type *from);
+ Element_type *from,
+ bool packing_keys);
/**
Function for comparing two keys.
@@ -181,11 +182,12 @@ void Bounded_queue<Element_type, Key_type>::push(Element_type *element)
{
// 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);
+ (void)(*m_keymaker)(m_sort_param, *pq_top, element, false);
queue_replace_top(&m_queue);
} else {
// Insert new key into the queue.
- (*m_keymaker)(m_sort_param, m_sort_keys[m_queue.elements], element);
+ (*m_keymaker)(m_sort_param, m_sort_keys[m_queue.elements],
+ element, false);
queue_insert(&m_queue,
reinterpret_cast<uchar*>(&m_sort_keys[m_queue.elements]));
}
diff --git a/sql/create_options.cc b/sql/create_options.cc
index a8d997efaf4..04469120db1 100644
--- a/sql/create_options.cc
+++ b/sql/create_options.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2010, 2019, MariaDB Corporation.
+/* Copyright (C) 2010, 2020, MariaDB Corporation.
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
@@ -44,9 +44,8 @@ void engine_option_value::link(engine_option_value **start,
/* check duplicates to avoid writing them to frm*/
for(opt= *start;
opt && ((opt->parsed && !opt->value.str) ||
- my_strnncoll(system_charset_info,
- (uchar *)name.str, name.length,
- (uchar*)opt->name.str, opt->name.length));
+ system_charset_info->strnncoll(name.str, name.length,
+ opt->name.str, opt->name.length));
opt= opt->next) /* no-op */;
if (opt)
{
@@ -187,9 +186,8 @@ static bool set_one_value(ha_create_table_option *opt,
for (end=start;
*end && *end != ',';
end++) /* no-op */;
- if (!my_strnncoll(system_charset_info,
- (uchar*)start, end-start,
- (uchar*)value->str, value->length))
+ if (!system_charset_info->strnncoll(start, end-start,
+ value->str, value->length))
{
*val= num;
DBUG_RETURN(0);
@@ -211,29 +209,17 @@ static bool set_one_value(ha_create_table_option *opt,
if (!value->str)
DBUG_RETURN(0);
- if (!my_strnncoll(system_charset_info,
- (const uchar*)"NO", 2,
- (uchar *)value->str, value->length) ||
- !my_strnncoll(system_charset_info,
- (const uchar*)"OFF", 3,
- (uchar *)value->str, value->length) ||
- !my_strnncoll(system_charset_info,
- (const uchar*)"0", 1,
- (uchar *)value->str, value->length))
+ if (!system_charset_info->strnncoll("NO", 2, value->str, value->length) ||
+ !system_charset_info->strnncoll("OFF", 3, value->str, value->length) ||
+ !system_charset_info->strnncoll("0", 1, value->str, value->length))
{
*val= FALSE;
DBUG_RETURN(FALSE);
}
- if (!my_strnncoll(system_charset_info,
- (const uchar*)"YES", 3,
- (uchar *)value->str, value->length) ||
- !my_strnncoll(system_charset_info,
- (const uchar*)"ON", 2,
- (uchar *)value->str, value->length) ||
- !my_strnncoll(system_charset_info,
- (const uchar*)"1", 1,
- (uchar *)value->str, value->length))
+ if (!system_charset_info->strnncoll("YES", 3, value->str, value->length) ||
+ !system_charset_info->strnncoll("ON", 2, value->str, value->length) ||
+ !system_charset_info->strnncoll("1", 1, value->str, value->length))
{
*val= TRUE;
DBUG_RETURN(FALSE);
@@ -295,9 +281,8 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
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))
+ if (system_charset_info->strnncoll(opt->name, opt->name_length,
+ val->name.str, val->name.length))
continue;
/* skip duplicates (see engine_option_value constructor above) */
@@ -422,7 +407,7 @@ static bool resolve_sysvars(handlerton *hton, ha_create_table_option *rules)
return 1;
}
DBUG_ASSERT(str.length());
- opt->values= my_strndup(str.ptr(), str.length()-1, MYF(MY_WME));
+ opt->values= my_strndup(PSI_INSTRUMENT_ME, str.ptr(), str.length()-1, MYF(MY_WME));
if (!opt->values)
return 1;
break;
@@ -809,9 +794,8 @@ bool is_engine_option_known(engine_option_value *opt,
for (; rules->name; rules++)
{
- if (!my_strnncoll(system_charset_info,
- (uchar*)rules->name, rules->name_length,
- (uchar*)opt->name.str, opt->name.length))
+ if (!system_charset_info->strnncoll(rules->name, rules->name_length,
+ opt->name.str, opt->name.length))
return true;
}
return false;
diff --git a/sql/datadict.cc b/sql/datadict.cc
index da8376d8b1a..e09eee98565 100644
--- a/sql/datadict.cc
+++ b/sql/datadict.cc
@@ -25,7 +25,7 @@ static int read_string(File file, uchar**to, size_t length)
DBUG_ENTER("read_string");
/* This can't use MY_THREAD_SPECIFIC as it's used on server start */
- if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
+ if (!(*to= (uchar*) my_malloc(PSI_INSTRUMENT_ME, length+1,MYF(MY_WME))) ||
mysql_file_read(file, *to, length, MYF(MY_NABP)))
{
my_free(*to);
@@ -49,16 +49,13 @@ static int read_string(File file, uchar**to, size_t length)
If engine_name is 0, then the function will only test if the file is a
view or not
- @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise
-
@retval TABLE_TYPE_UNKNOWN error - file can't be opened
@retval TABLE_TYPE_NORMAL table
@retval TABLE_TYPE_SEQUENCE sequence table
@retval TABLE_TYPE_VIEW view
*/
-Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
- bool *is_sequence)
+Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name)
{
File file;
uchar header[40]; //"TYPE=VIEW\n" it is 10 characters
@@ -67,10 +64,8 @@ Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
uchar dbt;
DBUG_ENTER("dd_frm_type");
- *is_sequence= 0;
-
- if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0)))
- < 0)
+ file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0));
+ if (file < 0)
DBUG_RETURN(TABLE_TYPE_UNKNOWN);
/*
@@ -110,7 +105,7 @@ Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
if (((header[39] >> 4) & 3) == HA_CHOICE_YES)
{
DBUG_PRINT("info", ("Sequence found"));
- *is_sequence= 1;
+ type= TABLE_TYPE_SEQUENCE;
}
/* cannot use ha_resolve_by_legacy_type without a THD */
diff --git a/sql/datadict.h b/sql/datadict.h
index cbdf788deb6..f4af592247a 100644
--- a/sql/datadict.h
+++ b/sql/datadict.h
@@ -38,13 +38,11 @@ enum Table_type
To check whether it's an frm of a view, use dd_frm_is_view().
*/
-enum Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
- bool *is_sequence);
+enum Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name);
static inline bool dd_frm_is_view(THD *thd, char *path)
{
- bool not_used2;
- return dd_frm_type(thd, path, NULL, &not_used2) == TABLE_TYPE_VIEW;
+ return dd_frm_type(thd, path, NULL) == TABLE_TYPE_VIEW;
}
bool dd_recreate_table(THD *thd, const char *db, const char *table_name);
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc
index c037af40e33..dde3ce5a35b 100644
--- a/sql/debug_sync.cc
+++ b/sql/debug_sync.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2009, 2013, Oracle and/or its affiliates.
- Copyright (c) 2013, 2020, MariaDB
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -122,6 +122,25 @@ static void init_debug_sync_psi_keys(void)
/**
+ Set the THD::proc_info without instrumentation.
+ This method is private to DEBUG_SYNC,
+ and on purpose avoid any use of:
+ - the SHOW PROFILE instrumentation
+ - the PERFORMANCE_SCHEMA instrumentation
+ so that using DEBUG_SYNC() in the server code
+ does not cause the instrumentations to record
+ spurious data.
+*/
+static const char*
+debug_sync_thd_proc_info(THD *thd, const char* info)
+{
+ const char* old_proc_info= thd->proc_info;
+ thd->proc_info= info;
+ return old_proc_info;
+}
+
+
+/**
Initialize the debug sync facility at server start.
@return status
@@ -236,7 +255,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),
+ my_malloc(PSI_NOT_INSTRUMENTED,
+ sizeof(st_debug_sync_control),
MYF(MY_WME | MY_ZEROFILL | MY_THREAD_SPECIFIC));
if (!thd->debug_sync_control)
{
@@ -267,12 +287,6 @@ void debug_sync_end_thread(THD *thd)
{
st_debug_sync_control *ds_control= thd->debug_sync_control;
- /*
- This synchronization point can be used to synchronize on thread end.
- This is the latest point in a THD's life, where this can be done.
- */
- DEBUG_SYNC(thd, "thread_end");
-
if (ds_control->ds_action)
{
st_debug_sync_action *action= ds_control->ds_action;
@@ -302,6 +316,20 @@ void debug_sync_end_thread(THD *thd)
}
+void debug_sync_reset_thread(THD *thd)
+{
+ if (thd->debug_sync_control)
+ {
+ /*
+ This synchronization point can be used to synchronize on thread end.
+ This is the latest point in a THD's life, where this can be done.
+ */
+ DEBUG_SYNC(thd, "thread_end");
+ thd->debug_sync_control->ds_active= 0;
+ }
+}
+
+
/**
Move a string by length.
@@ -658,7 +686,7 @@ static st_debug_sync_action *debug_sync_get_action(THD *thd,
if (ds_control->ds_active > ds_control->ds_allocated)
{
uint new_alloc= ds_control->ds_active + 3;
- void *new_action= my_realloc(ds_control->ds_action,
+ void *new_action= my_realloc(PSI_NOT_INSTRUMENTED, ds_control->ds_action,
new_alloc * sizeof(st_debug_sync_action),
MYF(MY_WME | MY_ALLOW_ZERO_PTR));
if (!new_action)
@@ -857,8 +885,7 @@ static char *debug_sync_token(char **token_p, uint *token_length_p,
DBUG_ASSERT(ptr);
/* Skip leading space */
- ptr+= system_charset_info->cset->scan(system_charset_info,
- ptr, ptrend, MY_SEQ_SPACES);
+ ptr+= system_charset_info->scan(ptr, ptrend, MY_SEQ_SPACES);
if (!*ptr)
{
ptr= NULL;
@@ -869,8 +896,7 @@ static char *debug_sync_token(char **token_p, uint *token_length_p,
*token_p= ptr;
/* Find token end. */
- ptr+= system_charset_info->cset->scan(system_charset_info,
- ptr, ptrend, MY_SEQ_NONSPACES);
+ ptr+= system_charset_info->scan(ptr, ptrend, MY_SEQ_NONSPACES);
/* Get token length. */
*token_length_p= (uint)(ptr - *token_p);
@@ -880,7 +906,7 @@ static char *debug_sync_token(char **token_p, uint *token_length_p,
{
DBUG_ASSERT(ptr < ptrend);
/* Get terminator character length. */
- uint mbspacelen= my_charlen_fix(system_charset_info, ptr, ptrend);
+ uint mbspacelen= system_charset_info->charlen_fix(ptr, ptrend);
/* Terminate token. */
*ptr= '\0';
@@ -889,8 +915,7 @@ static char *debug_sync_token(char **token_p, uint *token_length_p,
ptr+= mbspacelen;
/* Skip trailing space */
- ptr+= system_charset_info->cset->scan(system_charset_info,
- ptr, ptrend, MY_SEQ_SPACES);
+ ptr+= system_charset_info->scan(ptr, ptrend, MY_SEQ_SPACES);
}
end:
@@ -1368,7 +1393,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
strxnmov(ds_control->ds_proc_info, sizeof(ds_control->ds_proc_info)-1,
"debug sync point: ", action->sync_point.c_ptr(), NullS);
old_proc_info= thd->proc_info;
- thd_proc_info(thd, ds_control->ds_proc_info);
+ debug_sync_thd_proc_info(thd, ds_control->ds_proc_info);
}
/*
@@ -1453,12 +1478,10 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
if (unlikely(error == ETIMEDOUT || error == ETIME))
{
// 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;
+ Abort_on_warning_instant_set aws(thd, false);
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_DEBUG_SYNC_TIMEOUT,
ER_THD(thd, ER_DEBUG_SYNC_TIMEOUT));
- thd->abort_on_warning= save_abort_on_warning;
DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ASSERT(0););
break;
}
@@ -1488,11 +1511,11 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
mysql_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= old_mutex;
thd->mysys_var->current_cond= old_cond;
- thd_proc_info(thd, old_proc_info);
+ debug_sync_thd_proc_info(thd, old_proc_info);
mysql_mutex_unlock(&thd->mysys_var->mutex);
}
else
- thd_proc_info(thd, old_proc_info);
+ debug_sync_thd_proc_info(thd, old_proc_info);
}
else
{
diff --git a/sql/debug_sync.h b/sql/debug_sync.h
index 7a63a52959c..3b8aa8815e1 100644
--- a/sql/debug_sync.h
+++ b/sql/debug_sync.h
@@ -41,10 +41,16 @@ extern int debug_sync_init(void);
extern void debug_sync_end(void);
extern void debug_sync_init_thread(THD *thd);
extern void debug_sync_end_thread(THD *thd);
+void debug_sync_reset_thread(THD *thd);
extern bool debug_sync_set_action(THD *thd, const char *action_str, size_t len);
extern bool debug_sync_update(THD *thd, char *val_str, size_t len);
extern uchar *debug_sync_value_ptr(THD *thd);
-
+#else
+static inline void debug_sync_init_thread(THD *thd) {}
+static inline void debug_sync_end_thread(THD *thd) {}
+static inline void debug_sync_reset_thread(THD *thd) {}
+static inline bool debug_sync_set_action(THD *, const char *, size_t)
+{ return false; }
#endif /* defined(ENABLED_DEBUG_SYNC) */
#endif /* DEBUG_SYNC_INCLUDED */
diff --git a/sql/derror.cc b/sql/derror.cc
index 8b44d1bff9b..187d5bc20d2 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -120,8 +120,9 @@ bool init_errmessage(void)
all_errors+= errors_per_range[i];
if (!(original_error_messages= (const char***)
- my_malloc((all_errors + MAX_ERROR_RANGES)* sizeof(void*),
- MYF(MY_ZEROFILL))))
+ my_malloc(PSI_NOT_INSTRUMENTED,
+ (all_errors + MAX_ERROR_RANGES)*sizeof(void*),
+ MYF(MY_ZEROFILL))))
DBUG_RETURN(TRUE);
errmsgs= (const char**)(original_error_messages + MAX_ERROR_RANGES);
@@ -315,7 +316,8 @@ bool read_texts(const char *file_name, const char *language,
DBUG_RETURN(1);
if (!(*data= (const char***)
- my_malloc((size_t) ((MAX_ERROR_RANGES+1) * sizeof(char**) +
+ my_malloc(key_memory_errmsgs,
+ (size_t) ((MAX_ERROR_RANGES+1) * sizeof(char**) +
MY_MAX(msg_file.text_length, msg_file.errors * 2)+
msg_file.errors * sizeof(char*)),
MYF(MY_WME))))
diff --git a/sql/discover.cc b/sql/discover.cc
index 3df777c19ba..4267f97cf59 100644
--- a/sql/discover.cc
+++ b/sql/discover.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -75,7 +76,8 @@ int readfrm(const char *name, const uchar **frmdata, size_t *len)
// Read whole frm file
error= 3;
- if (!(read_data= (uchar*)my_malloc(read_len, MYF(MY_WME))))
+ if (!(read_data= (uchar*)my_malloc(key_memory_frm_string, read_len,
+ MYF(MY_WME))))
goto err;
if (mysql_file_read(file, read_data, read_len, MYF(MY_NABP)))
{
@@ -97,34 +99,32 @@ int readfrm(const char *name, const uchar **frmdata, size_t *len)
/*
- Write the content of a frm data pointer
- to a frm file.
+ Write the content of a frm data pointer to a frm or par file.
- @param path path to table-file "db/name"
- @param frmdata frm data
- @param len length of the frmdata
+ @param path full path to table-file "db/name.frm" or .par
+ @param db Database name. Only used for my_error()
+ @param table Table name. Only used for my_error()
+ @param data data to write to file
+ @param len length of the data
@retval
0 ok
@retval
- 2 Could not write file
+ <> 0 Could not write file. In this case the file is not created
*/
-int writefrm(const char *path, const char *db, const char *table,
- bool tmp_table, const uchar *frmdata, size_t len)
+int writefile(const char *path, const char *db, const char *table,
+ bool tmp_table, const uchar *data, size_t len)
{
- char file_name[FN_REFLEN+1];
int error;
int create_flags= O_RDWR | O_TRUNC;
- DBUG_ENTER("writefrm");
+ DBUG_ENTER("writefile");
DBUG_PRINT("enter",("name: '%s' len: %lu ",path, (ulong) len));
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,
+ File file= mysql_file_create(key_file_frm, path,
CREATE_MODE, create_flags, MYF(0));
if (unlikely((error= file < 0)))
@@ -136,16 +136,19 @@ int writefrm(const char *path, const char *db, const char *table,
}
else
{
- error= (int)mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP));
+ error= (int)mysql_file_write(file, data, 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));
+ my_sync_dir_by_file(path, MYF(MY_WME));
error|= mysql_file_close(file, MYF(MY_WME));
+ if (error)
+ my_delete(path, MYF(0));
}
DBUG_RETURN(error);
-} /* writefrm */
+} /* writefile */
+
static inline void advance(FILEINFO* &from, FILEINFO* &to,
FILEINFO* cur, bool &skip)
@@ -153,7 +156,7 @@ static inline void advance(FILEINFO* &from, FILEINFO* &to,
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)
+ 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...)
@@ -206,12 +209,12 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta,
size_t len= (octothorp ? octothorp : ext) - cur->name;
if (from != cur &&
(strlen(from->name) <= len ||
- my_strnncoll(cs, (uchar*)from->name, len, (uchar*)cur->name, len) ||
+ cs->strnncoll(from->name, len, 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)
+ if (cs->strnncoll(ext, strlen(ext),
+ ext_meta, ext_meta_len) == 0)
{
*ext = 0;
if (result->add_file(cur->name))
@@ -255,8 +258,8 @@ int ext_table_discovery_simple(MY_DIR *dirp,
if (ext)
{
- if (my_strnncoll(cs, (uchar*)ext, strlen(ext),
- (uchar*)reg_ext, reg_ext_length) == 0)
+ if (cs->strnncoll(ext, strlen(ext),
+ reg_ext, reg_ext_length) == 0)
{
*ext = 0;
if (result->add_file(cur->name))
diff --git a/sql/discover.h b/sql/discover.h
index f14be662dbc..1775f5d6551 100644
--- a/sql/discover.h
+++ b/sql/discover.h
@@ -21,8 +21,8 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext,
#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);
+int writefile(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)
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 70faeee6039..91d1d871307 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -25,13 +25,13 @@
// date_add_interval,
// calc_time_diff
#include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
-#include "sql_acl.h" // EVENT_ACL, SUPER_ACL
#include "sp.h" // load_charset, load_collation
#include "events.h"
#include "event_data_objects.h"
#include "event_db_repository.h"
#include "sp_head.h"
#include "sql_show.h" // append_definer, append_identifier
+#include "mysql/psi/mysql_sp.h"
#ifdef WITH_WSREP
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
@@ -40,6 +40,18 @@
@{
*/
+#ifdef HAVE_PSI_INTERFACE
+void init_scheduler_psi_keys()
+{
+ const char *category= "scheduler";
+
+ PSI_server->register_statement(category, & Event_queue_element_for_exec::psi_info, 1);
+}
+
+PSI_statement_info Event_queue_element_for_exec::psi_info=
+{ 0, "event", 0};
+#endif
+
/*************************************************************************/
/**
@@ -173,11 +185,13 @@ Event_creation_ctx::load_from_db(THD *thd,
*/
bool
-Event_queue_element_for_exec::init(const LEX_CSTRING *db, const LEX_CSTRING *n)
+Event_queue_element_for_exec::init(const LEX_CSTRING &db, const LEX_CSTRING &n)
{
- if (!(dbname.str= my_strndup(db->str, dbname.length= db->length, MYF(MY_WME))))
+ if (!(dbname.str= my_strndup(key_memory_Event_queue_element_for_exec_names,
+ db.str, dbname.length= db.length, MYF(MY_WME))))
return TRUE;
- if (!(name.str= my_strndup(n->str, name.length= n->length, MYF(MY_WME))))
+ if (!(name.str= my_strndup(key_memory_Event_queue_element_for_exec_names,
+ n.str, name.length= n.length, MYF(MY_WME))))
{
my_free(const_cast<char*>(dbname.str));
return TRUE;
@@ -211,7 +225,7 @@ Event_basic::Event_basic()
{
DBUG_ENTER("Event_basic::Event_basic");
/* init memory root */
- init_sql_alloc(&mem_root, "Event_basic", 256, 512, MYF(0));
+ init_sql_alloc(key_memory_event_basic_root, &mem_root, 256, 512, MYF(0));
dbname.str= name.str= NULL;
dbname.length= name.length= 0;
time_zone= NULL;
@@ -1425,15 +1439,23 @@ Event_job_data::execute(THD *thd, bool drop)
{
Parser_state parser_state;
+ sql_digest_state *parent_digest= thd->m_digest;
+ PSI_statement_locker *parent_locker= thd->m_statement_psi;
+ bool res;
+
if (parser_state.init(thd, thd->query(), thd->query_length()))
goto end;
- if (parse_sql(thd, & parser_state, creation_ctx))
+ thd->m_digest= NULL;
+ thd->m_statement_psi= NULL;
+ res= parse_sql(thd, & parser_state, creation_ctx);
+ thd->m_digest= parent_digest;
+ thd->m_statement_psi= parent_locker;
+
+ if (res)
{
- sql_print_error("Event Scheduler: "
- "%serror during compilation of %s.%s",
- thd->is_fatal_error ? "fatal " : "",
- (const char *) dbname.str, (const char *) name.str);
+ sql_print_error("Event Scheduler: %serror during compilation of %s.%s",
+ thd->is_fatal_error ? "fatal " : "", dbname.str, name.str);
goto end;
}
}
@@ -1456,6 +1478,9 @@ Event_job_data::execute(THD *thd, bool drop)
sphead->set_creation_ctx(creation_ctx);
sphead->optimize();
+ sphead->m_sp_share= MYSQL_GET_SP_SHARE(SP_TYPE_EVENT,
+ dbname.str, static_cast<uint>(dbname.length),
+ name.str, static_cast<uint>(name.length));
ret= sphead->execute_procedure(thd, &empty_item_list);
/*
There is no pre-locking and therefore there should be no
@@ -1480,8 +1505,6 @@ end:
ret= 1;
else
{
- ulong saved_master_access;
-
thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
/*
@@ -1493,8 +1516,8 @@ end:
Temporarily reset it to read-write.
*/
- saved_master_access= thd->security_ctx->master_access;
- thd->security_ctx->master_access |= SUPER_ACL;
+ privilege_t saved_master_access(thd->security_ctx->master_access);
+ thd->security_ctx->master_access |= PRIV_IGNORE_READ_ONLY;
bool save_tx_read_only= thd->tx_read_only;
thd->tx_read_only= false;
diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h
index e5e3e4eb087..c20a8c31425 100644
--- a/sql/event_data_objects.h
+++ b/sql/event_data_objects.h
@@ -30,6 +30,8 @@ class THD;
class Time_zone;
struct TABLE;
+void init_scheduler_psi_keys(void);
+
class Event_queue_element_for_exec
{
public:
@@ -37,7 +39,7 @@ public:
~Event_queue_element_for_exec();
bool
- init(const LEX_CSTRING *dbname, const LEX_CSTRING *name);
+ init(const LEX_CSTRING &dbname, const LEX_CSTRING &name);
LEX_CSTRING dbname;
LEX_CSTRING name;
@@ -48,6 +50,15 @@ private:
/* Prevent use of these */
Event_queue_element_for_exec(const Event_queue_element_for_exec &);
void operator=(Event_queue_element_for_exec &);
+#ifdef HAVE_PSI_INTERFACE
+public:
+ PSI_statement_info* get_psi_info()
+ {
+ return & psi_info;
+ }
+
+ static PSI_statement_info psi_info;
+#endif
};
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 399a19b4112..9d0764cab14 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -24,13 +24,13 @@
#include "sql_db.h" // get_default_db_collation
#include "sql_time.h" // interval_type_to_name
#include "tztime.h" // struct Time_zone
-#include "sql_acl.h" // SUPER_ACL, MYSQL_DB_FIELD_COUNT, mysql_db_table_fields
#include "records.h" // init_read_record, end_read_record
#include "sp_head.h"
#include "event_data_objects.h"
#include "events.h"
#include "sql_show.h"
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
+#include "transaction.h"
/**
@addtogroup Event_Scheduler
@@ -534,23 +534,26 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
const char *db)
{
TABLE *schema_table= i_s_table->table;
- Open_tables_backup open_tables_backup;
TABLE_LIST event_table;
int ret= 0;
-
DBUG_ENTER("Event_db_repository::fill_schema_events");
DBUG_PRINT("info",("db=%s", db? db:"(null)"));
+ start_new_trans new_trans(thd);
+
event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
- if (open_system_tables_for_read(thd, &event_table, &open_tables_backup))
+ if (open_system_tables_for_read(thd, &event_table))
+ {
+ new_trans.restore_old_transaction();
DBUG_RETURN(TRUE);
+ }
if (table_intact.check(event_table.table, &event_table_def))
{
- close_system_tables(thd, &open_tables_backup);
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
- DBUG_RETURN(TRUE);
+ ret= 1;
+ goto err;
}
/*
@@ -567,7 +570,9 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table,
else
ret= table_scan_all_for_i_s(thd, schema_table, event_table.table);
- close_system_tables(thd, &open_tables_backup);
+err:
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
DBUG_PRINT("info", ("Return code=%d", ret));
DBUG_RETURN(ret);
@@ -613,7 +618,8 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type,
if (table_intact.check(*table, &event_table_def))
{
- close_thread_tables(thd);
+ thd->commit_whole_transaction_and_close_tables();
+ *table= 0; // Table is now closed
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
DBUG_RETURN(TRUE);
}
@@ -743,7 +749,8 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
ret= 0;
end:
- close_thread_tables(thd);
+ if (table)
+ thd->commit_whole_transaction_and_close_tables();
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->variables.sql_mode= saved_mode;
@@ -785,7 +792,6 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
*/
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
int ret= 1;
-
DBUG_ENTER("Event_db_repository::update_event");
/* None or both must be set */
@@ -858,7 +864,8 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
ret= 0;
end:
- close_thread_tables(thd);
+ if (table)
+ thd->commit_whole_transaction_and_close_tables();
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->variables.sql_mode= saved_mode;
@@ -920,7 +927,8 @@ Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db,
ret= 0;
end:
- close_thread_tables(thd);
+ if (table)
+ thd->commit_whole_transaction_and_close_tables();
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
DBUG_RETURN(MY_TEST(ret));
@@ -1000,12 +1008,16 @@ Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
TABLE *table= NULL;
READ_RECORD read_record_info;
enum enum_events_table_field field= ET_FIELD_DB;
- MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("Event_db_repository::drop_schema_events");
DBUG_PRINT("enter", ("field: %d schema: %s", field, schema->str));
+ start_new_trans new_trans(thd);
+
if (open_event_table(thd, TL_WRITE, &table))
+ {
+ new_trans.restore_old_transaction();
DBUG_VOID_RETURN;
+ }
/* only enabled events are in memory, so we go now and delete the rest */
if (init_read_record(&read_record_info, thd, table, NULL, NULL, 1, 0, FALSE))
@@ -1034,13 +1046,8 @@ Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema)
end_read_record(&read_record_info);
end:
- close_thread_tables(thd);
- /*
- Make sure to only release the MDL lock on mysql.event, not other
- metadata locks DROP DATABASE might have acquired.
- */
- thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
-
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
DBUG_VOID_RETURN;
}
@@ -1061,18 +1068,16 @@ Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
Event_basic *etn)
{
bool ret;
- ulonglong saved_mode= thd->variables.sql_mode;
- Open_tables_backup open_tables_backup;
TABLE_LIST event_table;
-
DBUG_ENTER("Event_db_repository::load_named_event");
DBUG_PRINT("enter",("thd: %p name: %*s", thd,
(int) name->length, name->str));
- event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
-
+ start_new_trans new_trans(thd);
/* Reset sql_mode during data dictionary operations. */
- thd->variables.sql_mode= 0;
+ Sql_mode_instant_set sms(thd, 0);
+
+ event_table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_EVENT_NAME, 0, TL_READ);
/*
We don't use open_event_table() here to make sure that SHOW
@@ -1080,11 +1085,12 @@ Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
does not release transactional metadata locks when the
event table is closed.
*/
- if (!(ret= open_system_tables_for_read(thd, &event_table, &open_tables_backup)))
+ if (!(ret= open_system_tables_for_read(thd, &event_table)))
{
if (table_intact.check(event_table.table, &event_table_def))
{
- close_system_tables(thd, &open_tables_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0));
DBUG_RETURN(TRUE);
}
@@ -1093,11 +1099,10 @@ Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname,
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_V2, MYF(0), "mysql", "event");
-
- close_system_tables(thd, &open_tables_backup);
+ thd->commit_whole_transaction_and_close_tables();
}
+ new_trans.restore_old_transaction();
- thd->variables.sql_mode= saved_mode;
DBUG_RETURN(ret);
}
@@ -1120,22 +1125,20 @@ update_timing_fields_for_event(THD *thd,
TABLE *table= NULL;
Field **fields;
int ret= 1;
- 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.
- */
- save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
-
- DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
+ DBUG_ASSERT(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY);
if (open_event_table(thd, TL_WRITE, &table))
- goto end;
+ DBUG_RETURN(1);
fields= table->field;
+ /*
+ Turn off row binlogging of event timing updates. These are not used
+ for RBR of events replicated to the slave.
+ */
+ table->file->row_logging= 0;
if (find_named_event(event_db_name, event_name, table))
goto end;
@@ -1156,12 +1159,9 @@ update_timing_fields_for_event(THD *thd,
}
ret= 0;
-
end:
- if (table)
- close_mysql_tables(thd);
-
- thd->restore_stmt_binlog_format(save_binlog_format);
+ if (thd->commit_whole_transaction_and_close_tables())
+ ret= 1;
DBUG_RETURN(MY_TEST(ret));
}
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 91c243b3f70..86356e4325d 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -24,6 +24,7 @@
#include "tztime.h" // my_tz_find, my_tz_OFFSET0, struct Time_zone
#include "log.h" // sql_print_error
#include "sql_class.h" // struct THD
+#include "mysql/psi/mysql_sp.h"
/**
@addtogroup Event_Scheduler
@@ -351,6 +352,9 @@ Event_queue::drop_matching_events(THD *thd, const LEX_CSTRING *pattern,
is ok.
*/
queue_remove(&queue, i);
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(SP_TYPE_EVENT, et->dbname.str, static_cast<uint>(et->dbname.length),
+ et->name.str, static_cast<uint>(et->name.length));
delete et;
}
else
@@ -637,7 +641,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
}
if (!(*event_name= new Event_queue_element_for_exec()) ||
- (*event_name)->init(&top->dbname, &top->name))
+ (*event_name)->init(top->dbname, top->name))
{
ret= TRUE;
break;
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index a4d010b19a8..78802a5e109 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -23,7 +23,7 @@
#include "event_queue.h"
#include "event_db_repository.h"
#include "sql_connect.h" // init_new_connection_handler_thread
-#include "sql_acl.h" // SUPER_ACL
+#include "sql_class.h"
/**
@addtogroup Event_Scheduler
@@ -129,11 +129,12 @@ bool
post_init_event_thread(THD *thd)
{
(void) init_new_connection_handler_thread();
- if (init_thr_lock() || thd->store_globals())
+ if (init_thr_lock())
{
thd->cleanup();
return TRUE;
}
+ thd->store_globals();
return FALSE;
}
@@ -178,8 +179,8 @@ pre_init_event_thread(THD* thd)
set_current_thd(thd);
thd->client_capabilities= 0;
- thd->security_ctx->master_access= 0;
- thd->security_ctx->db_access= 0;
+ thd->security_ctx->master_access= NO_ACL;
+ thd->security_ctx->db_access= NO_ACL;
thd->security_ctx->host_or_ip= (char*)my_localhost;
my_net_init(&thd->net, NULL, thd, MYF(MY_THREAD_SPECIFIC));
thd->security_ctx->set_user((char*)"event_scheduler");
@@ -292,6 +293,15 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
DBUG_ASSERT(thd->m_digest == NULL);
DBUG_ASSERT(thd->m_statement_psi == NULL);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_statement_locker_state state;
+ thd->m_statement_psi= MYSQL_START_STATEMENT(& state,
+ event->get_psi_info()->m_key,
+ event->dbname.str,
+ event->dbname.length,
+ thd->charset(), NULL);
+#endif
+
thd->thread_stack= &my_stack; // remember where our stack is
res= post_init_event_thread(thd);
@@ -320,7 +330,10 @@ 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);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+#endif
DBUG_ASSERT(thd->m_digest == NULL);
DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
event->name.str));
@@ -406,13 +419,14 @@ Event_scheduler::start(int *err_no)
Same goes for transaction access mode. Set it to read-write for this thd.
*/
- new_thd->security_ctx->master_access |= SUPER_ACL;
+ new_thd->security_ctx->master_access |= PRIV_IGNORE_READ_ONLY;
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));
+ (struct scheduler_param *)my_malloc(key_memory_Event_scheduler_scheduler_param,
+ sizeof(struct scheduler_param), MYF(0));
scheduler_param_value->thd= new_thd;
scheduler_param_value->scheduler= this;
@@ -497,6 +511,8 @@ Event_scheduler::run(THD *thd)
}
DBUG_PRINT("info", ("state=%s", scheduler_states_names[state].str));
free_root(thd->mem_root, MYF(0));
+ /* Ensure we don't have any open tables or table locks */
+ DBUG_ASSERT(thd->lock == 0);
}
LOCK_DATA();
diff --git a/sql/events.cc b/sql/events.cc
index 195c0fa09e2..33ddcdac3cb 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2005, 2013, Oracle and/or its affiliates.
- Copyright (c) 2017, MariaDB Corporation.
+ Copyright (c) 2017, 2020, MariaDB Corporation.
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 @@
#include "sp_head.h" // for Stored_program_creation_ctx
#include "set_var.h"
#include "lock.h" // lock_object_name
+#include "mysql/psi/mysql_sp.h"
/**
@addtogroup Event_Scheduler
@@ -103,8 +104,8 @@ ulong Events::inited;
int sortcmp_lex_string(const LEX_CSTRING *s, const LEX_CSTRING *t,
const CHARSET_INFO *cs)
{
- return cs->coll->strnncollsp(cs, (uchar *) s->str, s->length,
- (uchar *) t->str, t->length);
+ return cs->strnncollsp(s->str, s->length,
+ t->str, t->length);
}
@@ -620,6 +621,9 @@ Events::drop_event(THD *thd, const LEX_CSTRING *dbname,
/* Binlog the drop event. */
DBUG_ASSERT(thd->query() && thd->query_length());
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(SP_TYPE_EVENT,
+ dbname->str, static_cast<uint>(dbname->length), name->str, static_cast<uint>(name->length));
}
thd->restore_stmt_binlog_format(save_binlog_format);
@@ -832,13 +836,12 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
*/
if (thd->lex->sql_command == SQLCOM_SHOW_EVENTS)
{
- DBUG_ASSERT(thd->lex->first_select_lex()->db.str);
- if (!is_infoschema_db(&thd->lex->first_select_lex()->db) && // There is no events in I_S
- check_access(thd, EVENT_ACL, thd->lex->first_select_lex()->db.str,
- NULL, NULL, 0, 0))
+ LEX_CSTRING *lexdb= &thd->lex->first_select_lex()->db;
+ DBUG_ASSERT(lexdb);
+ if (!is_infoschema_db(lexdb) && !is_perfschema_db(lexdb) &&
+ check_access(thd, EVENT_ACL, lexdb->str, NULL, NULL, 0, 0))
DBUG_RETURN(1);
- db= normalize_db_name(thd->lex->first_select_lex()->db.str,
- db_tmp, sizeof(db_tmp));
+ db= normalize_db_name(lexdb->str, db_tmp, sizeof(db_tmp));
}
ret= db_repository->fill_schema_events(thd, tables, db);
@@ -1028,6 +1031,8 @@ 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};
+PSI_memory_key key_memory_event_basic_root;
+
#ifdef HAVE_PSI_INTERFACE
PSI_stage_info *all_events_stages[]=
{
@@ -1036,6 +1041,11 @@ PSI_stage_info *all_events_stages[]=
& stage_waiting_for_scheduler_to_stop
};
+static PSI_memory_info all_events_memory[]=
+{
+ { &key_memory_event_basic_root, "Event_basic::mem_root", PSI_FLAG_GLOBAL}
+};
+
static void init_events_psi_keys(void)
{
const char* category= "sql";
@@ -1053,6 +1063,10 @@ static void init_events_psi_keys(void)
count= array_elements(all_events_stages);
mysql_stage_register(category, all_events_stages, count);
+ count= array_elements(all_events_memory);
+ mysql_memory_register(category, all_events_memory, count);
+
+ init_scheduler_psi_keys();
}
#endif /* HAVE_PSI_INTERFACE */
@@ -1145,7 +1159,6 @@ Events::load_events_from_db(THD *thd)
READ_RECORD read_record_info;
bool ret= TRUE;
uint count= 0;
- ulong saved_master_access;
DBUG_ENTER("Events::load_events_from_db");
DBUG_PRINT("enter", ("thd: %p", thd));
@@ -1158,8 +1171,8 @@ Events::load_events_from_db(THD *thd)
Temporarily reset it to read-write.
*/
- saved_master_access= thd->security_ctx->master_access;
- thd->security_ctx->master_access |= SUPER_ACL;
+ privilege_t saved_master_access(thd->security_ctx->master_access);
+ thd->security_ctx->master_access |= PRIV_IGNORE_READ_ONLY;
bool save_tx_read_only= thd->tx_read_only;
thd->tx_read_only= false;
@@ -1228,15 +1241,9 @@ Events::load_events_from_db(THD *thd)
TRUE);
/* All the dmls to mysql.events tables are stmt bin-logged. */
- bool save_binlog_row_based;
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->set_current_stmt_binlog_format_stmt();
-
+ table->file->row_logging= 0;
(void) table->file->ha_update_row(table->record[1], table->record[0]);
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
-
delete et;
continue;
}
diff --git a/sql/events.h b/sql/events.h
index 9e1651c767a..2fb13d7c807 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -31,6 +31,8 @@ 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 */
+extern PSI_memory_key key_memory_event_basic_root;
+
/* Always defined, for SHOW PROCESSLIST. */
extern PSI_stage_info stage_waiting_on_empty_queue;
extern PSI_stage_info stage_waiting_for_next_activation;
diff --git a/sql/field.cc b/sql/field.cc
index 4a82eae6a0e..f8d9cbf93ce 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -58,7 +58,7 @@ const char field_separator=',';
((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1))
// Column marked for read or the field set to read out of record[0]
-inline bool Field::marked_for_read() const
+bool Field::marked_for_read() const
{
return !table ||
(!table->read_set ||
@@ -73,7 +73,7 @@ inline bool Field::marked_for_read() const
changed fields with DBUG_FIX_WRITE_SET() in table.cc
*/
-inline bool Field::marked_for_write_or_computed() const
+bool Field::marked_for_write_or_computed() const
{
return (!table ||
(!table->write_set ||
@@ -984,7 +984,7 @@ static bool
test_if_important_data(CHARSET_INFO *cs, const char *str, const char *strend)
{
if (cs != &my_charset_bin)
- str+= cs->cset->scan(cs, str, strend, MY_SEQ_SPACES);
+ str+= cs->scan(str, strend, MY_SEQ_SPACES);
return (str < strend);
}
@@ -1013,8 +1013,15 @@ CPP_UNNAMED_NS_END
Static help functions
*****************************************************************************/
+/*
+ @brief
+ Create a fixed size sort key part
+
+ @param buff buffer where values are written
+ @param length fixed size of the sort column
+*/
-void Field::make_sort_key(uchar *buff,uint length)
+void Field::make_sort_key_part(uchar *buff,uint length)
{
if (maybe_null())
{
@@ -1029,6 +1036,61 @@ void Field::make_sort_key(uchar *buff,uint length)
}
+/*
+ @brief
+ Create a packed sort key part
+
+ @param buff buffer where values are written
+ @param sort_field sort column structure
+
+ @retval
+ length of the bytes written, does not include the NULL bytes
+*/
+uint
+Field::make_packed_sort_key_part(uchar *buff,
+ const SORT_FIELD_ATTR *sort_field)
+{
+ if (maybe_null())
+ {
+ if (is_null())
+ {
+ *buff++= 0;
+ return 0; // For NULL values don't write any data
+ }
+ *buff++=1;
+ }
+ sort_string(buff, sort_field->original_length);
+ return sort_field->original_length;
+}
+
+
+uint
+Field_longstr::make_packed_sort_key_part(uchar *buff,
+ const SORT_FIELD_ATTR *sort_field)
+{
+ if (maybe_null())
+ {
+ if (is_null())
+ {
+ *buff++= 0;
+ return 0; // For NULL values don't write any data
+ }
+ *buff++=1;
+ }
+ uchar *end= pack_sort_string(buff, sort_field);
+ return (uint) (end-buff);
+}
+
+
+uchar*
+Field_longstr::pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field)
+{
+ StringBuffer<LONGLONG_BUFFER_SIZE> buf;
+ val_str(&buf, &buf);
+ return to + sort_field->pack_sort_string(to, &buf, field_charset());
+}
+
+
/**
@brief
Determine the relative position of the field value in a numeric interval
@@ -1120,15 +1182,15 @@ double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
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());
+ charset()->strnxfrm(mp_prefix, sizeof(mp),
+ ptr + data_offset,
+ data_length());
+ charset()->strnxfrm(minp_prefix, sizeof(minp),
+ min->ptr + data_offset,
+ min->data_length());
+ charset()->strnxfrm(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);
@@ -1582,7 +1644,7 @@ Value_source::Converter_string_to_number::check_edom_and_truncation(THD *thd,
@note
This is called after one has called one of the following functions:
- strntoull10rnd()
- - my_strntod()
+ - strntod()
- str2my_decimal()
@retval
@@ -1662,9 +1724,9 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, size_t len,
char *end;
int error;
- *rnd= (longlong) cs->cset->strntoull10rnd(cs, from, len,
- unsigned_flag, &end,
- &error);
+ *rnd= (longlong) cs->strntoull10rnd(from, len,
+ unsigned_flag, &end,
+ &error);
if (unsigned_flag)
{
@@ -1703,7 +1765,7 @@ double Field_real::get_double(const char *str, size_t length, CHARSET_INFO *cs,
int *error)
{
char *end;
- double nr= my_strntod(cs,(char*) str, length, &end, error);
+ double nr= cs->strntod((char*) str, length, &end, error);
if (unlikely(*error))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
@@ -1760,10 +1822,10 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val)
if (val_buffer->alloc(MY_INT64_NUM_DECIMAL_DIGITS))
return 0;
- length= (uint) (*cs->cset->longlong10_to_str)(cs, (char*) val_buffer->ptr(),
- MY_INT64_NUM_DECIMAL_DIGITS,
- unsigned_val ? 10 : -10,
- value);
+ length= (uint) (cs->longlong10_to_str)((char*) val_buffer->ptr(),
+ MY_INT64_NUM_DECIMAL_DIGITS,
+ unsigned_val ? 10 : -10,
+ value);
val_buffer->length(length);
return val_buffer;
}
@@ -1801,8 +1863,7 @@ void Field::hash(ulong *nr, ulong *nr2)
else
{
uint len= pack_length();
- CHARSET_INFO *cs= sort_charset();
- cs->coll->hash_sort(cs, ptr, len, nr, nr2);
+ sort_charset()->hash_sort(ptr, len, nr, nr2);
}
}
@@ -1827,7 +1888,7 @@ void Field::copy_from_tmp(int row_offset)
}
-bool Field::send_binary(Protocol *protocol)
+bool Field::send(Protocol *protocol)
{
char buff[MAX_FIELD_WIDTH];
String tmp(buff,sizeof(buff),charset());
@@ -1836,6 +1897,18 @@ bool Field::send_binary(Protocol *protocol)
}
+bool Field_num::send_numeric_zerofill_str(Protocol_text *protocol,
+ protocol_send_type_t send_type)
+{
+ DBUG_ASSERT(marked_for_read());
+ StringBuffer<MAX_FIELD_WIDTH> tmp(&my_charset_latin1);
+ val_str(&tmp);
+ return protocol->store_numeric_zerofill_str(tmp.ptr(),
+ tmp.length(),
+ send_type);
+}
+
+
/**
Check to see if field size is compatible with destination.
@@ -1871,9 +1944,9 @@ bool Field::send_binary(Protocol *protocol)
master's field size, @c false otherwise.
*/
bool Field::compatible_field_size(uint field_metadata,
- Relay_log_info *rli_arg __attribute__((unused)),
+ const Relay_log_info *rli_arg __attribute__((unused)),
uint16 mflags __attribute__((unused)),
- int *order_var)
+ int *order_var) const
{
uint const source_size= pack_length_from_metadata(field_metadata);
uint const destination_size= row_pack_length();
@@ -1887,13 +1960,16 @@ bool Field::compatible_field_size(uint field_metadata,
int Field::store(const char *to, size_t length, CHARSET_INFO *cs,
enum_check_fields check_level)
{
- int res;
- 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);
- thd->count_cuted_fields= old_check_level;
- return res;
+ Check_level_instant_set tmp_level(get_thd(), check_level);
+ return store(to, length, cs);
+}
+
+
+int Field::store_text(const char *to, size_t length, CHARSET_INFO *cs,
+ enum_check_fields check_level)
+{
+ Check_level_instant_set tmp_level(get_thd(), check_level);
+ return store_text(to, length, cs);
}
@@ -1902,6 +1978,23 @@ int Field::store_timestamp_dec(const timeval &ts, uint dec)
return store_time_dec(Datetime(get_thd(), ts).get_mysql_time(), dec);
}
+
+int Field::store_to_statistical_minmax_field(Field *field, String *val)
+{
+ val_str(val);
+ size_t length= Well_formed_prefix(val->charset(), val->ptr(),
+ MY_MIN(val->length(), field->field_length)).length();
+ return field->store(val->ptr(), length, &my_charset_bin);
+}
+
+
+int Field::store_from_statistical_minmax_field(Field *stat_field, String *str)
+{
+ stat_field->val_str(str);
+ return store_text(str->ptr(), str->length(), &my_charset_bin);
+}
+
+
/**
Pack the field into a format suitable for storage and transfer.
@@ -2012,24 +2105,24 @@ void Field::make_send_field(Send_field *field)
{
if (orig_table && orig_table->s->db.str && *orig_table->s->db.str)
{
- field->db_name= orig_table->s->db.str;
+ field->db_name= orig_table->s->db;
if (orig_table->pos_in_table_list &&
orig_table->pos_in_table_list->schema_table)
- field->org_table_name= (orig_table->pos_in_table_list->
- schema_table->table_name);
+ field->org_table_name= Lex_cstring_strlen(orig_table->pos_in_table_list->
+ schema_table->table_name);
else
- field->org_table_name= orig_table->s->table_name.str;
+ field->org_table_name= orig_table->s->table_name;
}
else
- field->org_table_name= field->db_name= "";
+ field->org_table_name= field->db_name= empty_clex_str;
if (orig_table && orig_table->alias.ptr())
{
- field->table_name= orig_table->alias.ptr();
+ orig_table->alias.get_value(&field->table_name);
field->org_col_name= field_name;
}
else
{
- field->table_name= "";
+ field->table_name= empty_clex_str;
field->org_col_name= empty_clex_str;
}
field->col_name= field_name;
@@ -2188,11 +2281,9 @@ Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{
- field_charset= collation.collation;
+ m_collation= collation;
if (collation.collation->state & MY_CS_BINSORT)
flags|=BINARY_FLAG;
- field_derivation= collation.derivation;
- field_repertoire= collation.repertoire;
}
@@ -2206,7 +2297,7 @@ bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
SELECT * FROM t1 WHERE varchar_column=DATE'2001-01-01'
return non-unuque values, e.g. '2001-01-01' and '2001-01-01x'.
*/
- if (!field_charset->coll->propagate(field_charset, 0, 0) ||
+ if (!field_charset()->propagate(0, 0) ||
item->cmp_type() != STRING_RESULT)
return false;
/*
@@ -2217,8 +2308,8 @@ bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const
WHERE latin1_bin_column = _latin1'A' COLLATE latin1_swedish_ci
return non-unique values 'a' and 'A'.
*/
- DTCollation tmp(field_charset, field_derivation, repertoire());
- return !tmp.aggregate(item->collation) && tmp.collation == field_charset;
+ DTCollation tmp(dtcollation());
+ return !tmp.aggregate(item->collation) && tmp.collation == field_charset();
}
@@ -2515,7 +2606,7 @@ bool Field_null::is_equal(const Column_definition &new_field) const
{
DBUG_ASSERT(!compression_method());
return new_field.type_handler() == type_handler() &&
- new_field.charset == field_charset &&
+ new_field.charset == field_charset() &&
new_field.length == max_display_length();
}
@@ -3061,8 +3152,7 @@ double Field_decimal::val_real(void)
DBUG_ASSERT(marked_for_read());
int not_used;
char *end_not_used;
- return my_strntod(&my_charset_bin, (char*) ptr, field_length, &end_not_used,
- &not_used);
+ return my_charset_bin.strntod((char*) ptr, field_length, &end_not_used, &not_used);
}
longlong Field_decimal::val_int(void)
@@ -3070,10 +3160,8 @@ longlong Field_decimal::val_int(void)
DBUG_ASSERT(marked_for_read());
int not_used;
if (unsigned_flag)
- return my_strntoull(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
- &not_used);
- return my_strntoll(&my_charset_bin, (char*) ptr, field_length, 10, NULL,
- &not_used);
+ return my_charset_bin.strntoull((char*) ptr, field_length, 10, NULL, &not_used);
+ return my_charset_bin.strntoll((char*) ptr, field_length, 10, NULL, &not_used);
}
@@ -3099,7 +3187,7 @@ String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
5.00 , -1.0, 05, -05, +5 with optional pre/end space
*/
-int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr)
+int Field_decimal::cmp(const uchar *a_ptr,const uchar *b_ptr) const
{
const uchar *end;
int swap=0;
@@ -3478,7 +3566,7 @@ my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value)
}
-int Field_new_decimal::cmp(const uchar *a,const uchar*b)
+int Field_new_decimal::cmp(const uchar *a,const uchar*b) const
{
return memcmp(a, b, bin_size);
}
@@ -3510,11 +3598,12 @@ void Field_new_decimal::sql_type(String &str) const
@returns number of bytes written to metadata_ptr
*/
-int Field_new_decimal::save_field_metadata(uchar *metadata_ptr)
+
+Binlog_type_info Field_new_decimal::binlog_type_info() const
{
- *metadata_ptr= precision;
- *(metadata_ptr + 1)= decimals();
- return 2;
+ DBUG_ASSERT(Field_new_decimal::type() == binlog_type());
+ return Binlog_type_info(Field_new_decimal::type(), precision +
+ (decimals() << 8), 2, binlog_signedness());
}
@@ -3530,7 +3619,7 @@ int Field_new_decimal::save_field_metadata(uchar *metadata_ptr)
@returns The size of the field based on the field metadata.
*/
-uint Field_new_decimal::pack_length_from_metadata(uint field_metadata)
+uint Field_new_decimal::pack_length_from_metadata(uint field_metadata) const
{
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
uint const source_decimal= field_metadata & 0x00ff;
@@ -3541,9 +3630,9 @@ uint Field_new_decimal::pack_length_from_metadata(uint field_metadata)
bool Field_new_decimal::compatible_field_size(uint field_metadata,
- Relay_log_info * __attribute__((unused)),
+ const Relay_log_info * __attribute__((unused)),
uint16 mflags __attribute__((unused)),
- int *order_var)
+ int *order_var) const
{
uint const source_precision= (field_metadata >> 8U) & 0x00ff;
uint const source_decimal= field_metadata & 0x00ff;
@@ -3664,6 +3753,17 @@ int Field_int::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
}
+void Field_int::sql_type(String &res) const
+{
+ CHARSET_INFO *cs=res.charset();
+ Name name= type_handler()->type_handler_signed()->name();
+ res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
+ "%.*s(%d)", (int) name.length(), name.ptr(),
+ (int) field_length));
+ add_zerofill_and_unsigned(res);
+}
+
+
/****************************************************************************
** tiny int
****************************************************************************/
@@ -3794,12 +3894,17 @@ String *Field_tiny::val_str(String *val_buffer,
return val_str_from_long(val_buffer, 5, -10, nr);
}
-bool Field_tiny::send_binary(Protocol *protocol)
+bool Field_tiny::send(Protocol *protocol)
{
- return protocol->store_tiny((longlong) (int8) ptr[0]);
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_TINY);
+ return protocol->store_tiny(Field_tiny::val_int());
}
-int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr)
+
+int Field_tiny::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
signed char a,b;
a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
@@ -3816,14 +3921,6 @@ void Field_tiny::sort_string(uchar *to,uint length __attribute__((unused)))
to[0] = (char) (ptr[0] ^ (uchar) 128); /* Revers signbit */
}
-void Field_tiny::sql_type(String &res) const
-{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "tinyint(%d)",(int) field_length));
- add_zerofill_and_unsigned(res);
-}
-
/****************************************************************************
Field type short int (2 byte)
****************************************************************************/
@@ -3962,13 +4059,17 @@ String *Field_short::val_str(String *val_buffer,
}
-bool Field_short::send_binary(Protocol *protocol)
+bool Field_short::send(Protocol *protocol)
{
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
return protocol->store_short(Field_short::val_int());
}
-int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_short::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
short a,b;
a=sint2korr(a_ptr);
@@ -3989,15 +4090,6 @@ void Field_short::sort_string(uchar *to,uint length __attribute__((unused)))
to[1] = ptr[0];
}
-void Field_short::sql_type(String &res) const
-{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "smallint(%d)",(int) field_length));
- add_zerofill_and_unsigned(res);
-}
-
-
/****************************************************************************
Field type medium int (3 byte)
****************************************************************************/
@@ -4145,7 +4237,7 @@ String *Field_int::val_str_from_long(String *val_buffer,
uint mlength= MY_MAX(field_length + 1, max_char_length * cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
- length= (uint) cs->cset->long10_to_str(cs, to, mlength, radix, nr);
+ length= (uint) cs->long10_to_str(to, mlength, radix, nr);
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer); /* purecov: inspected */
@@ -4154,14 +4246,17 @@ String *Field_int::val_str_from_long(String *val_buffer,
}
-bool Field_medium::send_binary(Protocol *protocol)
+bool Field_medium::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
return protocol->store_long(Field_medium::val_int());
}
-int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_medium::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
long a,b;
if (unsigned_flag)
@@ -4188,14 +4283,6 @@ void Field_medium::sort_string(uchar *to,uint length __attribute__((unused)))
}
-void Field_medium::sql_type(String &res) const
-{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "mediumint(%d)",(int) field_length));
- add_zerofill_and_unsigned(res);
-}
-
/****************************************************************************
** long int
****************************************************************************/
@@ -4333,13 +4420,17 @@ String *Field_long::val_str(String *val_buffer,
}
-bool Field_long::send_binary(Protocol *protocol)
+bool Field_long::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONG);
return protocol->store_long(Field_long::val_int());
}
-int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr)
+
+int Field_long::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
int32 a,b;
a=sint4korr(a_ptr);
@@ -4361,14 +4452,6 @@ void Field_long::sort_string(uchar *to,uint length __attribute__((unused)))
}
-void Field_long::sql_type(String &res) const
-{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "int(%d)",(int) field_length));
- add_zerofill_and_unsigned(res);
-}
-
/****************************************************************************
Field type longlong int (8 bytes)
****************************************************************************/
@@ -4380,7 +4463,7 @@ int Field_longlong::store(const char *from,size_t len,CHARSET_INFO *cs)
char *end;
ulonglong tmp;
- tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error);
+ tmp= cs->strntoull10rnd(from, len, unsigned_flag, &end, &error);
if (unlikely(error == MY_ERRNO_ERANGE))
{
set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
@@ -4468,8 +4551,8 @@ String *Field_longlong::val_str(String *val_buffer,
longlong j;
j=sint8korr(ptr);
- length=(uint) (cs->cset->longlong10_to_str)(cs,to,mlength,
- unsigned_flag ? 10 : -10, j);
+ length=(uint) (cs->longlong10_to_str)(to, mlength,
+ unsigned_flag ? 10 : -10, j);
val_buffer->length(length);
if (zerofill)
prepend_zeros(val_buffer);
@@ -4478,14 +4561,17 @@ String *Field_longlong::val_str(String *val_buffer,
}
-bool Field_longlong::send_binary(Protocol *protocol)
+bool Field_longlong::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_LONGLONG);
return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag);
}
-int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_longlong::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
longlong a,b;
a=sint8korr(a_ptr);
@@ -4512,14 +4598,6 @@ void Field_longlong::sort_string(uchar *to,uint length __attribute__((unused)))
}
-void Field_longlong::sql_type(String &res) const
-{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "bigint(%d)",(int) field_length));
- add_zerofill_and_unsigned(res);
-}
-
void Field_longlong::set_max()
{
DBUG_ASSERT(marked_for_write_or_computed());
@@ -4620,7 +4698,7 @@ String *Field_float::val_str(String *val_buffer,
}
-int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_float::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
float a,b;
float4get(a,a_ptr);
@@ -4666,10 +4744,13 @@ void Field_float::sort_string(uchar *to,uint length __attribute__((unused)))
}
-bool Field_float::send_binary(Protocol *protocol)
+bool Field_float::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
- return protocol->store((float) Field_float::val_real(), dec, (String*) 0);
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_FLOAT);
+ return protocol->store_float((float) Field_float::val_real(), dec);
}
@@ -4682,26 +4763,11 @@ bool Field_float::send_binary(Protocol *protocol)
@returns number of bytes written to metadata_ptr
*/
-int Field_float::save_field_metadata(uchar *metadata_ptr)
-{
- *metadata_ptr= pack_length();
- return 1;
-}
-
-
-void Field_float::sql_type(String &res) const
+Binlog_type_info Field_float::binlog_type_info() const
{
- if (dec >= FLOATING_POINT_DECIMALS)
- {
- res.set_ascii(STRING_WITH_LEN("float"));
- }
- else
- {
- CHARSET_INFO *cs= res.charset();
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "float(%d,%d)",(int) field_length,dec));
- }
- add_zerofill_and_unsigned(res);
+ DBUG_ASSERT(Field_float::type() == binlog_type());
+ return Binlog_type_info(Field_float::type(), pack_length(), 1,
+ binlog_signedness());
}
@@ -4927,6 +4993,24 @@ Item *Field_real::get_equal_const_item(THD *thd, const Context &ctx,
}
+void Field_real::sql_type(String &res) const
+{
+ const Name name= type_handler()->name();
+ if (dec >= FLOATING_POINT_DECIMALS)
+ {
+ res.set_ascii(name.ptr(), name.length());
+ }
+ else
+ {
+ CHARSET_INFO *cs= res.charset();
+ res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
+ "%.*s(%d,%d)", (int) name.length(), name.ptr(),
+ (int) field_length,dec));
+ }
+ add_zerofill_and_unsigned(res);
+}
+
+
String *Field_double::val_str(String *val_buffer,
String *val_ptr __attribute__((unused)))
{
@@ -4957,13 +5041,17 @@ String *Field_double::val_str(String *val_buffer,
return val_buffer;
}
-bool Field_double::send_binary(Protocol *protocol)
+bool Field_double::send(Protocol *protocol)
{
- return protocol->store((double) Field_double::val_real(), dec, (String*) 0);
+ DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if (unlikely(zerofill) && (txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_DOUBLE);
+ return protocol->store_double(Field_double::val_real(), dec);
}
-int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
double a,b;
float8get(a,a_ptr);
@@ -4994,26 +5082,11 @@ void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
@returns number of bytes written to metadata_ptr
*/
-int Field_double::save_field_metadata(uchar *metadata_ptr)
-{
- *metadata_ptr= pack_length();
- return 1;
-}
-
-
-void Field_double::sql_type(String &res) const
+Binlog_type_info Field_double::binlog_type_info() const
{
- CHARSET_INFO *cs=res.charset();
- if (dec >= FLOATING_POINT_DECIMALS)
- {
- res.set_ascii(STRING_WITH_LEN("double"));
- }
- else
- {
- res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
- "double(%d,%d)",(int) field_length,dec));
- }
- add_zerofill_and_unsigned(res);
+ DBUG_ASSERT(Field_double::type() == binlog_type());
+ return Binlog_type_info(Field_double::type(), pack_length(), 1,
+ binlog_signedness());
}
@@ -5093,8 +5166,8 @@ int Field_timestamp::save_in_field(Field *to)
return to->store_timestamp_dec(Timeval(ts, sec_part), decimals());
}
-my_time_t Field_timestamp::get_timestamp(const uchar *pos,
- ulong *sec_part) const
+my_time_t Field_timestamp0::get_timestamp(const uchar *pos,
+ ulong *sec_part) const
{
DBUG_ASSERT(marked_for_read());
*sec_part= 0;
@@ -5102,7 +5175,7 @@ my_time_t Field_timestamp::get_timestamp(const uchar *pos,
}
-bool Field_timestamp::val_native(Native *to)
+bool Field_timestamp0::val_native(Native *to)
{
DBUG_ASSERT(marked_for_read());
my_time_t sec= (my_time_t) sint4korr(ptr);
@@ -5267,12 +5340,6 @@ int Field_timestamp::store_native(const Native &value)
}
-double Field_timestamp::val_real(void)
-{
- return (double) Field_timestamp::val_int();
-}
-
-
longlong Field_timestamp::val_int(void)
{
MYSQL_TIME ltime;
@@ -5376,15 +5443,15 @@ bool Field_timestamp::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
}
-bool Field_timestamp::send_binary(Protocol *protocol)
+bool Field_timestamp0::send(Protocol *protocol)
{
MYSQL_TIME ltime;
- Field_timestamp::get_date(&ltime, date_mode_t(0));
+ Field_timestamp0::get_date(&ltime, date_mode_t(0));
return protocol->store(&ltime, 0);
}
-int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_timestamp0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
int32 a,b;
a=sint4korr(a_ptr);
@@ -5393,7 +5460,7 @@ int Field_timestamp::cmp(const uchar *a_ptr, const uchar *b_ptr)
}
-void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused)))
+void Field_timestamp0::sort_string(uchar *to,uint length __attribute__((unused)))
{
to[0] = ptr[3];
to[1] = ptr[2];
@@ -5402,20 +5469,7 @@ void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused)))
}
-void Field_timestamp::sql_type(String &res) const
-{
- 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()
+int Field_timestamp0::set_time()
{
set_notnull();
store_TIMESTAMP(Timestamp(get_thd()->query_start(), 0));
@@ -5485,29 +5539,6 @@ static longlong read_native(const uchar *from, uint bytes)
}
#endif
-static void store_lowendian(ulonglong num, uchar *to, uint bytes)
-{
- switch(bytes) {
- case 1: *to= (uchar)num; break;
- case 2: int2store(to, num); break;
- case 3: int3store(to, num); break;
- case 4: int4store(to, num); break;
- case 8: int8store(to, num); break;
- default: DBUG_ASSERT(0);
- }
-}
-
-static longlong read_lowendian(const uchar *from, uint bytes)
-{
- switch(bytes) {
- case 1: return from[0];
- case 2: return uint2korr(from);
- case 3: return uint3korr(from);
- case 4: return uint4korr(from);
- case 8: return sint8korr(from);
- default: DBUG_ASSERT(0); return 0;
- }
-}
void Field_timestamp_hires::store_TIMEVAL(const timeval &tv)
{
@@ -5572,7 +5603,7 @@ int Field_timestamp_with_dec::set_time()
return 0;
}
-bool Field_timestamp_with_dec::send_binary(Protocol *protocol)
+bool Field_timestamp_with_dec::send(Protocol *protocol)
{
MYSQL_TIME ltime;
Field_timestamp::get_date(&ltime, date_mode_t(0));
@@ -5580,7 +5611,7 @@ bool Field_timestamp_with_dec::send_binary(Protocol *protocol)
}
-int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
int32 a,b;
ulong a_sec_part, b_sec_part;
@@ -5653,6 +5684,11 @@ bool Field_timestampf::val_native(Native *to)
return Field::val_native(to);
}
+Binlog_type_info Field_timestampf::binlog_type_info() const
+{
+ return Binlog_type_info(Field_timestampf::binlog_type(), decimals(), 1);
+}
+
/*************************************************************/
sql_mode_t Field_temporal::can_handle_sql_mode_dependency_on_store() const
@@ -5689,6 +5725,44 @@ void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
}
+void Field_temporal::sql_type_dec_comment(String &res,
+ const Name &name,
+ uint dec,
+ const Name &comment) const
+{
+ CHARSET_INFO *cs=res.charset();
+ res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ "%.*s(%u)%s%.*s%s",
+ (uint) name.length(), name.ptr(),
+ dec,
+ comment.length() ? " /* " : "",
+ (uint) comment.length(), comment.ptr(),
+ comment.length() ? " */" : ""));
+}
+
+
+void Field_temporal::sql_type_comment(String &res,
+ const Name &name,
+ const Name &comment) const
+{
+ CHARSET_INFO *cs=res.charset();
+ res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ "%.*s%s%.*s%s",
+ (uint) name.length(), name.ptr(),
+ comment.length() ? " /* " : "",
+ (uint) comment.length(), comment.ptr(),
+ comment.length() ? " */" : ""));
+}
+
+
+const Name & Field_temporal::type_version_mysql56()
+{
+ DBUG_EXECUTE_IF("sql_type", return Type_handler::version_mysql56(); );
+ static Name none(NULL, 0);
+ return none;
+}
+
+
/*
Store string into a date/time field
@@ -5870,7 +5944,7 @@ int Field_time::store_TIME_with_warning(const Time *t,
}
-void Field_time::store_TIME(const MYSQL_TIME *ltime)
+void Field_time0::store_TIME(const MYSQL_TIME *ltime)
{
DBUG_ASSERT(ltime->year == 0);
DBUG_ASSERT(ltime->month == 0);
@@ -5958,14 +6032,14 @@ Field *Field_time::new_key_field(MEM_ROOT *root, TABLE *new_table,
}
-double Field_time::val_real(void)
+double Field_time0::val_real(void)
{
DBUG_ASSERT(marked_for_read());
uint32 j= (uint32) uint3korr(ptr);
return (double) j;
}
-longlong Field_time::val_int(void)
+longlong Field_time0::val_int(void)
{
DBUG_ASSERT(marked_for_read());
return (longlong) sint3korr(ptr);
@@ -6014,7 +6088,7 @@ bool Field_time::check_zero_in_date_with_warn(date_mode_t fuzzydate)
DATE_FORMAT(time, "%l.%i %p")
*/
-bool Field_time::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+bool Field_time0::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
if (check_zero_in_date_with_warn(fuzzydate))
return true;
@@ -6054,7 +6128,7 @@ bool Field_time::val_native(Native *to)
}
-bool Field_time::send_binary(Protocol *protocol)
+bool Field_time::send(Protocol *protocol)
{
MYSQL_TIME ltime;
get_date(&ltime, Time::Options(TIME_TIME_ONLY, get_thd()));
@@ -6062,7 +6136,7 @@ bool Field_time::send_binary(Protocol *protocol)
}
-int Field_time::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_time0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
int32 a,b;
a=(int32) sint3korr(a_ptr);
@@ -6070,24 +6144,13 @@ int Field_time::cmp(const uchar *a_ptr, const uchar *b_ptr)
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
-void Field_time::sort_string(uchar *to,uint length __attribute__((unused)))
+void Field_time0::sort_string(uchar *to,uint length __attribute__((unused)))
{
to[0] = (uchar) (ptr[2] ^ 128);
to[1] = ptr[1];
to[2] = ptr[0];
}
-void Field_time::sql_type(String &res) const
-{
- if (decimals() == 0)
- {
- res.set_ascii(STRING_WITH_LEN("time"));
- return;
- }
- 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()
{
@@ -6249,7 +6312,7 @@ bool Field_time_hires::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
}
-int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
ulonglong a=read_bigendian(a_ptr, Field_time_hires::pack_length());
ulonglong b=read_bigendian(b_ptr, Field_time_hires::pack_length());
@@ -6295,6 +6358,10 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
TIME_from_longlong_time_packed(ltime, tmp);
return false;
}
+Binlog_type_info Field_timef::binlog_type_info() const
+{
+ return Binlog_type_info(Field_timef::binlog_type(), decimals(), 1);
+}
longlong Field_timef::val_time_packed(THD *thd)
@@ -6334,7 +6401,7 @@ int Field_year::store(const char *from, size_t len,CHARSET_INFO *cs)
DBUG_ASSERT(marked_for_write_or_computed());
char *end;
int error;
- longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error);
+ longlong nr= cs->strntoull10rnd(from, len, 0, &end, &error);
if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155 ||
error == MY_ERRNO_ERANGE)
@@ -6409,9 +6476,12 @@ int Field_year::store_time_dec(const MYSQL_TIME *ltime, uint dec_arg)
return 0;
}
-bool Field_year::send_binary(Protocol *protocol)
+bool Field_year::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
+ Protocol_text *txt;
+ if ((txt= dynamic_cast<Protocol_text*>(protocol)))
+ return send_numeric_zerofill_str(txt, PROTOCOL_SEND_SHORT);
ulonglong tmp= Field_year::val_int();
return protocol->store_short(tmp);
}
@@ -6546,7 +6616,7 @@ void Field_date::store_TIME(const MYSQL_TIME *ltime)
int4store(ptr,tmp);
}
-bool Field_date::send_binary(Protocol *protocol)
+bool Field_date::send(Protocol *protocol)
{
longlong tmp= Field_date::val_int();
MYSQL_TIME tm;
@@ -6604,7 +6674,7 @@ String *Field_date::val_str(String *val_buffer,
}
-int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_date::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
int32 a,b;
a=sint4korr(a_ptr);
@@ -6640,7 +6710,7 @@ void Field_newdate::store_TIME(const MYSQL_TIME *ltime)
}
-bool Field_newdate::send_binary(Protocol *protocol)
+bool Field_newdate::send(Protocol *protocol)
{
MYSQL_TIME tm;
Field_newdate::get_date(&tm, date_mode_t(0));
@@ -6716,7 +6786,7 @@ longlong Field_newdate::val_datetime_packed(THD *thd)
}
-int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
uint32 a,b;
a=(uint32) uint3korr(a_ptr);
@@ -6805,7 +6875,7 @@ Item *Field_newdate::get_equal_const_item(THD *thd, const Context &ctx,
** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int.
****************************************************************************/
-void Field_datetime::store_TIME(const MYSQL_TIME *ltime)
+void Field_datetime0::store_TIME(const MYSQL_TIME *ltime)
{
ulonglong tmp= TIME_to_ulonglong_datetime(ltime);
int8store(ptr,tmp);
@@ -6820,20 +6890,15 @@ Field_datetime::conversion_depends_on_sql_mode(THD *thd, Item *expr) const
}
-bool Field_datetime::send_binary(Protocol *protocol)
+bool Field_datetime0::send(Protocol *protocol)
{
MYSQL_TIME tm;
- Field_datetime::get_date(&tm, date_mode_t(0));
+ Field_datetime0::get_date(&tm, date_mode_t(0));
return protocol->store(&tm, 0);
}
-double Field_datetime::val_real(void)
-{
- return (double) Field_datetime::val_int();
-}
-
-longlong Field_datetime::val_int(void)
+longlong Field_datetime0::val_int(void)
{
DBUG_ASSERT(marked_for_read());
longlong j;
@@ -6842,8 +6907,8 @@ longlong Field_datetime::val_int(void)
}
-String *Field_datetime::val_str(String *val_buffer,
- String *val_ptr __attribute__((unused)))
+String *Field_datetime0::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
{
val_buffer->alloc(field_length);
val_buffer->length(field_length);
@@ -6854,7 +6919,7 @@ String *Field_datetime::val_str(String *val_buffer,
char *pos;
int part3;
- tmp= Field_datetime::val_int();
+ tmp= Field_datetime0::val_int();
/*
Avoid problem with slow longlong arithmetic and sprintf
@@ -6888,8 +6953,8 @@ String *Field_datetime::val_str(String *val_buffer,
return val_buffer;
}
-bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
- date_mode_t fuzzydate) const
+bool Field_datetime0::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
+ date_mode_t fuzzydate) const
{
DBUG_ASSERT(marked_for_read());
longlong tmp= sint8korr(pos);
@@ -6910,7 +6975,7 @@ bool Field_datetime::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
}
-int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_datetime0::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
longlong a,b;
a=sint8korr(a_ptr);
@@ -6919,7 +6984,7 @@ int Field_datetime::cmp(const uchar *a_ptr, const uchar *b_ptr)
((ulonglong) a > (ulonglong) b) ? 1 : 0;
}
-void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused)))
+void Field_datetime0::sort_string(uchar *to,uint length __attribute__((unused)))
{
to[0] = ptr[7];
to[1] = ptr[6];
@@ -6932,19 +6997,6 @@ void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused)))
}
-void Field_datetime::sql_type(String &res) const
-{
- 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;
@@ -6966,7 +7018,7 @@ void Field_datetime_hires::store_TIME(const MYSQL_TIME *ltime)
store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
}
-bool Field_datetime_with_dec::send_binary(Protocol *protocol)
+bool Field_datetime_with_dec::send(Protocol *protocol)
{
MYSQL_TIME ltime;
get_date(&ltime, date_mode_t(0));
@@ -7012,7 +7064,7 @@ bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
}
-int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length());
ulonglong b=read_bigendian(b_ptr, Field_datetime_hires::pack_length());
@@ -7050,6 +7102,10 @@ bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
TIME_from_longlong_datetime_packed(ltime, tmp);
return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
}
+Binlog_type_info Field_datetimef::binlog_type_info() const
+{
+ return Binlog_type_info(Field_datetimef::binlog_type(), decimals(), 1);
+}
longlong Field_datetimef::val_datetime_packed(THD *thd)
{
@@ -7141,7 +7197,7 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
if ((pstr < end) &&
(thd= get_thd())->count_cuted_fields > CHECK_FIELD_EXPRESSION)
{
- if (test_if_important_data(field_charset, pstr, end))
+ if (test_if_important_data(field_charset(), pstr, end))
{
if (thd->abort_on_warning)
set_warning(ER_DATA_TOO_LONG, 1);
@@ -7160,6 +7216,44 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end,
}
+/*
+ This is JSON specific.
+ We should eventually add Field_json_varchar and Field_json_blob
+ and move make_send_field() to the new classes.
+*/
+void Field_longstr::make_send_field(Send_field *field)
+{
+ Field_str::make_send_field(field);
+ if (check_constraint)
+ {
+ /*
+ Append the format that is implicitly implied by the CHECK CONSTRAINT.
+ For example:
+ CREATE TABLE t1 (js longtext DEFAULT NULL CHECK (json_valid(a)));
+ SELECT j FROM t1;
+ will add "format=json" to the extended type info metadata for t1.js.
+ */
+ check_constraint->expr->set_format_by_check_constraint(field);
+ }
+}
+
+
+/*
+ An optimized version that uses less stack than Field::send().
+*/
+bool Field_longstr::send(Protocol *protocol)
+{
+ String tmp;
+ val_str(&tmp, &tmp);
+ /*
+ Ensure this function is only used with classes that do not allocate
+ memory in val_str()
+ */
+ DBUG_ASSERT(tmp.alloced_length() == 0);
+ return protocol->store(tmp.ptr(), tmp.length(), tmp.charset());
+}
+
+
/* Copy a string and fill with space */
int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
@@ -7173,14 +7267,14 @@ int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs)
rc= well_formed_copy_with_check((char*) ptr, field_length,
cs, from, length,
- field_length / field_charset->mbmaxlen,
+ Field_string::char_length(),
false, &copy_length);
/* Append spaces if the string was shorter than the field. */
if (copy_length < field_length)
- field_charset->cset->fill(field_charset,(char*) ptr+copy_length,
- field_length-copy_length,
- field_charset->pad_char);
+ field_charset()->fill((char*) ptr + copy_length,
+ field_length - copy_length,
+ field_charset()->pad_char);
return rc;
}
@@ -7190,13 +7284,10 @@ int Field_str::store(longlong nr, bool unsigned_val)
{
char buff[64];
uint length;
- length= (uint) (field_charset->cset->longlong10_to_str)(field_charset,
- buff,
- sizeof(buff),
- (unsigned_val ? 10:
- -10),
- nr);
- return store(buff, length, field_charset);
+ length= (uint) (field_charset()->longlong10_to_str)(buff, sizeof(buff),
+ (unsigned_val ? 10: -10),
+ nr);
+ return store(buff, length, field_charset());
}
@@ -7212,8 +7303,7 @@ int Field_str::store(double nr)
{
DBUG_ASSERT(marked_for_write_or_computed());
char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
- uint local_char_length= MY_MIN(sizeof(buff),
- field_length / field_charset->mbmaxlen);
+ uint local_char_length= MY_MIN(sizeof(buff), Field_str::char_length());
size_t length= 0;
my_bool error= (local_char_length == 0);
@@ -7236,7 +7326,7 @@ bool Field_string::is_equal(const Column_definition &new_field) const
DBUG_ASSERT(!compression_method());
return new_field.type_handler() == type_handler() &&
new_field.char_length == char_length() &&
- new_field.charset == field_charset &&
+ new_field.charset == field_charset() &&
new_field.length == max_display_length();
}
@@ -7322,7 +7412,7 @@ Field_string::Warn_filter_string::Warn_filter_string(const THD *thd,
const Field_string *field)
:Warn_filter(!thd->no_errors,
!thd->no_errors &&
- field->Field_string::charset() == &my_charset_bin)
+ field->field_charset() == &my_charset_bin)
{ }
@@ -7370,12 +7460,11 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
size_t length;
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);
+ length= field_charset()->charpos(ptr, ptr + field_length,
+ Field_string::char_length());
else
- length= field_charset->cset->lengthsp(field_charset, (const char*) ptr,
- field_length);
- val_ptr->set((const char*) ptr, length, field_charset);
+ length= field_charset()->lengthsp((const char*) ptr, field_length);
+ val_ptr->set((const char*) ptr, length, field_charset());
return val_ptr;
}
@@ -7395,7 +7484,7 @@ my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
struct Check_field_param {
- Field *field;
+ const Field *field;
};
#ifdef HAVE_REPLICATION
@@ -7414,9 +7503,9 @@ check_field_for_37426(const void *param_arg)
bool
Field_string::compatible_field_size(uint field_metadata,
- Relay_log_info *rli_arg,
+ const Relay_log_info *rli_arg,
uint16 mflags __attribute__((unused)),
- int *order_var)
+ int *order_var) const
{
#ifdef HAVE_REPLICATION
const Check_field_param check_param = { this };
@@ -7428,15 +7517,15 @@ Field_string::compatible_field_size(uint field_metadata,
}
-int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
size_t a_len, b_len;
- if (field_charset->mbmaxlen != 1)
+ if (mbmaxlen() != 1)
{
- size_t char_len= field_length/field_charset->mbmaxlen;
- a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len);
- b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len);
+ size_t char_len= Field_string::char_length();
+ a_len= field_charset()->charpos(a_ptr, a_ptr + field_length, char_len);
+ b_len= field_charset()->charpos(b_ptr, b_ptr + field_length, char_len);
}
else
a_len= b_len= field_length;
@@ -7444,9 +7533,8 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
We have to remove end space to be able to compare multi-byte-characters
like in latin_de 'ae' and 0xe4
*/
- return field_charset->coll->strnncollsp(field_charset,
- a_ptr, a_len,
- b_ptr, b_len);
+ return field_charset()->strnncollsp(a_ptr, a_len,
+ b_ptr, b_len);
}
@@ -7455,13 +7543,11 @@ void Field_string::sort_string(uchar *to,uint length)
#ifdef DBUG_ASSERT_EXISTS
size_t tmp=
#endif
- 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);
+ field_charset()->strnxfrm(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);
}
@@ -7510,38 +7596,8 @@ void Field_string::sql_rpl_type(String *res) const
uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
{
- size_t length= MY_MIN(field_length,max_length);
- size_t local_char_length= max_length/field_charset->mbmaxlen;
- DBUG_PRINT("debug", ("Packing field '%s' - length: %zu ", field_name.str,
- length));
-
- if (length > local_char_length)
- local_char_length= my_charpos(field_charset, from, from+length,
- local_char_length);
- set_if_smaller(length, local_char_length);
-
- /*
- TODO: change charset interface to add a new function that does
- the following or add a flag to lengthsp to do it itself
- (this is for not packing padding adding bytes in BINARY
- fields).
- */
- if (field_charset->mbmaxlen == 1)
- {
- while (length && from[length-1] == field_charset->pad_char)
- length --;
- }
- else
- length= field_charset->cset->lengthsp(field_charset, (const char*) from, length);
-
- // Length always stored little-endian
- *to++= (uchar) length;
- if (field_length > 255)
- *to++= (uchar) (length >> 8);
-
- // Store the actual bytes of the string
- memcpy(to, from, length);
- return to+length;
+ DBUG_PRINT("debug", ("Packing field '%s'", field_name.str));
+ return StringPack(field_charset(), field_length).pack(to, from, max_length);
}
@@ -7568,44 +7624,8 @@ const uchar *
Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
uint param_data)
{
- uint from_length, length;
-
- /*
- Compute the declared length of the field on the master. This is
- used to decide if one or two bytes should be read as length.
- */
- if (param_data)
- from_length= (((param_data >> 4) & 0x300) ^ 0x300) + (param_data & 0x00ff);
- else
- from_length= field_length;
-
- DBUG_PRINT("debug",
- ("param_data: 0x%x, field_length: %u, from_length: %u",
- param_data, field_length, from_length));
- /*
- Compute the actual length of the data by reading one or two bits
- (depending on the declared field length on the master).
- */
- if (from_length > 255)
- {
- if (from + 2 > from_end)
- return 0;
- length= uint2korr(from);
- from+= 2;
- }
- else
- {
- if (from + 1 > from_end)
- return 0;
- length= (uint) *from++;
- }
- if (from + length > from_end || length > field_length)
- return 0;
-
- memcpy(to, from, length);
- // Pad the string with the pad character of the fields charset
- field_charset->cset->fill(field_charset, (char*) to + length, field_length - length, field_charset->pad_char);
- return from+length;
+ return StringPack(field_charset(), field_length).unpack(to, from, from_end,
+ param_data);
}
@@ -7639,41 +7659,51 @@ Field_string::unpack(uchar *to, const uchar *from, const uchar *from_end,
@returns number of bytes written to metadata_ptr
*/
-int Field_string::save_field_metadata(uchar *metadata_ptr)
+
+Binlog_type_info_fixed_string::Binlog_type_info_fixed_string(uchar type_code,
+ uint32 octets,
+ CHARSET_INFO *cs)
+ :Binlog_type_info(type_code, 0, 2, cs)
{
- DBUG_ASSERT(field_length < 1024);
- DBUG_ASSERT((real_type() & 0xF0) == 0xF0);
- DBUG_PRINT("debug", ("field_length: %u, real_type: %u",
- field_length, real_type()));
- *metadata_ptr= (real_type() ^ ((field_length & 0x300) >> 4));
- *(metadata_ptr + 1)= field_length & 0xFF;
- return 2;
+ DBUG_ASSERT(octets < 1024);
+ DBUG_ASSERT((type_code & 0xF0) == 0xF0);
+ DBUG_PRINT("debug", ("octets: %u, type_code: %u", octets, type_code));
+ m_metadata= (type_code ^ ((octets & 0x300) >> 4)) +
+ (((uint)(octets & 0xFF)) << 8);
+}
+
+
+Binlog_type_info Field_string::binlog_type_info() const
+{
+ DBUG_ASSERT(Field_string::type() == binlog_type());
+ return Binlog_type_info_fixed_string(Field_string::binlog_type(),
+ field_length, charset());
}
uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
{
- if (length > 255)
- return uint2korr(data_ptr)+2;
- return (uint) *data_ptr + 1;
+ return StringPack::packed_col_length(data_ptr, length);
}
uint Field_string::max_packed_col_length(uint max_length)
{
- return (max_length > 255 ? 2 : 1)+max_length;
+ return StringPack::max_packed_col_length(max_length);
}
-uint Field_string::get_key_image(uchar *buff, uint length, imagetype type_arg)
+uint Field_string::get_key_image(uchar *buff, uint length, const uchar *ptr_arg,
+ imagetype type_arg) const
{
- size_t bytes = my_charpos(field_charset, (char*) ptr,
- (char*) ptr + field_length,
- length / field_charset->mbmaxlen);
- memcpy(buff, ptr, bytes);
+ size_t bytes= field_charset()->charpos((char*) ptr_arg,
+ (char*) ptr_arg + field_length,
+ length / mbmaxlen());
+ memcpy(buff, ptr_arg, bytes);
if (bytes < length)
- field_charset->cset->fill(field_charset, (char*) buff + bytes,
- length - bytes, field_charset->pad_char);
+ field_charset()->fill((char*) buff + bytes,
+ length - bytes,
+ field_charset()->pad_char);
return (uint)bytes;
}
@@ -7699,6 +7729,12 @@ Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table,
}
+en_fieldtype Field_string::tmp_engine_column_type(bool use_packed_rows) const
+{
+ return field_length >= MIN_STRING_LENGTH_TO_PACK_ROWS ? FIELD_SKIP_ENDSPACE :
+ FIELD_NORMAL;
+}
+
/****************************************************************************
VARCHAR type
Data in field->ptr is stored as:
@@ -7728,11 +7764,10 @@ const uint Field_varstring::MAX_SIZE= UINT_MAX16;
@returns number of bytes written to metadata_ptr
*/
-int Field_varstring::save_field_metadata(uchar *metadata_ptr)
+Binlog_type_info Field_varstring::binlog_type_info() const
{
- DBUG_ASSERT(field_length <= 65535);
- int2store((char*)metadata_ptr, field_length);
- return 2;
+ DBUG_ASSERT(Field_varstring::type() == binlog_type());
+ return Binlog_type_info(Field_varstring::type(), field_length, 2, charset());
}
@@ -7754,7 +7789,7 @@ int Field_varstring::store(const char *from,size_t length,CHARSET_INFO *cs)
rc= well_formed_copy_with_check((char*) get_data(), field_length,
cs, from, length,
- field_length / field_charset->mbmaxlen,
+ Field_varstring::char_length(),
true, &copy_length);
store_length(copy_length);
@@ -7789,7 +7824,7 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
DBUG_ASSERT(marked_for_read());
- val_ptr->set((const char*) get_data(), get_length(), field_charset);
+ val_ptr->set((const char*) get_data(), get_length(), field_charset());
return val_ptr;
}
@@ -7808,6 +7843,17 @@ my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
}
+/*
+ An optimized version that uses less stack and less temporary
+ variable initialization than Field_longstr::send()
+*/
+bool Field_varstring::send(Protocol *protocol)
+{
+ return protocol->store((const char *) get_data(), get_length(),
+ field_charset());
+}
+
+
#ifdef HAVE_valgrind
void Field_varstring::mark_unused_memory_as_defined()
{
@@ -7817,7 +7863,7 @@ void Field_varstring::mark_unused_memory_as_defined()
#endif
-int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
uint a_length, b_length;
int diff;
@@ -7834,13 +7880,8 @@ int Field_varstring::cmp(const uchar *a_ptr, const uchar *b_ptr)
}
set_if_smaller(a_length, field_length);
set_if_smaller(b_length, field_length);
- diff= field_charset->coll->strnncollsp(field_charset,
- a_ptr+
- length_bytes,
- a_length,
- b_ptr+
- length_bytes,
- b_length);
+ diff= field_charset()->strnncollsp(a_ptr + length_bytes, a_length,
+ b_ptr + length_bytes, b_length);
return diff;
}
@@ -7859,7 +7900,7 @@ static int cmp_str_prefix(const uchar *ua, size_t alen, const uchar *ub,
int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
- size_t prefix_len)
+ size_t prefix_len) const
{
/* avoid expensive well_formed_char_length if possible */
if (prefix_len == table->field[field_index]->field_length)
@@ -7878,7 +7919,7 @@ int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
b_length= uint2korr(b_ptr);
}
return cmp_str_prefix(a_ptr+length_bytes, a_length, b_ptr+length_bytes,
- b_length, prefix_len, field_charset);
+ b_length, prefix_len, field_charset());
}
@@ -7887,20 +7928,19 @@ int Field_varstring::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/
-int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length)
+int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length) const
{
size_t length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- size_t local_char_length= max_key_length / field_charset->mbmaxlen;
+ size_t local_char_length= max_key_length / mbmaxlen();
- local_char_length= my_charpos(field_charset, ptr + length_bytes,
- ptr + length_bytes + length, local_char_length);
+ local_char_length= field_charset()->charpos(ptr + length_bytes,
+ ptr + length_bytes + length,
+ local_char_length);
set_if_smaller(length, local_char_length);
- return field_charset->coll->strnncollsp(field_charset,
- ptr + length_bytes,
- length,
- key_ptr+
- HA_KEY_BLOB_LENGTH,
- uint2korr(key_ptr));
+ return field_charset()->strnncollsp(ptr + length_bytes,
+ length,
+ key_ptr + HA_KEY_BLOB_LENGTH,
+ uint2korr(key_ptr));
}
@@ -7912,13 +7952,10 @@ int Field_varstring::key_cmp(const uchar *key_ptr, uint max_key_length)
(keys are created and compared in key.cc)
*/
-int Field_varstring::key_cmp(const uchar *a,const uchar *b)
+int Field_varstring::key_cmp(const uchar *a,const uchar *b) const
{
- return field_charset->coll->strnncollsp(field_charset,
- a + HA_KEY_BLOB_LENGTH,
- uint2korr(a),
- b + HA_KEY_BLOB_LENGTH,
- uint2korr(b));
+ return field_charset()->strnncollsp(a + HA_KEY_BLOB_LENGTH, uint2korr(a),
+ b + HA_KEY_BLOB_LENGTH, uint2korr(b));
}
@@ -7928,7 +7965,7 @@ void Field_varstring::sort_string(uchar *to,uint length)
val_str(&buf, &buf);
- if (field_charset == &my_charset_bin)
+ if (field_charset() == &my_charset_bin)
{
/* Store length last in high-byte order to sort longer strings first */
if (length_bytes == 1)
@@ -7941,11 +7978,11 @@ void Field_varstring::sort_string(uchar *to,uint length)
#ifdef DBUG_ASSERT_EXISTS
size_t rc=
#endif
- field_charset->coll->strnxfrm(field_charset, to, length,
- char_length() * field_charset->strxfrm_multiply,
- (const uchar*) buf.ptr(), buf.length(),
- MY_STRXFRM_PAD_WITH_SPACE |
- MY_STRXFRM_PAD_TO_MAXLEN);
+ field_charset()->strnxfrm(to, length,
+ char_length() * field_charset()->strxfrm_multiply,
+ (const uchar *) buf.ptr(), buf.length(),
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(rc == length);
}
@@ -8098,18 +8135,19 @@ uint Field_varstring::max_packed_col_length(uint max_length)
return (max_length > 255 ? 2 : 1)+max_length;
}
+void Field_varstring::val_str_from_ptr(String *val, const uchar *ptr) const
+{
+ val->set((const char*) get_data(ptr), get_length(ptr), field_charset());
+}
+
uint Field_varstring::get_key_image(uchar *buff, uint length,
- imagetype type_arg)
+ const uchar *ptr_arg,
+ imagetype type_arg) const
{
String val;
- uint local_char_length;
- my_bitmap_map *old_map;
+ val_str_from_ptr(&val, ptr_arg);
- old_map= dbug_tmp_use_all_columns(table, table->read_set);
- val_str(&val, &val);
- dbug_tmp_restore_column_map(table->read_set, old_map);
-
- local_char_length= val.charpos(length / field_charset->mbmaxlen);
+ uint local_char_length= val.charpos(length / mbmaxlen());
if (local_char_length < val.length())
val.length(local_char_length);
/* Key is always stored with 2 bytes */
@@ -8130,12 +8168,12 @@ uint Field_varstring::get_key_image(uchar *buff, uint length,
void Field_varstring::set_key_image(const uchar *buff,uint length)
{
length= uint2korr(buff); // Real length is here
- (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset);
+ (void) store((const char*) buff + HA_KEY_BLOB_LENGTH, length, field_charset());
}
int Field_varstring::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
- uint32 max_length)
+ uint32 max_length) const
{
uint32 a_length,b_length;
@@ -8190,7 +8228,7 @@ bool Field_varstring::is_equal(const Column_definition &new_field) const
new_field.length == field_length &&
new_field.char_length == char_length() &&
!new_field.compression_method() == !compression_method() &&
- new_field.charset == field_charset;
+ new_field.charset == field_charset();
}
@@ -8203,8 +8241,7 @@ void Field_varstring::hash(ulong *nr, ulong *nr2)
else
{
uint len= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- CHARSET_INFO *cs= charset();
- cs->coll->hash_sort(cs, ptr + length_bytes, len, nr, nr2);
+ charset()->hash_sort(ptr + length_bytes, len, nr, nr2);
}
}
@@ -8254,11 +8291,11 @@ int Field_longstr::compress(char *to, uint to_length,
uint buf_length;
int rc= 0;
- if (String::needs_conversion_on_storage(length, cs, field_charset) ||
+ if (String::needs_conversion_on_storage(length, cs, field_charset()) ||
max_length < length)
{
- set_if_smaller(max_length, static_cast<ulonglong>(field_charset->mbmaxlen) * length + 1);
- if (!(buf= (char*) my_malloc(max_length, MYF(MY_WME))))
+ set_if_smaller(max_length, static_cast<ulonglong>(mbmaxlen()) * length + 1);
+ if (!(buf= (char*) my_malloc(PSI_INSTRUMENT_ME, max_length, MYF(MY_WME))))
{
*out_length= 0;
return -1;
@@ -8308,7 +8345,7 @@ int Field_longstr::compress(char *to, uint to_length,
*/
String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
- const uchar *from, uint from_length)
+ const uchar *from, uint from_length) const
{
if (from_length)
{
@@ -8317,7 +8354,7 @@ String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
/* Uncompressed data */
if (!method)
{
- val_ptr->set((const char*) from + 1, from_length - 1, field_charset);
+ val_ptr->set((const char*) from + 1, from_length - 1, field_charset());
return val_ptr;
}
@@ -8326,7 +8363,7 @@ String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
if (!compression_methods[method].uncompress(val_buffer, from, from_length,
field_length))
{
- val_buffer->set_charset(field_charset);
+ val_buffer->set_charset(field_charset());
status_var_increment(get_thd()->status_var.column_decompressions);
return val_buffer;
}
@@ -8338,7 +8375,7 @@ String *Field_longstr::uncompress(String *val_buffer, String *val_ptr,
safer route, let's return a zero string and let the general
handler catch the error.
*/
- val_ptr->set("", 0, field_charset);
+ val_ptr->set("", 0, field_charset());
return val_ptr;
}
@@ -8356,6 +8393,11 @@ int Field_varstring_compressed::store(const char *from, size_t length,
return rc;
}
+void Field_varstring_compressed::val_str_from_ptr(String *val, const uchar *ptr) const
+{
+ uncompress(val, val, get_data(ptr), get_length(ptr));
+}
+
String *Field_varstring_compressed::val_str(String *val_buffer, String *val_ptr)
{
@@ -8370,7 +8412,7 @@ double Field_varstring_compressed::val_real(void)
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
- return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset,
+ return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset(),
buf.ptr(), buf.length()).result();
}
@@ -8381,12 +8423,13 @@ longlong Field_varstring_compressed::val_int(void)
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
- return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset,
+ return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
buf.ptr(), buf.length()).result();
}
-int Field_varstring_compressed::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_varstring_compressed::cmp(const uchar *a_ptr,
+ const uchar *b_ptr) const
{
String a, b;
uint a_length, b_length;
@@ -8405,7 +8448,14 @@ int Field_varstring_compressed::cmp(const uchar *a_ptr, const uchar *b_ptr)
uncompress(&a, &a, a_ptr + length_bytes, a_length);
uncompress(&b, &b, b_ptr + length_bytes, b_length);
- return sortcmp(&a, &b, field_charset);
+ return sortcmp(&a, &b, field_charset());
+}
+
+
+Binlog_type_info Field_varstring_compressed::binlog_type_info() const
+{
+ return Binlog_type_info(Field_varstring_compressed::binlog_type(),
+ field_length, 2, charset());
}
@@ -8450,7 +8500,7 @@ uint32 Field_blob::get_length(const uchar *pos, uint packlength_arg) const
*/
int Field_blob::copy_value(Field_blob *from)
{
- DBUG_ASSERT(field_charset == from->charset());
+ DBUG_ASSERT(field_charset() == from->charset());
DBUG_ASSERT(!compression_method() == !from->compression_method());
int rc= 0;
uint32 length= from->get_length();
@@ -8458,7 +8508,7 @@ int Field_blob::copy_value(Field_blob *from)
if (packlength < from->packlength)
{
set_if_smaller(length, Field_blob::max_data_length());
- length= (uint32) Well_formed_prefix(field_charset,
+ length= (uint32) Well_formed_prefix(field_charset(),
(const char *) data, length).length();
rc= report_if_important_data((const char *) data + length,
(const char *) data + from->get_length(),
@@ -8495,7 +8545,7 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
if (table && table->blob_storage) // GROUP_CONCAT with ORDER BY | DISTINCT
{
DBUG_ASSERT(!f_is_hex_escape(flags));
- DBUG_ASSERT(field_charset == cs);
+ DBUG_ASSERT(field_charset() == cs);
DBUG_ASSERT(length <= max_data_length());
new_length= length;
@@ -8526,7 +8576,7 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
If content of the 'from'-address is cached in the 'value'-object
it is possible that the content needs a character conversion.
*/
- if (!String::needs_conversion_on_storage(length, cs, field_charset))
+ if (!String::needs_conversion_on_storage(length, cs, field_charset()))
{
Field_blob::store_length(length);
bmove(ptr + packlength, &from, sizeof(char*));
@@ -8537,14 +8587,14 @@ int Field_blob::store(const char *from,size_t length,CHARSET_INFO *cs)
from= tmpstr.ptr();
}
- new_length= MY_MIN(max_data_length(), field_charset->mbmaxlen * length);
+ new_length= MY_MIN(max_data_length(), mbmaxlen() * length);
if (value.alloc(new_length))
goto oom_error;
tmp= const_cast<char*>(value.ptr());
if (f_is_hex_escape(flags))
{
- copy_length= my_copy_with_hex_escaping(field_charset,
+ copy_length= my_copy_with_hex_escaping(field_charset(),
tmp, new_length,
from, length);
Field_blob::store_length(copy_length);
@@ -8632,14 +8682,13 @@ my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
int Field_blob::cmp(const uchar *a,uint32 a_length, const uchar *b,
- uint32 b_length)
+ uint32 b_length) const
{
- return field_charset->coll->strnncollsp(field_charset,
- a, a_length, b, b_length);
+ return field_charset()->strnncollsp(a, a_length, b, b_length);
}
-int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
uchar *blob1,*blob2;
memcpy(&blob1, a_ptr+packlength, sizeof(char*));
@@ -8650,18 +8699,19 @@ int Field_blob::cmp(const uchar *a_ptr, const uchar *b_ptr)
int Field_blob::cmp_prefix(const uchar *a_ptr, const uchar *b_ptr,
- size_t prefix_len)
+ size_t prefix_len) const
{
uchar *blob1,*blob2;
memcpy(&blob1, a_ptr+packlength, sizeof(char*));
memcpy(&blob2, b_ptr+packlength, sizeof(char*));
size_t a_len= get_length(a_ptr), b_len= get_length(b_ptr);
- return cmp_str_prefix(blob1, a_len, blob2, b_len, prefix_len, field_charset);
+ return cmp_str_prefix(blob1, a_len, blob2, b_len, prefix_len,
+ field_charset());
}
int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
- uint32 max_length)
+ uint32 max_length) const
{
char *a,*b;
uint diff;
@@ -8681,44 +8731,14 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
/* The following is used only when comparing a key */
-uint Field_blob::get_key_image(uchar *buff,uint length, imagetype type_arg)
+uint Field_blob::get_key_image_itRAW(const uchar *ptr_arg, uchar *buff,
+ uint length) const
{
- size_t blob_length= get_length(ptr);
- uchar *blob;
-
-#ifdef HAVE_SPATIAL
- if (type_arg == itMBR)
- {
- const char *dummy;
- MBR mbr;
- Geometry_buffer buffer;
- Geometry *gobj;
- const uint image_length= SIZEOF_STORED_DOUBLE*4;
-
- if (blob_length < SRID_SIZE)
- {
- bzero(buff, image_length);
- return image_length;
- }
- blob= get_ptr();
- gobj= Geometry::construct(&buffer, (char*) blob, (uint32)blob_length);
- if (!gobj || gobj->get_mbr(&mbr, &dummy))
- bzero(buff, image_length);
- else
- {
- float8store(buff, mbr.xmin);
- float8store(buff+8, mbr.xmax);
- float8store(buff+16, mbr.ymin);
- float8store(buff+24, mbr.ymax);
- }
- return image_length;
- }
-#endif /*HAVE_SPATIAL*/
-
- blob= get_ptr();
- size_t local_char_length= length / field_charset->mbmaxlen;
- local_char_length= my_charpos(field_charset, blob, blob + blob_length,
- local_char_length);
+ size_t blob_length= get_length(ptr_arg);
+ const uchar *blob= get_ptr(ptr_arg);
+ size_t local_char_length= length / mbmaxlen();
+ local_char_length= field_charset()->charpos(blob, blob + blob_length,
+ local_char_length);
set_if_smaller(blob_length, local_char_length);
if (length > blob_length)
@@ -8740,26 +8760,26 @@ void Field_blob::set_key_image(const uchar *buff,uint length)
{
length= uint2korr(buff);
(void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
- field_charset);
+ field_charset());
}
-int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
+int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length) const
{
uchar *blob1;
size_t blob_length=get_length(ptr);
memcpy(&blob1, ptr+packlength, sizeof(char*));
CHARSET_INFO *cs= charset();
size_t local_char_length= max_key_length / cs->mbmaxlen;
- local_char_length= my_charpos(cs, blob1, blob1+blob_length,
- local_char_length);
+ local_char_length= cs->charpos(blob1, blob1+blob_length,
+ local_char_length);
set_if_smaller(blob_length, local_char_length);
return Field_blob::cmp(blob1, (uint32)blob_length,
key_ptr+HA_KEY_BLOB_LENGTH,
uint2korr(key_ptr));
}
-int Field_blob::key_cmp(const uchar *a,const uchar *b)
+int Field_blob::key_cmp(const uchar *a,const uchar *b) const
{
return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
b+HA_KEY_BLOB_LENGTH, uint2korr(b));
@@ -8790,19 +8810,25 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
@returns number of bytes written to metadata_ptr
*/
-int Field_blob::save_field_metadata(uchar *metadata_ptr)
+Binlog_type_info Field_blob::binlog_type_info() const
{
- DBUG_ENTER("Field_blob::save_field_metadata");
- *metadata_ptr= pack_length_no_ptr();
- DBUG_PRINT("debug", ("metadata: %u (pack_length_no_ptr)", *metadata_ptr));
- DBUG_RETURN(1);
+ DBUG_ASSERT(Field_blob::type() == binlog_type());
+ return Binlog_type_info(Field_blob::type(), pack_length_no_ptr(), 1,
+ charset());
}
uint32 Field_blob::sort_length() const
{
- return (uint32) (get_thd()->variables.max_sort_length +
- (field_charset == &my_charset_bin ? 0 : packlength));
+ return packlength == 4 ?
+ UINT_MAX32 :
+ (uint32) field_length + sort_suffix_length();
+}
+
+
+uint32 Field_blob::sort_suffix_length() const
+{
+ return field_charset() == &my_charset_bin ? packlength : 0;
}
@@ -8811,11 +8837,11 @@ void Field_blob::sort_string(uchar *to,uint length)
String buf;
val_str(&buf, &buf);
- if (!buf.length() && field_charset->pad_char == 0)
+ if (!buf.length() && field_charset()->pad_char == 0)
bzero(to,length);
else
{
- if (field_charset == &my_charset_bin)
+ if (field_charset() == &my_charset_bin)
{
/*
Store length of blob last in blob to shorter blobs before longer blobs
@@ -8827,10 +8853,10 @@ void Field_blob::sort_string(uchar *to,uint length)
#ifdef DBUG_ASSERT_EXISTS
size_t rc=
#endif
- field_charset->coll->strnxfrm(field_charset, to, length, length,
- (const uchar*) buf.ptr(), buf.length(),
- MY_STRXFRM_PAD_WITH_SPACE |
- MY_STRXFRM_PAD_TO_MAXLEN);
+ field_charset()->strnxfrm(to, length, length,
+ (const uchar *) buf.ptr(), buf.length(),
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(rc == length);
}
}
@@ -8877,9 +8903,7 @@ void Field_blob::sql_type(String &res) const
uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
{
- uchar *save= ptr;
- ptr= (uchar*) from;
- uint32 length=get_length(); // Length of from string
+ uint32 length=get_length(from, packlength); // Length of from string
/*
Store max length, which will occupy packlength bytes. If the max
@@ -8893,10 +8917,9 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
*/
if (length > 0)
{
- from= get_ptr();
+ from= get_ptr(from);
memcpy(to+packlength, from,length);
}
- ptr=save; // Restore org row pointer
return to+packlength+length;
}
@@ -8965,7 +8988,7 @@ bool Field_blob::is_equal(const Column_definition &new_field) const
return new_field.type_handler() == type_handler() &&
!new_field.compression_method() == !compression_method() &&
new_field.pack_length == pack_length() &&
- new_field.charset == field_charset;
+ new_field.charset == field_charset();
}
@@ -9001,8 +9024,7 @@ int Field_blob_compressed::store(const char *from, size_t length,
DBUG_ASSERT(marked_for_write_or_computed());
uint compressed_length;
uint max_length= max_data_length();
- uint to_length= (uint) MY_MIN(max_length,
- field_charset->mbmaxlen * length + 1);
+ uint to_length= (uint) MY_MIN(max_length, mbmaxlen() * length + 1);
String tmp(from, length, cs);
int rc;
@@ -9036,7 +9058,7 @@ double Field_blob_compressed::val_real(void)
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
- return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset,
+ return Converter_strntod_with_warn(thd, Warn_filter(thd), field_charset(),
buf.ptr(), buf.length()).result();
}
@@ -9047,285 +9069,16 @@ longlong Field_blob_compressed::val_int(void)
THD *thd= get_thd();
String buf;
val_str(&buf, &buf);
- return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset,
+ return Converter_strntoll_with_warn(thd, Warn_filter(thd), field_charset(),
buf.ptr(), buf.length()).result();
}
-
-#ifdef HAVE_SPATIAL
-/* Values 1-40 reserved for 1-byte options,
- 41-80 for 2-byte options,
- 81-120 for 4-byte options,
- 121-160 for 8-byte options,
- other - varied length in next 1-3 bytes.
-*/
-enum extra2_gis_field_options {
- FIELDGEOM_END=0,
- FIELDGEOM_STORAGE_MODEL=1,
- FIELDGEOM_PRECISION=2,
- FIELDGEOM_SCALE=3,
- FIELDGEOM_SRID=81,
-};
-
-
-uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields)
-{
- uint image_size= 0;
- List_iterator<Create_field> it(create_fields);
- Create_field *field;
- while ((field= it++))
- {
- if (field->real_field_type() != MYSQL_TYPE_GEOMETRY)
- continue;
- if (buff)
- {
- uchar *cbuf= buff + image_size;
-
- cbuf[0]= FIELDGEOM_STORAGE_MODEL;
- cbuf[1]= (uchar) Field_geom::GEOM_STORAGE_WKB;
-
- cbuf[2]= FIELDGEOM_PRECISION;
- cbuf[3]= (uchar) field->length;
-
- cbuf[4]= FIELDGEOM_SCALE;
- cbuf[5]= (uchar) field->decimals;
-
- cbuf[6]= FIELDGEOM_SRID;
- int4store(cbuf + 7, ((uint32) field->srid));
-
- cbuf[11]= FIELDGEOM_END;
- }
- image_size+= 12;
- }
-
- return image_size;
-}
-
-
-uint gis_field_options_read(const uchar *buf, size_t buf_len,
- Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid)
-{
- const uchar *buf_end= buf + buf_len;
- const uchar *cbuf= buf;
- int option_id;
-
- *precision= *scale= *srid= 0;
- *st_type= Field_geom::GEOM_STORAGE_WKB;
-
- if (!buf) /* can only happen with the old FRM file */
- goto end_of_record;
-
- while (cbuf < buf_end)
- {
- switch ((option_id= *(cbuf++)))
- {
- case FIELDGEOM_STORAGE_MODEL:
- *st_type= (Field_geom::storage_type) cbuf[0];
- break;
- case FIELDGEOM_PRECISION:
- *precision= cbuf[0];
- break;
- case FIELDGEOM_SCALE:
- *scale= cbuf[0];
- break;
- case FIELDGEOM_SRID:
- *srid= uint4korr(cbuf);
- break;
- case FIELDGEOM_END:
- goto end_of_record;
- }
- if (option_id > 0 && option_id <= 40)
- cbuf+= 1;
- else if (option_id > 40 && option_id <= 80)
- cbuf+= 2;
- else if (option_id > 80 && option_id <= 120)
- cbuf+= 4;
- else if (option_id > 120 && option_id <= 160)
- cbuf+= 8;
- else /* > 160 and <=255 */
- cbuf+= cbuf[0] ? 1 + cbuf[0] : 3 + uint2korr(cbuf+1);
- }
-
-end_of_record:
- return (uint)(cbuf - buf);
-}
-
-
-
-void Field_geom::sql_type(String &res) const
+Binlog_type_info Field_blob_compressed::binlog_type_info() const
{
- CHARSET_INFO *cs= &my_charset_latin1;
- switch (geom_type)
- {
- case GEOM_POINT:
- res.set(STRING_WITH_LEN("point"), cs);
- break;
- case GEOM_LINESTRING:
- res.set(STRING_WITH_LEN("linestring"), cs);
- break;
- case GEOM_POLYGON:
- res.set(STRING_WITH_LEN("polygon"), cs);
- break;
- case GEOM_MULTIPOINT:
- res.set(STRING_WITH_LEN("multipoint"), cs);
- break;
- case GEOM_MULTILINESTRING:
- res.set(STRING_WITH_LEN("multilinestring"), cs);
- break;
- case GEOM_MULTIPOLYGON:
- res.set(STRING_WITH_LEN("multipolygon"), cs);
- break;
- case GEOM_GEOMETRYCOLLECTION:
- res.set(STRING_WITH_LEN("geometrycollection"), cs);
- break;
- default:
- res.set(STRING_WITH_LEN("geometry"), cs);
- }
+ return Binlog_type_info(Field_blob_compressed::binlog_type(),
+ pack_length_no_ptr(), 1, charset());
}
-
-int Field_geom::store(double nr)
-{
- my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
- ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
- return -1;
-}
-
-
-int Field_geom::store(longlong nr, bool unsigned_val)
-{
- my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
- ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
- return -1;
-}
-
-
-int Field_geom::store_decimal(const my_decimal *)
-{
- my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
- ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
- return -1;
-}
-
-
-int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs)
-{
- if (!length)
- bzero(ptr, Field_blob::pack_length());
- else
- {
- if (from == Geometry::bad_geometry_data.ptr())
- goto err;
- // Check given WKB
- uint32 wkb_type;
- if (length < SRID_SIZE + WKB_HEADER_SIZE + 4)
- goto err;
- wkb_type= uint4korr(from + SRID_SIZE + 1);
- if (wkb_type < (uint32) Geometry::wkb_point ||
- wkb_type > (uint32) Geometry::wkb_last)
- goto err;
-
- if (geom_type != Field::GEOM_GEOMETRY &&
- geom_type != Field::GEOM_GEOMETRYCOLLECTION &&
- (uint32) geom_type != wkb_type)
- {
- const char *db= table->s->db.str;
- const char *tab_name= table->s->table_name.str;
- Geometry_buffer buffer;
- Geometry *geom= NULL;
- String wkt;
- const char *dummy;
-
- if (!db)
- db= "";
- if (!tab_name)
- tab_name= "";
- wkt.set_charset(&my_charset_latin1);
- if (!(geom= Geometry::construct(&buffer, from, uint32(length))) ||
- geom->as_wkt(&wkt, &dummy))
- goto err;
-
- my_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, MYF(0),
- Geometry::ci_collection[geom_type]->m_name.str,
- wkt.c_ptr_safe(), db, tab_name, field_name.str,
- (ulong) table->in_use->get_stmt_da()->
- current_row_for_warning());
-
- goto err_exit;
- }
-
- Field_blob::store_length(length);
- if ((table->copy_blobs || length <= MAX_FIELD_WIDTH) &&
- from != value.ptr())
- { // Must make a copy
- value.copy(from, length, cs);
- from= value.ptr();
- }
- bmove(ptr + packlength, &from, sizeof(char*));
- }
- return 0;
-
-err:
- my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
- ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
-err_exit:
- bzero(ptr, Field_blob::pack_length());
- return -1;
-}
-
-Field::geometry_type Field_geom::geometry_type_merge(geometry_type a,
- geometry_type b)
-{
- if (a == b)
- return a;
- return Field::GEOM_GEOMETRY;
-}
-
-
-bool Field_geom::is_equal(const Column_definition &new_field) const
-{
- return new_field.type_handler() == type_handler() &&
- /*
- - Allow ALTER..INPLACE to supertype (GEOMETRY),
- e.g. POINT to GEOMETRY or POLYGON to GEOMETRY.
- - Allow ALTER..INPLACE to the same geometry type: POINT -> POINT
- */
- (new_field.geom_type == geom_type ||
- new_field.geom_type == GEOM_GEOMETRY);
-}
-
-
-bool Field_geom::can_optimize_range(const Item_bool_func *cond,
- const Item *item,
- bool is_eq_func) const
-{
- return item->cmp_type() == STRING_RESULT;
-}
-
-
-bool Field_geom::load_data_set_no_data(THD *thd, bool fixed_format)
-{
- return Field_geom::load_data_set_null(thd);
-}
-
-
-bool Field_geom::load_data_set_null(THD *thd)
-{
- Field_blob::reset();
- if (!maybe_null())
- {
- my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field_name.str,
- thd->get_stmt_da()->current_row_for_warning());
- return true;
- }
- set_null();
- set_has_explicit_value(); // Do not auto-update this field
- return false;
-}
-
-
-#endif /*HAVE_SPATIAL*/
-
/****************************************************************************
** enum type.
** This is a string which only can have a selection of different values.
@@ -9369,24 +9122,24 @@ int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs)
String tmpstr(buff,sizeof(buff), &my_charset_bin);
/* Convert character set if necessary */
- if (String::needs_conversion_on_storage(length, cs, field_charset))
+ if (String::needs_conversion_on_storage(length, cs, field_charset()))
{
uint dummy_errors;
- tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
+ tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
from= tmpstr.ptr();
length= tmpstr.length();
}
/* Remove end space */
- length= (uint)field_charset->cset->lengthsp(field_charset, from, length);
- uint tmp=find_type2(typelib, from, length, field_charset);
+ length= (uint) field_charset()->lengthsp(from, length);
+ uint tmp=find_type2(typelib, from, length, field_charset());
if (!tmp)
{
if (length < 6) // Can't be more than 99999 enums
{
/* This is for reading numbers with LOAD DATA INFILE */
char *end;
- tmp=(uint) my_strntoul(cs,from,length,10,&end,&err);
+ tmp=(uint) cs->strntoul(from,length,10,&end,&err);
if (err || end != from+length || tmp > typelib->count)
{
tmp=0;
@@ -9440,9 +9193,13 @@ double Field_enum::val_real(void)
longlong Field_enum::val_int(void)
{
DBUG_ASSERT(marked_for_read());
- return read_lowendian(ptr, packlength);
+ return val_int(ptr);
}
+longlong Field_enum::val_int(const uchar *real_ptr) const
+{
+ return read_lowendian(real_ptr, packlength);
+}
/**
Save the field metadata for enum fields.
@@ -9455,11 +9212,11 @@ longlong Field_enum::val_int(void)
@returns number of bytes written to metadata_ptr
*/
-int Field_enum::save_field_metadata(uchar *metadata_ptr)
+Binlog_type_info Field_enum::binlog_type_info() const
{
- *metadata_ptr= real_type();
- *(metadata_ptr + 1)= pack_length();
- return 2;
+ DBUG_ASSERT(Field_enum::type() == binlog_type());
+ return Binlog_type_info(Field_enum::type(), real_type() + (pack_length() << 8),
+ 2, charset(), (TYPELIB *)get_typelib(), NULL);
}
@@ -9468,22 +9225,18 @@ String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
{
uint tmp=(uint) Field_enum::val_int();
if (!tmp || tmp > typelib->count)
- val_ptr->set("", 0, field_charset);
+ val_ptr->set("", 0, field_charset());
else
val_ptr->set((const char*) typelib->type_names[tmp-1],
typelib->type_lengths[tmp-1],
- field_charset);
+ field_charset());
return val_ptr;
}
-int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr)
+int Field_enum::cmp(const uchar *a_ptr, const uchar *b_ptr) const
{
- uchar *old= ptr;
- ptr= (uchar*) a_ptr;
- ulonglong a=Field_enum::val_int();
- ptr= (uchar*) b_ptr;
- ulonglong b=Field_enum::val_int();
- ptr= old;
+ ulonglong a=Field_enum::val_int(a_ptr);
+ ulonglong b=Field_enum::val_int(b_ptr);
return (a < b) ? -1 : (a > b) ? 1 : 0;
}
@@ -9555,20 +9308,20 @@ int Field_set::store(const char *from,size_t length,CHARSET_INFO *cs)
String tmpstr(buff,sizeof(buff), &my_charset_bin);
/* Convert character set if necessary */
- if (String::needs_conversion_on_storage(length, cs, field_charset))
+ if (String::needs_conversion_on_storage(length, cs, field_charset()))
{
uint dummy_errors;
- tmpstr.copy(from, length, cs, field_charset, &dummy_errors);
+ tmpstr.copy(from, length, cs, field_charset(), &dummy_errors);
from= tmpstr.ptr();
length= tmpstr.length();
}
- ulonglong tmp= find_set(typelib, from, length, field_charset,
+ ulonglong tmp= find_set(typelib, from, length, field_charset(),
&not_used, &not_used2, &got_warning);
if (!tmp && length && length < 22)
{
/* This is for reading numbers with LOAD DATA INFILE */
char *end;
- tmp=my_strntoull(cs,from,length,10,&end,&err);
+ tmp= cs->strntoull(from,length,10,&end,&err);
if (err || end != from+length ||
tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1))
{
@@ -9622,7 +9375,7 @@ String *Field_set::val_str(String *val_buffer,
return val_buffer;
}
- val_buffer->set_charset(field_charset);
+ val_buffer->set_charset(field_charset());
val_buffer->length(0);
while (tmp && bitnr < (uint) typelib->count)
@@ -9633,7 +9386,7 @@ String *Field_set::val_str(String *val_buffer,
val_buffer->append(&field_separator, 1, &my_charset_latin1);
String str(typelib->type_names[bitnr],
typelib->type_lengths[bitnr],
- field_charset);
+ field_charset());
val_buffer->append(str);
}
tmp>>=1;
@@ -9666,6 +9419,13 @@ void Field_set::sql_type(String &res) const
res.append(')');
}
+Binlog_type_info Field_set::binlog_type_info() const
+{
+ DBUG_ASSERT(Field_set::type() == binlog_type());
+ return Binlog_type_info(Field_set::type(), real_type()
+ + (pack_length() << 8), 2, charset(), NULL, (TYPELIB *)get_typelib());
+}
+
/**
@retval
1 if the fields are equally defined
@@ -9688,14 +9448,12 @@ bool Field::eq_def(const Field *field) const
@return TRUE if the type names of t1 match those of t2. FALSE otherwise.
*/
-static bool compare_type_names(CHARSET_INFO *charset, TYPELIB *t1, TYPELIB *t2)
+static bool compare_type_names(CHARSET_INFO *charset, const TYPELIB *t1,
+ const TYPELIB *t2)
{
for (uint i= 0; i < t1->count; i++)
- if (my_strnncoll(charset,
- (const uchar*) t1->type_names[i],
- t1->type_lengths[i],
- (const uchar*) t2->type_names[i],
- t2->type_lengths[i]))
+ if (charset->strnncoll(t1->type_names[i], t1->type_lengths[i],
+ t2->type_names[i], t2->type_lengths[i]))
return FALSE;
return TRUE;
}
@@ -9707,7 +9465,7 @@ static bool compare_type_names(CHARSET_INFO *charset, TYPELIB *t1, TYPELIB *t2)
bool Field_enum::eq_def(const Field *field) const
{
- TYPELIB *values;
+ const TYPELIB *values;
if (!Field::eq_def(field))
return FALSE;
@@ -9718,7 +9476,7 @@ bool Field_enum::eq_def(const Field *field) const
if (typelib->count != values->count)
return FALSE;
- return compare_type_names(field_charset, typelib, values);
+ return compare_type_names(field_charset(), typelib, values);
}
@@ -9733,14 +9491,14 @@ bool Field_enum::eq_def(const Field *field) const
bool Field_enum::is_equal(const Column_definition &new_field) const
{
- TYPELIB *values= new_field.interval;
+ const TYPELIB *values= new_field.interval;
/*
The fields are compatible if they have the same flags,
type, charset and have the same underlying length.
*/
if (new_field.type_handler() != type_handler() ||
- new_field.charset != field_charset ||
+ new_field.charset != field_charset() ||
new_field.pack_length != pack_length())
return false;
@@ -9753,7 +9511,7 @@ bool Field_enum::is_equal(const Column_definition &new_field) const
return false;
/* Check whether there are modification before the end. */
- if (! compare_type_names(field_charset, typelib, new_field.interval))
+ if (! compare_type_names(field_charset(), typelib, new_field.interval))
return false;
return true;
@@ -9915,6 +9673,14 @@ Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
}
+const DTCollation & Field_bit::dtcollation() const
+{
+ static DTCollation tmp(&my_charset_bin,
+ DERIVATION_IMPLICIT, MY_REPERTOIRE_UNICODE30);
+ return tmp;
+}
+
+
void Field_bit::hash(ulong *nr, ulong *nr2)
{
if (is_null())
@@ -9927,7 +9693,7 @@ void Field_bit::hash(ulong *nr, ulong *nr2)
longlong value= Field_bit::val_int();
uchar tmp[8];
mi_int8store(tmp,value);
- cs->coll->hash_sort(cs, tmp, 8, nr, nr2);
+ cs->hash_sort(tmp, 8, nr, nr2);
}
}
@@ -10123,7 +9889,8 @@ my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value)
The a and b pointer must be pointers to the field in a record
(not the table->record[0] necessarily)
*/
-int Field_bit::cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len)
+int Field_bit::cmp_prefix(const uchar *a, const uchar *b,
+ size_t prefix_len) const
{
my_ptrdiff_t a_diff= a - ptr;
my_ptrdiff_t b_diff= b - ptr;
@@ -10141,7 +9908,7 @@ int Field_bit::cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len)
}
-int Field_bit::key_cmp(const uchar *str, uint length)
+int Field_bit::key_cmp(const uchar *str, uint length) const
{
if (bit_len)
{
@@ -10170,11 +9937,12 @@ int Field_bit::cmp_offset(my_ptrdiff_t row_offset)
}
-uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
+uint Field_bit::get_key_image(uchar *buff, uint length, const uchar *ptr_arg, imagetype type_arg) const
{
if (bit_len)
{
- uchar bits= get_rec_bits(bit_ptr, bit_ofs, bit_len);
+ const uchar *bit_ptr_for_arg= ptr_arg + (bit_ptr - ptr);
+ uchar bits= get_rec_bits(bit_ptr_for_arg, bit_ofs, bit_len);
*buff++= bits;
length--;
}
@@ -10185,33 +9953,6 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
/**
- Save the field metadata for bit fields.
-
- Saves the bit length in the first byte and bytes in record in the
- second byte of the field metadata array at index of *metadata_ptr and
- *(metadata_ptr + 1).
-
- @param metadata_ptr First byte of field metadata
-
- @returns number of bytes written to metadata_ptr
-*/
-int Field_bit::save_field_metadata(uchar *metadata_ptr)
-{
- DBUG_ENTER("Field_bit::save_field_metadata");
- DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
- bit_len, bytes_in_rec));
- /*
- Since this class and Field_bit_as_char have different ideas of
- what should be stored here, we compute the values of the metadata
- explicitly using the field_length.
- */
- metadata_ptr[0]= field_length % 8;
- metadata_ptr[1]= field_length / 8;
- DBUG_RETURN(2);
-}
-
-
-/**
Returns the number of bytes field uses in row-based replication
row packed size.
@@ -10223,7 +9964,7 @@ int Field_bit::save_field_metadata(uchar *metadata_ptr)
@returns The size of the field based on the field metadata.
*/
-uint Field_bit::pack_length_from_metadata(uint field_metadata)
+uint Field_bit::pack_length_from_metadata(uint field_metadata) const
{
uint const from_len= (field_metadata >> 8U) & 0x00ff;
uint const from_bit_len= field_metadata & 0x00ff;
@@ -10234,9 +9975,9 @@ uint Field_bit::pack_length_from_metadata(uint field_metadata)
bool
Field_bit::compatible_field_size(uint field_metadata,
- Relay_log_info * __attribute__((unused)),
+ const Relay_log_info * __attribute__((unused)),
uint16 mflags,
- int *order_var)
+ int *order_var) const
{
DBUG_ENTER("Field_bit::compatible_field_size");
DBUG_ASSERT((field_metadata >> 16) == 0);
@@ -10466,31 +10207,31 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
{
DBUG_ENTER("Column_definition::create_interval_from_interval_list");
DBUG_ASSERT(!interval);
- if (!(interval= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
+ TYPELIB *tmpint;
+ if (!(interval= tmpint= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB))))
DBUG_RETURN(true); // EOM
List_iterator<String> it(interval_list);
StringBuffer<64> conv;
char comma_buf[5]; /* 5 bytes for 'filename' charset */
DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen);
- int comma_length= charset->cset->wc_mb(charset, ',',
- (uchar*) comma_buf,
- (uchar*) comma_buf +
- sizeof(comma_buf));
+ int comma_length= charset->wc_mb(',',
+ (uchar*) comma_buf,
+ (uchar*) comma_buf + sizeof(comma_buf));
DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf));
if (!multi_alloc_root(mem_root,
- &interval->type_names,
+ &tmpint->type_names,
sizeof(char*) * (interval_list.elements + 1),
- &interval->type_lengths,
+ &tmpint->type_lengths,
sizeof(uint) * (interval_list.elements + 1),
NullS))
goto err; // EOM
- interval->name= "";
- interval->count= interval_list.elements;
+ tmpint->name= "";
+ tmpint->count= interval_list.elements;
- for (uint i= 0; i < interval->count; i++)
+ for (uint i= 0; i < interval_list.elements; i++)
{
uint32 dummy;
String *tmp= it++;
@@ -10515,24 +10256,24 @@ bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root,
goto err; // EOM
// Strip trailing spaces.
- value.length= charset->cset->lengthsp(charset, value.str, value.length);
+ value.length= charset->lengthsp(value.str, value.length);
((char*) value.str)[value.length]= '\0';
if (real_field_type() == MYSQL_TYPE_SET)
{
- if (charset->coll->instr(charset, value.str, value.length,
- comma_buf, comma_length, NULL, 0))
+ if (charset->instr(value.str, value.length,
+ comma_buf, comma_length, NULL, 0))
{
ErrConvString err(tmp);
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr());
goto err;
}
}
- interval->type_names[i]= value.str;
- interval->type_lengths[i]= (uint)value.length;
+ tmpint->type_names[i]= value.str;
+ tmpint->type_lengths[i]= (uint)value.length;
}
- interval->type_names[interval->count]= 0; // End marker
- interval->type_lengths[interval->count]= 0;
+ tmpint->type_names[interval_list.elements]= 0; // End marker
+ tmpint->type_lengths[interval_list.elements]= 0;
interval_list.empty(); // Don't need interval_list anymore
DBUG_RETURN(false);
err:
@@ -10596,30 +10337,26 @@ bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root,
}
-void Column_definition::set_attributes(const Lex_field_type_st &type,
- CHARSET_INFO *cs)
+bool Column_definition::set_attributes(THD *thd,
+ const Lex_field_type_st &def,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
{
DBUG_ASSERT(type_handler() == &type_handler_null);
DBUG_ASSERT(charset == &my_charset_bin || charset == NULL);
DBUG_ASSERT(length == 0);
DBUG_ASSERT(decimals == 0);
- set_handler(type.type_handler());
- charset= cs;
+ set_handler(def.type_handler());
+ return type_handler()->Column_definition_set_attributes(thd, this,
+ def, cs, type);
+}
-#if MYSQL_VERSION_ID > 100500
-#error When merging to 10.5, please move the code below to
-#error Type_handler_timestamp_common::Column_definition_set_attributes()
-#else
- /*
- Unlike other types TIMESTAMP fields are NOT NULL by default.
- Unless --explicit-defaults-for-timestamp is given.
- */
- if (!opt_explicit_defaults_for_timestamp &&
- type.type_handler()->field_type() == MYSQL_TYPE_TIMESTAMP)
- flags|= NOT_NULL_FLAG;
-#endif
+void
+Column_definition_attributes::set_length_and_dec(const Lex_length_and_dec_st
+ &type)
+{
if (type.length())
{
int err;
@@ -10637,13 +10374,11 @@ void Column_definition::create_length_to_internal_length_bit()
{
if (f_bit_as_char(pack_flag))
{
- key_length= pack_length= ((length + 7) & ~7) / 8;
+ pack_length= ((length + 7) & ~7) / 8;
}
else
{
pack_length= (uint) length / 8;
- /* We need one extra byte to store the bits we save among the null bits */
- key_length= pack_length + MY_TEST(length & 7);
}
}
@@ -10652,16 +10387,17 @@ void Column_definition::create_length_to_internal_length_newdecimal()
{
DBUG_ASSERT(length < UINT_MAX32);
uint prec= get_decimal_precision((uint)length, decimals, flags & UNSIGNED_FLAG);
- key_length= pack_length= my_decimal_get_binary_size(prec, decimals);
+ pack_length= my_decimal_get_binary_size(prec, decimals);
}
-bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
- enum_vcol_info_type type)
+bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
+ enum_vcol_info_type type, Alter_info *alter_info)
{
bool ret;
Item::vcol_func_processor_result res;
+ res.alter_info= alter_info;
if (!vcol->name.length)
vcol->name= *name;
@@ -10670,7 +10406,6 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
Walk through the Item tree checking if all items are valid
to be part of the virtual column
*/
- res.errors= 0;
ret= vcol->expr->walk(&Item::check_vcol_func_processor, 0, &res);
vcol->flags= res.errors;
@@ -10781,6 +10516,7 @@ bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length)
MAX_DATETIME_PRECISION);
return true;
}
+ decimals= (uint) length;
length+= int_part_length + (length ? 1 : 0);
return false;
}
@@ -10986,15 +10722,27 @@ bool Field_vers_trx_id::test_if_equality_guarantees_uniqueness(const Item* item)
Column_definition_attributes::Column_definition_attributes(const Field *field)
:length(field->character_octet_length() / field->charset()->mbmaxlen),
+ decimals(field->decimals()),
unireg_check(field->unireg_check),
interval(NULL),
charset(field->charset()), // May be NULL ptr
srid(0),
- geom_type(Field::GEOM_GEOMETRY),
pack_flag(0)
{}
+Column_definition_attributes::
+ Column_definition_attributes(const Type_all_attributes &attr)
+ :length(attr.max_length),
+ decimals(attr.decimals),
+ unireg_check(Field::NONE),
+ interval(attr.get_typelib()),
+ charset(attr.collation.collation),
+ srid(0),
+ pack_flag(attr.unsigned_flag ? 0 : FIELDFLAG_DECIMAL)
+{}
+
+
/** Create a field suitable for create of table. */
Column_definition::Column_definition(THD *thd, Field *old_field,
@@ -11005,10 +10753,8 @@ Column_definition::Column_definition(THD *thd, Field *old_field,
field_name= old_field->field_name;
flags= old_field->flags;
pack_length=old_field->pack_length();
- key_length= old_field->key_length();
set_handler(old_field->type_handler());
comment= old_field->comment;
- decimals= old_field->decimals();
vcol_info= old_field->vcol_info;
option_list= old_field->option_list;
compression_method_ptr= 0;
@@ -11090,7 +10836,6 @@ Column_definition::redefine_stage1_common(const Column_definition *dup_field,
schema->default_table_charset;
length= dup_field->char_length;
pack_length= dup_field->pack_length;
- key_length= dup_field->key_length;
decimals= dup_field->decimals;
unireg_check= dup_field->unireg_check;
flags= dup_field->flags;
@@ -11222,6 +10967,19 @@ Column_definition::set_compressed_deprecated_column_attribute(THD *thd,
}
+bool Column_definition::check_vcol_for_key(THD *thd) const
+{
+ if (vcol_info && (vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC))
+ {
+ /* use check_expression() to report an error */
+ check_expression(vcol_info, &field_name, VCOL_GENERATED_STORED);
+ DBUG_ASSERT(thd->is_error());
+ return true;
+ }
+ return false;
+}
+
+
Send_field::Send_field(THD *thd, Item *item)
{
item->make_send_field(thd, this);
@@ -11241,11 +10999,11 @@ uint32 Field_blob::max_display_length() const
switch (packlength)
{
case 1:
- return 255 * field_charset->mbmaxlen;
+ return 255 * mbmaxlen();
case 2:
- return 65535 * field_charset->mbmaxlen;
+ return 65535 * mbmaxlen();
case 3:
- return 16777215 * field_charset->mbmaxlen;
+ return 16777215 * mbmaxlen();
case 4:
return (uint32) UINT_MAX32;
default:
@@ -11489,8 +11247,7 @@ bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
StringBuffer<MAX_FIELD_WIDTH> str;
bool rc= false;
THD *thd= get_thd();
- sql_mode_t sql_mode_backup= thd->variables.sql_mode;
- thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+ Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
val_str(&str);
if (!(to->length= str.length()))
@@ -11498,7 +11255,6 @@ bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to)
else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length()))))
to->length= 0;
- thd->variables.sql_mode= sql_mode_backup;
return rc;
}
@@ -11516,7 +11272,7 @@ void Field_string::print_key_value(String *out, uint32 length)
{
if (charset() == &my_charset_bin)
{
- size_t len= field_charset->cset->lengthsp(field_charset, (const char*) ptr, length);
+ size_t len= field_charset()->lengthsp((const char*) ptr, length);
print_key_value_binary(out, ptr, static_cast<uint32>(len));
}
else
diff --git a/sql/field.h b/sql/field.h
index b3bc2d4dbea..be4d279ce61 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -37,6 +37,7 @@
class Send_field;
class Copy_field;
class Protocol;
+class Protocol_text;
class Create_field;
class Relay_log_info;
class Field;
@@ -51,6 +52,8 @@ class Table_ident;
class SEL_ARG;
class RANGE_OPT_PARAM;
struct KEY_PART;
+struct SORT_FIELD;
+struct SORT_FIELD_ATTR;
enum enum_check_fields
{
@@ -60,6 +63,45 @@ enum enum_check_fields
CHECK_FIELD_ERROR_FOR_NULL,
};
+
+enum enum_conv_type
+{
+ CONV_TYPE_PRECISE,
+ CONV_TYPE_VARIANT,
+ CONV_TYPE_SUBSET_TO_SUPERSET,
+ CONV_TYPE_SUPERSET_TO_SUBSET,
+ CONV_TYPE_IMPOSSIBLE
+};
+
+
+class Conv_param
+{
+ uint16 m_table_def_flags;
+public:
+ Conv_param(uint16 table_def_flags)
+ :m_table_def_flags(table_def_flags)
+ { }
+ uint16 table_def_flags() const { return m_table_def_flags; }
+};
+
+
+class Conv_source: public Type_handler_hybrid_field_type
+{
+ uint16 m_metadata;
+ CHARSET_INFO *m_cs;
+public:
+ Conv_source(const Type_handler *h, uint16 metadata, CHARSET_INFO *cs)
+ :Type_handler_hybrid_field_type(h),
+ m_metadata(metadata),
+ m_cs(cs)
+ {
+ DBUG_ASSERT(cs);
+ }
+ uint16 metadata() const { return m_metadata; }
+ uint mbmaxlen() const { return m_cs->mbmaxlen; }
+};
+
+
/*
Common declarations for Field and Item
*/
@@ -165,7 +207,7 @@ protected:
public:
Converter_strntod(CHARSET_INFO *cs, const char *str, size_t length)
{
- m_result= my_strntod(cs, (char *) str, length, &m_end_of_num, &m_error);
+ m_result= cs->strntod((char *) str, length, &m_end_of_num, &m_error);
// strntod() does not set an error if the input string was empty
m_edom= m_error !=0 || str == m_end_of_num;
}
@@ -185,7 +227,7 @@ protected:
public:
Converter_strntoll(CHARSET_INFO *cs, const char *str, size_t length)
{
- m_result= my_strntoll(cs, str, length, 10, &m_end_of_num, &m_error);
+ m_result= cs->strntoll(str, length, 10, &m_end_of_num, &m_error);
/*
All non-zero errors means EDOM error.
strntoll() does not set an error if the input string was empty.
@@ -202,7 +244,7 @@ protected:
Converter_strtoll10(CHARSET_INFO *cs, const char *str, size_t length)
{
m_end_of_num= (char *) str + length;
- m_result= (*(cs->cset->strtoll10))(cs, str, &m_end_of_num, &m_error);
+ m_result= cs->strtoll10(str, &m_end_of_num, &m_error);
/*
Negative error means "good negative number".
Only a positive m_error value means a real error.
@@ -410,7 +452,7 @@ public:
{ // Use this when an item is [a part of] a boolean expression
public:
Context_boolean()
- :Context(ANY_SUBST, &type_handler_longlong, &my_charset_bin) { }
+ :Context(ANY_SUBST, &type_handler_slonglong, &my_charset_bin) { }
};
};
@@ -510,6 +552,7 @@ static inline const char *vcol_type_name(enum_vcol_info_type type)
#define VCOL_AUTO_INC 16
#define VCOL_IMPOSSIBLE 32
#define VCOL_NOT_VIRTUAL 64 /* Function can't be virtual */
+#define VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS 128
#define VCOL_NOT_STRICTLY_DETERMINISTIC \
(VCOL_NON_DETERMINISTIC | VCOL_TIME_FUNC | VCOL_SESSION_FUNC)
@@ -596,6 +639,101 @@ public:
inline void print(String*);
};
+class Binlog_type_info
+{
+public:
+ enum binlog_sign_t
+ {
+ SIGN_SIGNED,
+ SIGN_UNSIGNED,
+ SIGN_NOT_APPLICABLE // for non-numeric types
+ };
+ uchar m_type_code; // according to Field::binlog_type()
+ /**
+ Retrieve the field metadata for fields.
+ */
+ uint16 m_metadata;
+ uint8 m_metadata_size;
+ binlog_sign_t m_signedness;
+ CHARSET_INFO *m_cs; // NULL if not relevant
+ TYPELIB *m_enum_typelib; // NULL if not relevant
+ TYPELIB *m_set_typelib; // NULL if not relevant
+ uchar m_geom_type; // Non-geometry fields can return 0
+ Binlog_type_info(uchar type_code,
+ uint16 metadata,
+ uint8 metadata_size)
+ :m_type_code(type_code),
+ m_metadata(metadata),
+ m_metadata_size(metadata_size),
+ m_signedness(SIGN_NOT_APPLICABLE),
+ m_cs(NULL),
+ m_enum_typelib(NULL),
+ m_set_typelib(NULL),
+ m_geom_type(0)
+ {};
+ Binlog_type_info(uchar type_code, uint16 metadata,
+ uint8 metadata_size,
+ binlog_sign_t signedness)
+ :m_type_code(type_code),
+ m_metadata(metadata),
+ m_metadata_size(metadata_size),
+ m_signedness(signedness),
+ m_cs(NULL),
+ m_enum_typelib(NULL),
+ m_set_typelib(NULL),
+ m_geom_type(0)
+ {};
+ Binlog_type_info(uchar type_code, uint16 metadata,
+ uint8 metadata_size,
+ CHARSET_INFO *cs)
+ :m_type_code(type_code),
+ m_metadata(metadata),
+ m_metadata_size(metadata_size),
+ m_signedness(SIGN_NOT_APPLICABLE),
+ m_cs(cs),
+ m_enum_typelib(NULL),
+ m_set_typelib(NULL),
+ m_geom_type(0)
+ {};
+ Binlog_type_info(uchar type_code, uint16 metadata,
+ uint8 metadata_size,
+ CHARSET_INFO *cs,
+ TYPELIB *t_enum, TYPELIB *t_set)
+ :m_type_code(type_code),
+ m_metadata(metadata),
+ m_metadata_size(metadata_size),
+ m_signedness(SIGN_NOT_APPLICABLE),
+ m_cs(cs),
+ m_enum_typelib(t_enum),
+ m_set_typelib(t_set),
+ m_geom_type(0)
+ {};
+ Binlog_type_info(uchar type_code, uint16 metadata,
+ uint8 metadata_size, CHARSET_INFO *cs,
+ uchar geom_type)
+ :m_type_code(type_code),
+ m_metadata(metadata),
+ m_metadata_size(metadata_size),
+ m_signedness(SIGN_NOT_APPLICABLE),
+ m_cs(cs),
+ m_enum_typelib(NULL),
+ m_set_typelib(NULL),
+ m_geom_type(geom_type)
+ {};
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+};
+
+
+class Binlog_type_info_fixed_string: public Binlog_type_info
+{
+public:
+ Binlog_type_info_fixed_string(uchar type_code,
+ uint32 octet_length,
+ CHARSET_INFO *cs);
+};
+
+
class Field: public Value_source
{
Field(const Item &); /* Prevent use of these */
@@ -609,6 +747,7 @@ protected:
}
void error_generated_column_function_is_not_allowed(THD *thd, bool error)
const;
+ static void do_field_eq(Copy_field *copy);
static void do_field_int(Copy_field *copy);
static void do_field_real(Copy_field *copy);
static void do_field_string(Copy_field *copy);
@@ -686,16 +825,10 @@ public:
TIMESTAMP_DNUN_FIELD=23, // TIMESTAMP DEFAULT NOW() ON UPDATE NOW()
TMYSQL_COMPRESSED= 24, // Compatibility with TMySQL
};
- enum geometry_type
- {
- GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3,
- GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6,
- GEOM_GEOMETRYCOLLECTION = 7
- };
enum imagetype { itRAW, itMBR};
utype unireg_check;
- const uint32 field_length; // Length of field
+ uint32 field_length; // Length of field
uint32 flags;
uint16 field_index; // field number in fields array
uchar null_bit; // Bit used to test null bit
@@ -749,15 +882,13 @@ public:
const LEX_CSTRING *field_name_arg);
virtual ~Field() {}
- DTCollation dtcollation() const
+ virtual Type_numeric_attributes type_numeric_attributes() const
{
- return DTCollation(charset(), derivation(), repertoire());
+ return Type_numeric_attributes(field_length, decimals(), is_unsigned());
}
- virtual Type_std_attributes type_std_attributes() const
+ Type_std_attributes type_std_attributes() const
{
- return Type_std_attributes(field_length, decimals(),
- MY_TEST(flags & UNSIGNED_FLAG),
- dtcollation());
+ return Type_std_attributes(type_numeric_attributes(), dtcollation());
}
bool is_unsigned() const { return flags & UNSIGNED_FLAG; }
@@ -768,6 +899,10 @@ public:
*/
typedef void Copy_func(Copy_field*);
virtual Copy_func *get_copy_func(const Field *from) const= 0;
+ virtual Copy_func *get_copy_func_to(const Field *to) const
+ {
+ return to->get_copy_func(this);
+ }
/* Store functions returns 1 on overflow and -1 on fatal error */
virtual int store_field(Field *from) { return from->save_in_field(this); }
virtual int save_in_field(Field *to)= 0;
@@ -786,6 +921,23 @@ public:
reset();
}
virtual int store(const char *to, size_t length,CHARSET_INFO *cs)=0;
+ /*
+ This is used by engines like CSV and Federated to signal the field
+ that the data is going to be in text (rather than binary) representation,
+ even if cs points to &my_charset_bin.
+
+ If a Field distinguishes between text and binary formats (e.g. INET6),
+ we cannot call store(str,length,&my_charset_bin),
+ to avoid "field" mis-interpreting the data format as binary.
+ */
+ virtual int store_text(const char *to, size_t length, CHARSET_INFO *cs)
+ {
+ return store(to, length, cs);
+ }
+ virtual int store_binary(const char *to, size_t length)
+ {
+ return store(to, length, &my_charset_bin);
+ }
virtual int store_hex_hybrid(const char *str, size_t length);
virtual int store(double nr)=0;
virtual int store(longlong nr, bool unsigned_val)=0;
@@ -810,6 +962,8 @@ public:
{ return store_time_dec(ltime, TIME_SECOND_PART_DIGITS); }
int store(const char *to, size_t length, CHARSET_INFO *cs,
enum_check_fields check_level);
+ int store_text(const char *to, size_t length, CHARSET_INFO *cs,
+ enum_check_fields check_level);
int store(const LEX_STRING *ls, CHARSET_INFO *cs)
{
DBUG_ASSERT(ls->length < UINT_MAX32);
@@ -826,6 +980,24 @@ public:
return store(ls.str, (uint) ls.length, cs);
}
+ /*
+ @brief
+ Store minimum/maximum value of a column in the statistics table.
+ @param
+ field statistical table field
+ str value buffer
+ */
+ virtual int store_to_statistical_minmax_field(Field *field, String *str);
+
+ /*
+ @brief
+ Store minimum/maximum value of a column from the statistical table.
+ @param
+ field statistical table field
+ str value buffer
+ */
+ virtual int store_from_statistical_minmax_field(Field *field, String *str);
+
#ifdef HAVE_valgrind
/**
Mark unused memory in the field as defined. Mainly used to ensure
@@ -838,8 +1010,8 @@ public:
void mark_unused_memory_as_defined() {}
#endif
- virtual double val_real(void)=0;
- virtual longlong val_int(void)=0;
+ virtual double val_real()=0;
+ virtual longlong val_int()=0;
/*
Get ulonglong representation.
Negative values are truncated to 0.
@@ -849,7 +1021,7 @@ public:
longlong nr= val_int();
return nr < 0 ? 0 : (ulonglong) nr;
}
- virtual bool val_bool(void)= 0;
+ virtual bool val_bool()= 0;
virtual my_decimal *val_decimal(my_decimal *)=0;
inline String *val_str(String *str) { return val_str(str, str); }
/*
@@ -891,7 +1063,7 @@ public:
str_needs_quotes() returns TRUE if the value returned by val_str() needs
to be quoted when used in constructing an SQL query.
*/
- virtual bool str_needs_quotes() { return FALSE; }
+ virtual bool str_needs_quotes() const { return false; }
const Type_handler *type_handler_for_comparison() const
{
return type_handler()->type_handler_for_comparison();
@@ -924,36 +1096,27 @@ public:
table, which is located on disk).
*/
virtual uint32 pack_length_in_rec() const { return pack_length(); }
- virtual bool compatible_field_size(uint metadata, Relay_log_info *rli,
- uint16 mflags, int *order);
- virtual uint pack_length_from_metadata(uint field_metadata)
+ virtual bool compatible_field_size(uint metadata, const Relay_log_info *rli,
+ uint16 mflags, int *order) const;
+ virtual uint pack_length_from_metadata(uint field_metadata) const
{
DBUG_ENTER("Field::pack_length_from_metadata");
DBUG_RETURN(field_metadata);
}
virtual uint row_pack_length() const { return 0; }
-
- /**
- Retrieve the field metadata for fields.
-
- This default implementation returns 0 and saves 0 in the first_byte value.
-
- @param first_byte First byte of field metadata
-
- @returns 0 no bytes written.
- */
-
- virtual int save_field_metadata(uchar *first_byte)
- { return 0; }
-
-
/*
data_length() return the "real size" of the data in memory.
*/
virtual uint32 data_length() { return pack_length(); }
virtual uint32 sort_length() const { return pack_length(); }
+ /*
+ sort_suffix_length() return the length bytes needed to store the length
+ for binary charset
+ */
+ virtual uint32 sort_suffix_length() const { return 0; }
+
/*
Get the number bytes occupied by the value in the field.
CHAR values are stripped of trailing spaces.
@@ -984,7 +1147,7 @@ public:
return pack_length();
};
- virtual int reset(void) { bzero(ptr,pack_length()); return 0; }
+ virtual int reset() { bzero(ptr,pack_length()); return 0; }
virtual void reset_fields() {}
const uchar *ptr_in_record(const uchar *record) const
{
@@ -1030,6 +1193,8 @@ public:
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 uint16 key_part_flag() const { return 0; }
+ virtual uint16 key_part_length_bytes() const { return 0; }
virtual uint32 key_length() const { return pack_length(); }
virtual const Type_handler *type_handler() const= 0;
virtual enum_field_types type() const
@@ -1088,23 +1253,44 @@ public:
*/
return type();
}
- inline int cmp(const uchar *str) { return cmp(ptr,str); }
- virtual int cmp(const uchar *,const uchar *)=0;
+ virtual Binlog_type_info binlog_type_info() const
+ {
+ DBUG_ASSERT(Field::type() == binlog_type());
+ return Binlog_type_info(Field::type(), 0, 0);
+ }
+ virtual en_fieldtype tmp_engine_column_type(bool use_packed_rows) const
+ {
+ return FIELD_NORMAL;
+ }
+ /*
+ Conversion type for from the source to the current field.
+ */
+ virtual enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param)
+ const= 0;
+ enum_conv_type rpl_conv_type_from_same_data_type(uint16 metadata,
+ const Relay_log_info *rli,
+ const Conv_param &param)
+ const;
+ inline int cmp(const uchar *str) const { return cmp(ptr,str); }
/*
The following method is used for comparing prefix keys.
Currently it's only used in partitioning.
*/
- virtual int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len)
+ virtual int cmp_prefix(const uchar *a, const uchar *b,
+ size_t prefix_len) const
{ return cmp(a, b); }
- virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U)
+ virtual int cmp(const uchar *,const uchar *) const=0;
+ virtual int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U) const
{ return memcmp(a,b,pack_length()); }
virtual int cmp_offset(my_ptrdiff_t row_offset)
{ return cmp(ptr,ptr+row_offset); }
virtual int cmp_binary_offset(uint row_offset)
{ return cmp_binary(ptr, ptr+row_offset); };
- virtual int key_cmp(const uchar *a,const uchar *b)
+ virtual int key_cmp(const uchar *a,const uchar *b) const
{ return cmp(a, b); }
- virtual int key_cmp(const uchar *str, uint length)
+ virtual int key_cmp(const uchar *str, uint length) const
{ return cmp(ptr,str); }
/*
Update the value m of the 'min_val' field with the current value v
@@ -1151,6 +1337,8 @@ public:
{
return Information_schema_character_attributes();
}
+ virtual void update_data_type_statistics(Data_type_statistics *st) const
+ { }
/*
Caller beware: sql_type can change str.Ptr, so check
ptr() to see if it changed if you are using your own buffer
@@ -1202,7 +1390,7 @@ public:
void load_data_set_value(const char *pos, uint length, CHARSET_INFO *cs);
/* @return true if this field is NULL-able (even if temporarily) */
- inline bool real_maybe_null(void) const { return null_ptr != 0; }
+ inline bool real_maybe_null() const { return null_ptr != 0; }
uint null_offset(const uchar *record) const
{ return (uint) (null_ptr - record); }
/*
@@ -1220,7 +1408,7 @@ public:
void set_null_ptr(uchar *p_null_ptr, uint p_null_bit)
{
null_ptr= p_null_ptr;
- null_bit= p_null_bit;
+ null_bit= static_cast<uchar>(p_null_bit);
}
bool stored_in_db() const { return !vcol_info || vcol_info->stored_in_db; }
@@ -1269,7 +1457,18 @@ public:
return bytes;
}
- void make_sort_key(uchar *buff, uint length);
+ /*
+ Create mem-comparable sort key part for a sort key
+ */
+ void make_sort_key_part(uchar *buff, uint length);
+
+ /*
+ create a compact sort key which can be compared with a comparison
+ function. They are called packed sort keys
+ */
+ virtual uint make_packed_sort_key_part(uchar *buff,
+ const SORT_FIELD_ATTR *sort_field);
+
virtual void make_send_field(Send_field *);
/*
@@ -1310,8 +1509,11 @@ public:
if (null_ptr)
null_ptr=ADD_TO_PTR(null_ptr,ptr_diff,uchar*);
}
- virtual void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
- { memcpy(buff,ptr,length); }
+ void get_image(uchar *buff, uint length, CHARSET_INFO *cs) const
+ { get_image(buff, length, ptr, cs); }
+ virtual void get_image(uchar *buff, uint length,
+ const uchar *ptr_arg, CHARSET_INFO *cs) const
+ { memcpy(buff,ptr_arg,length); }
virtual void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)
{ memcpy(ptr,buff,length); }
@@ -1342,9 +1544,11 @@ public:
Number of copied bytes (excluding padded zero bytes -- see above).
*/
- virtual uint get_key_image(uchar *buff, uint length, imagetype type_arg)
+ uint get_key_image(uchar *buff, uint length, imagetype type_arg) const
+ { return get_key_image(buff, length, ptr, type_arg); }
+ virtual uint get_key_image(uchar *buff, uint length, const uchar *ptr_arg, imagetype type_arg) const
{
- get_image(buff, length, &my_charset_bin);
+ get_image(buff, length, ptr_arg, &my_charset_bin);
return length;
}
virtual void set_key_image(const uchar *buff,uint length)
@@ -1373,7 +1577,7 @@ public:
ptr= old_ptr;
return str;
}
- virtual bool send_binary(Protocol *protocol);
+ virtual bool send(Protocol *protocol);
virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
/**
@@ -1393,6 +1597,7 @@ public:
{ return length;}
virtual uint max_packed_col_length(uint max_length)
{ return max_length;}
+ virtual bool is_packable() const { return false; }
uint offset(const uchar *record) const
{
@@ -1403,15 +1608,13 @@ public:
virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
virtual longlong val_datetime_packed(THD *thd);
virtual longlong val_time_packed(THD *thd);
- virtual TYPELIB *get_typelib() const { return NULL; }
- virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
+ virtual const TYPELIB *get_typelib() const { return NULL; }
+ virtual CHARSET_INFO *charset() const= 0;
+ virtual const DTCollation &dtcollation() const= 0;
virtual CHARSET_INFO *charset_for_protocol(void) const
{ return binary() ? &my_charset_bin : charset(); }
virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
virtual bool has_charset(void) const { return FALSE; }
- virtual enum Derivation derivation(void) const
- { return DERIVATION_IMPLICIT; }
- virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; }
virtual int set_time() { return 1; }
bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
int cuted_increment, ulong current_row=0) const;
@@ -1564,12 +1767,6 @@ public:
{
return field_length / charset()->mbmaxlen;
}
- virtual geometry_type get_geometry_type() const
- {
- /* shouldn't get here. */
- DBUG_ASSERT(0);
- return GEOM_GEOMETRY;
- }
ha_storage_media field_storage_type() const
{
@@ -1806,6 +2003,14 @@ protected:
void prepend_zeros(String *value) const;
Item *get_equal_zerofill_const_item(THD *thd, const Context &ctx,
Item *const_item);
+ Binlog_type_info::binlog_sign_t binlog_signedness() const
+ {
+ return (flags & UNSIGNED_FLAG) ? Binlog_type_info::SIGN_UNSIGNED :
+ Binlog_type_info::SIGN_SIGNED;
+ }
+ bool send_numeric_zerofill_str(Protocol_text *protocol,
+ protocol_send_type_t send_type);
+
public:
const uint8 dec;
bool zerofill,unsigned_flag; // Purify cannot handle bit fields
@@ -1813,11 +2018,17 @@ public:
uchar null_bit_arg, utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
- 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; }
- sql_mode_t can_handle_sql_mode_dependency_on_store() const;
+ CHARSET_INFO *charset() const override
+ {
+ return DTCollation_numeric::singleton().collation;
+ }
+ const DTCollation &dtcollation() const override
+ {
+ return DTCollation_numeric::singleton();
+ }
+ sql_mode_t can_handle_sql_mode_dependency_on_store() const override;
Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override
{
return (flags & ZEROFILL_FLAG) ?
get_equal_zerofill_const_item(thd, ctx, const_item) :
@@ -1825,94 +2036,111 @@ public:
}
void add_zerofill_and_unsigned(String &res) const;
friend class Create_field;
- void make_send_field(Send_field *);
- uint decimals() const { return (uint) dec; }
- uint size_of() const { return sizeof(*this); }
- bool eq_def(const Field *field) const;
- Copy_func *get_copy_func(const Field *from) const
+ void make_send_field(Send_field *) override;
+ uint decimals() const override { return (uint) dec; }
+ uint size_of() const override { return sizeof(*this); }
+ bool eq_def(const Field *field) const override;
+ Copy_func *get_copy_func(const Field *from) const override
{
if (unsigned_flag && from->cmp_type() == DECIMAL_RESULT)
return do_field_decimal;
return do_field_int;
}
- int save_in_field(Field *to)
+ int save_in_field(Field *to) override
{
return to->store(val_int(), MY_TEST(flags & UNSIGNED_FLAG));
}
- bool is_equal(const Column_definition &new_field) const;
- uint row_pack_length() const { return pack_length(); }
- uint32 pack_length_from_metadata(uint field_metadata) {
+ bool is_equal(const Column_definition &new_field) const override;
+ uint row_pack_length() const override { return pack_length(); }
+ uint32 pack_length_from_metadata(uint field_metadata) const override
+ {
uint32 length= pack_length();
DBUG_PRINT("result", ("pack_length_from_metadata(%d): %u",
field_metadata, length));
return length;
}
- double pos_in_interval(Field *min, Field *max)
+ double pos_in_interval(Field *min, Field *max) override
{
return pos_in_interval_val_real(min, max);
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
- scalar_comparison_op op, Item *value);
+ scalar_comparison_op op, Item *value) override;
+ Binlog_type_info binlog_type_info() const override
+ {
+ DBUG_ASSERT(Field_num::type() == binlog_type());
+ return Binlog_type_info(Field_num::type(), 0, 0, binlog_signedness());
+ }
};
class Field_str :public Field {
protected:
- // TODO-10.2: Reuse DTCollation instead of these three members
- CHARSET_INFO *field_charset;
- enum Derivation field_derivation;
- uint field_repertoire;
+ DTCollation m_collation;
+ // A short alias for m_collation.collation with non-virtual linkage
+ const CHARSET_INFO *field_charset() const { return m_collation.collation; }
+ uint mbmaxlen() const { return m_collation.collation->mbmaxlen; }
public:
bool can_be_substituted_to_equal_item(const Context &ctx,
- const Item_equal *item_equal);
+ const Item_equal *item_equal) override;
Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
const DTCollation &collation);
- uint decimals() const { return NOT_FIXED_DEC; }
- int save_in_field(Field *to) { return save_in_field_str(to); }
- bool memcpy_field_possible(const Field *from) const
+ uint decimals() const override { return NOT_FIXED_DEC; }
+ int save_in_field(Field *to) override { return save_in_field_str(to); }
+ bool memcpy_field_possible(const Field *from) const override
{
return real_type() == from->real_type() &&
pack_length() == from->pack_length() &&
charset() == from->charset();
}
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_decimal(const my_decimal *);
- int store(const char *to,size_t length,CHARSET_INFO *cs)=0;
- int store_hex_hybrid(const char *str, size_t length)
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_decimal(const my_decimal *) override;
+ int store(const char *to,size_t length,CHARSET_INFO *cs) override=0;
+ int store_hex_hybrid(const char *str, size_t length) override
{
return store(str, length, &my_charset_bin);
}
- uint repertoire(void) const { return field_repertoire; }
- CHARSET_INFO *charset(void) const { return field_charset; }
- enum Derivation derivation(void) const { return field_derivation; }
- bool binary() const { return field_charset == &my_charset_bin; }
- uint32 max_display_length() const { return field_length; }
- uint32 character_octet_length() const { return field_length; }
- uint32 char_length() const { return field_length / field_charset->mbmaxlen; }
+ CHARSET_INFO *charset() const override { return m_collation.collation; }
+ const DTCollation &dtcollation() const override
+ {
+ return m_collation;
+ }
+ bool binary() const override { return field_charset() == &my_charset_bin; }
+ uint32 max_display_length() const override { return field_length; }
+ uint32 character_octet_length() const override { return field_length; }
+ uint32 char_length() const override
+ {
+ return field_length / mbmaxlen();
+ }
Information_schema_character_attributes
- information_schema_character_attributes() const
+ information_schema_character_attributes() const override
{
return Information_schema_character_attributes(max_display_length(),
char_length());
}
friend class Create_field;
- my_decimal *val_decimal(my_decimal *);
- bool val_bool() { return val_real() != 0e0; }
- virtual bool str_needs_quotes() { return TRUE; }
- bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); }
+ my_decimal *val_decimal(my_decimal *) override;
+ bool val_bool() override { return val_real() != 0e0; }
+ bool str_needs_quotes() const override { return true; }
+ bool eq_cmp_as_binary() override { return MY_TEST(flags & BINARY_FLAG); }
virtual uint length_size() const { return 0; }
- double pos_in_interval(Field *min, Field *max)
+ double pos_in_interval(Field *min, Field *max) override
{
return pos_in_interval_val_str(min, max, length_size());
}
- bool test_if_equality_guarantees_uniqueness(const Item *const_item) const;
+ bool test_if_equality_guarantees_uniqueness(const Item *const_item) const
+ override;
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
- scalar_comparison_op op, Item *value);
+ scalar_comparison_op op, Item *value) override;
+ Binlog_type_info binlog_type_info() const override
+ {
+ DBUG_ASSERT(Field_str::type() == binlog_type());
+ return Binlog_type_info(Field_str::type(), 0, 0, charset());
+ }
};
/* base class for Field_string, Field_varstring and Field_blob */
@@ -1941,7 +2169,7 @@ protected:
{
String_copier copier;
- *copy_length= copier.well_formed_copy(field_charset, to, to_length,
+ *copy_length= copier.well_formed_copy(field_charset(), to, to_length,
from_cs, from, from_length,
nchars);
@@ -1957,7 +2185,7 @@ protected:
uint *out_length,
CHARSET_INFO *cs, size_t nchars);
String *uncompress(String *val_buffer, String *val_ptr,
- const uchar *from, uint from_length);
+ const uchar *from, uint from_length) const;
public:
Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -1966,11 +2194,15 @@ public:
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
field_name_arg, collation)
{}
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ int store_decimal(const my_decimal *d) override;
+ uint32 max_data_length() const override;
+ void make_send_field(Send_field *) override;
+ bool send(Protocol *protocol) override;
- int store_decimal(const my_decimal *d);
- uint32 max_data_length() const;
-
- bool is_varchar_and_in_write_set() const
+ bool is_varchar_and_in_write_set() const override
{
DBUG_ASSERT(table && table->write_set);
return bitmap_is_set(table->write_set, field_index);
@@ -1978,14 +2210,18 @@ public:
bool match_collation_to_optimize_range() const { return true; }
bool can_optimize_keypart_ref(const Item_bool_func *cond,
- const Item *item) const;
+ const Item *item) const override;
bool can_optimize_hash_join(const Item_bool_func *cond,
- const Item *item) const;
+ const Item *item) const override;
bool can_optimize_group_min_max(const Item_bool_func *cond,
- const Item *const_item) const;
+ const Item *const_item) const override;
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
- bool is_eq_func) const;
+ bool is_eq_func) const override;
+ bool is_packable() const override { return true; }
+ uint make_packed_sort_key_part(uchar *buff,
+ const SORT_FIELD_ATTR *sort_field)override;
+ uchar* pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field);
};
/* base class for float and double and decimal (old one) */
@@ -2003,19 +2239,23 @@ public:
field_name_arg, dec_arg, zero_arg, unsigned_arg),
not_fixed(dec_arg >= FLOATING_POINT_DECIMALS)
{}
- Copy_func *get_copy_func(const Field *from) const
+ Copy_func *get_copy_func(const Field *from) const override
{
return do_field_real;
}
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
Information_schema_numeric_attributes
- information_schema_numeric_attributes() const
+ information_schema_numeric_attributes() const override
{
return dec == NOT_FIXED_DEC ?
Information_schema_numeric_attributes(field_length) :
Information_schema_numeric_attributes(field_length, dec);
}
- int save_in_field(Field *to) { return to->store(val_real()); }
- bool memcpy_field_possible(const Field *from) const
+ void sql_type(String &str) const override;
+ int save_in_field(Field *to) override { return to->store(val_real()); }
+ bool memcpy_field_possible(const Field *from) const override
{
/*
Cannot do memcpy from a longer field to a shorter field,
@@ -2028,18 +2268,20 @@ public:
decimals() == from->decimals() &&
field_length >= from->field_length;
}
- int store_decimal(const my_decimal *dec) { return store(dec->to_double()); }
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
- my_decimal *val_decimal(my_decimal *);
- bool val_bool() { return val_real() != 0e0; }
- uint32 max_display_length() const { return field_length; }
- uint size_of() const { return sizeof(*this); }
- Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
+ int store_decimal(const my_decimal *dec) override
+ { return store(dec->to_double()); }
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ bool val_bool() override { return val_real() != 0e0; }
+ uint32 max_display_length() const override { return field_length; }
+ uint size_of() const override { return sizeof *this; }
+ Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override;
};
-class Field_decimal :public Field_real {
+class Field_decimal final :public Field_real {
public:
Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2049,33 +2291,35 @@ public:
unireg_check_arg, field_name_arg,
dec_arg, zero_arg, unsigned_arg)
{}
- Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
- const Type_handler *type_handler() const { return &type_handler_olddecimal; }
- enum ha_base_keytype key_type() const
+ Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type)
+ override;
+ const Type_handler *type_handler() const override
+ { return &type_handler_olddecimal; }
+ enum ha_base_keytype key_type() const override
{ return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
Information_schema_numeric_attributes
- information_schema_numeric_attributes() const
+ information_schema_numeric_attributes() const override
{
uint tmp= dec ? 2 : 1; // The sign and the decimal point
return Information_schema_numeric_attributes(field_length - tmp, dec);
}
- Copy_func *get_copy_func(const Field *from) const
+ Copy_func *get_copy_func(const Field *from) const override
{
return eq_def(from) ? get_identical_copy_func() : do_field_string;
}
- int reset(void);
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
+ int reset() override;
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
void overflow(bool negative);
- bool zero_pack() const { return 0; }
- void sql_type(String &str) const;
- virtual uchar *pack(uchar* to, const uchar *from, uint max_length)
+ bool zero_pack() const override { return false; }
+ void sql_type(String &str) const override;
+ uchar *pack(uchar* to, const uchar *from, uint max_length) override
{
return Field::pack(to, from, max_length);
}
@@ -2083,9 +2327,7 @@ public:
/* New decimal/numeric field which use fixed point arithmetic */
-class Field_new_decimal :public Field_num {
-private:
- int save_field_metadata(uchar *first_byte);
+class Field_new_decimal final :public Field_num {
public:
/* The maximum number of decimal digits can be stored */
uint precision;
@@ -2101,20 +2343,21 @@ public:
enum utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
uint8 dec_arg, bool zero_arg, bool unsigned_arg);
- const Type_handler *type_handler() const { return &type_handler_newdecimal; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- Copy_func *get_copy_func(const Field *from) const
+ const Type_handler *type_handler() const override
+ { return &type_handler_newdecimal; }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; }
+ Copy_func *get_copy_func(const Field *from) const override
{
// if (from->real_type() == MYSQL_TYPE_BIT) // QQ: why?
// return do_field_int;
return do_field_decimal;
}
- int save_in_field(Field *to)
+ int save_in_field(Field *to) override
{
my_decimal tmp(ptr, precision, dec);
return to->store_decimal(&tmp);
}
- bool memcpy_field_possible(const Field *from) const
+ bool memcpy_field_possible(const Field *from) const override
{
return real_type() == from->real_type() &&
pack_length() == from->pack_length() &&
@@ -2122,63 +2365,69 @@ public:
decimals() == from->decimals() &&
field_length == from->field_length;
}
- int reset(void);
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ int reset() override;
bool store_value(const my_decimal *decimal_value);
bool store_value(const my_decimal *decimal_value, int *native_error);
void set_value_on_overflow(my_decimal *decimal_value, bool sign);
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- int store_decimal(const my_decimal *);
- double val_real(void)
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ int store_decimal(const my_decimal *) override;
+ double val_real() override
{
return my_decimal(ptr, precision, dec).to_double();
}
- longlong val_int(void)
+ longlong val_int() override
{
return my_decimal(ptr, precision, dec).to_longlong(unsigned_flag);
}
- ulonglong val_uint(void)
+ ulonglong val_uint() override
{
return (ulonglong) my_decimal(ptr, precision, dec).to_longlong(true);
}
- my_decimal *val_decimal(my_decimal *);
- String *val_str(String *val_buffer, String *val_ptr __attribute__((unused)))
+ my_decimal *val_decimal(my_decimal *) override;
+ String *val_str(String *val_buffer, String *) override
{
uint fixed_precision= zerofill ? precision : 0;
return my_decimal(ptr, precision, dec).
to_string(val_buffer, fixed_precision, dec, '0');
}
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
my_decimal nr(ptr, precision, dec);
return decimal_to_datetime_with_warn(get_thd(), &nr, ltime,
fuzzydate, table->s, field_name.str);
}
- bool val_bool()
+ bool val_bool() override
{
return my_decimal(ptr, precision, dec).to_bool();
}
- int cmp(const uchar *, const uchar *);
- void sort_string(uchar *buff, uint length);
- bool zero_pack() const { return 0; }
- void sql_type(String &str) const;
- uint32 max_display_length() const { return field_length; }
+ int cmp(const uchar *, const uchar *) const override;
+ void sort_string(uchar *buff, uint length) override;
+ bool zero_pack() const override { return false; }
+ void sql_type(String &str) const override;
+ uint32 max_display_length() const override { return field_length; }
Information_schema_numeric_attributes
- information_schema_numeric_attributes() const
+ information_schema_numeric_attributes() const override
{
return Information_schema_numeric_attributes(precision, dec);
}
- uint size_of() const { return sizeof(*this); }
- uint32 pack_length() const { return (uint32) bin_size; }
- uint pack_length_from_metadata(uint field_metadata);
- uint row_pack_length() const { return pack_length(); }
- bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
- uint16 mflags, int *order_var);
- bool is_equal(const Column_definition &new_field) const;
- virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data);
- Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
+ uint size_of() const override { return sizeof *this; }
+ uint32 pack_length() const override { return (uint32) bin_size; }
+ uint pack_length_from_metadata(uint field_metadata) const override;
+ uint row_pack_length() const override { return pack_length(); }
+ bool compatible_field_size(uint field_metadata, const Relay_log_info *rli,
+ uint16 mflags, int *order_var) const override;
+ bool is_equal(const Column_definition &new_field) const override;
+ const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
+ uint param_data) override;
+ Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override;
+ Binlog_type_info binlog_type_info() const override;
};
@@ -2194,28 +2443,31 @@ public:
:Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, 0, zero_arg, unsigned_arg)
{}
- bool memcpy_field_possible(const Field *from) const
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ bool memcpy_field_possible(const Field *from) const override
{
return real_type() == from->real_type() &&
pack_length() == from->pack_length() &&
is_unsigned() == from->is_unsigned();
}
- int store_decimal(const my_decimal *);
- my_decimal *val_decimal(my_decimal *);
- bool val_bool() { return val_int() != 0; }
- ulonglong val_uint()
+ int store_decimal(const my_decimal *) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ bool val_bool() override { return val_int() != 0; }
+ ulonglong val_uint() override
{
longlong nr= val_int();
return nr < 0 && !unsigned_flag ? 0 : (ulonglong) nr;
}
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
virtual const Type_limits_int *type_limits_int() const= 0;
- uint32 max_display_length() const
+ uint32 max_display_length() const override
{
return type_limits_int()->char_length();
}
- Type_std_attributes type_std_attributes() const
+ Type_numeric_attributes type_numeric_attributes() const override
{
/*
For integer data types, the user-specified length does not constrain the
@@ -2229,19 +2481,19 @@ public:
*/
uint32 length1= max_display_length();
uint32 length2= field_length;
- return Type_std_attributes(MY_MAX(length1, length2), decimals(),
- MY_TEST(flags & UNSIGNED_FLAG),
- dtcollation());
+ return Type_numeric_attributes(MY_MAX(length1, length2),
+ decimals(), is_unsigned());
}
Information_schema_numeric_attributes
- information_schema_numeric_attributes() const
+ information_schema_numeric_attributes() const override
{
uint32 prec= type_limits_int()->precision();
return Information_schema_numeric_attributes(prec, 0);
}
+ void sql_type(String &str) const override;
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
- scalar_comparison_op op, Item *value)
+ scalar_comparison_op op, Item *value) override
{
return get_mm_leaf_int(param, key_part, cond, op, value, unsigned_flag);
}
@@ -2250,6 +2502,12 @@ public:
class Field_tiny :public Field_int
{
+ const Type_handler_general_purpose_int *type_handler_priv() const
+ {
+ if (is_unsigned())
+ return &type_handler_utiny;
+ return &type_handler_stiny;
+ }
public:
Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2258,49 +2516,55 @@ public:
:Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_tiny; }
- enum ha_base_keytype key_type() const
+ const Type_handler *type_handler() const override
+ { return type_handler_priv(); }
+ enum ha_base_keytype key_type() const override
{ return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void) { ptr[0]=0; return 0; }
- 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 { return 1; }
- void sql_type(String &str) const;
- const Type_limits_int *type_limits_int() const
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int reset() override { ptr[0]=0; return 0; }
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 1; }
+ const Type_limits_int *type_limits_int() const override
{
- return type_handler_tiny.type_limits_int_by_unsigned_flag(is_unsigned());
+ return type_handler_priv()->type_limits_int();
}
- virtual uchar *pack(uchar* to, const uchar *from, uint max_length)
+ uchar *pack(uchar* to, const uchar *from, uint max_length) override
{
*to= *from;
return to + 1;
}
- virtual const uchar *unpack(uchar* to, const uchar *from,
- const uchar *from_end, uint param_data)
+ const uchar *unpack(uchar* to, const uchar *from,
+ const uchar *from_end, uint param_data) override
{
if (from == from_end)
return 0;
*to= *from;
return from + 1;
}
- virtual ulonglong get_max_int_value() const
+ ulonglong get_max_int_value() const override
{
return unsigned_flag ? 0xFFULL : 0x7FULL;
}
};
-class Field_short :public Field_int
+class Field_short final :public Field_int
{
+ const Type_handler_general_purpose_int *type_handler_priv() const
+ {
+ if (is_unsigned())
+ return &type_handler_ushort;
+ return &type_handler_sshort;
+ }
public:
Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2315,39 +2579,45 @@ public:
:Field_int((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
NONE, field_name_arg, 0, unsigned_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_short; }
- enum ha_base_keytype key_type() const
+ const Type_handler *type_handler() const override
+ { return type_handler_priv(); }
+ enum ha_base_keytype key_type() const override
{ return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;}
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void) { ptr[0]=ptr[1]=0; return 0; }
- 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 { return 2; }
- void sql_type(String &str) const;
- const Type_limits_int *type_limits_int() const
- {
- return type_handler_short.type_limits_int_by_unsigned_flag(is_unsigned());
- }
- virtual uchar *pack(uchar* to, const uchar *from, uint max_length)
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int reset() override { ptr[0]=ptr[1]=0; return 0; }
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 2; }
+ const Type_limits_int *type_limits_int() const override
+ {
+ return type_handler_priv()->type_limits_int();
+ }
+ uchar *pack(uchar* to, const uchar *from, uint) override
{ return pack_int16(to, from); }
- virtual const uchar *unpack(uchar* to, const uchar *from,
- const uchar *from_end, uint param_data)
+ const uchar *unpack(uchar* to, const uchar *from,
+ const uchar *from_end, uint) override
{ return unpack_int16(to, from, from_end); }
- virtual ulonglong get_max_int_value() const
+ ulonglong get_max_int_value() const override
{
return unsigned_flag ? 0xFFFFULL : 0x7FFFULL;
}
};
-class Field_medium :public Field_int
+class Field_medium final :public Field_int
{
+ const Type_handler_general_purpose_int *type_handler_priv() const
+ {
+ if (is_unsigned())
+ return &type_handler_uint24;
+ return &type_handler_sint24;
+ }
public:
Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2356,38 +2626,44 @@ public:
:Field_int(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, zero_arg, unsigned_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_int24; }
- enum ha_base_keytype key_type() const
+ const Type_handler *type_handler() const override
+ { return type_handler_priv(); }
+ enum ha_base_keytype key_type() const override
{ return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
- 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 { return 3; }
- void sql_type(String &str) const;
- const Type_limits_int *type_limits_int() const
- {
- return type_handler_int24.type_limits_int_by_unsigned_flag(is_unsigned());
- }
- virtual uchar *pack(uchar* to, const uchar *from, uint max_length)
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int reset() override { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 3; }
+ const Type_limits_int *type_limits_int() const override
+ {
+ return type_handler_priv()->type_limits_int();
+ }
+ uchar *pack(uchar* to, const uchar *from, uint max_length) override
{
return Field::pack(to, from, max_length);
}
- virtual ulonglong get_max_int_value() const
+ ulonglong get_max_int_value() const override
{
return unsigned_flag ? 0xFFFFFFULL : 0x7FFFFFULL;
}
};
-class Field_long :public Field_int
+class Field_long final :public Field_int
{
+ const Type_handler_general_purpose_int *type_handler_priv() const
+ {
+ if (is_unsigned())
+ return &type_handler_ulong;
+ return &type_handler_slong;
+ }
public:
Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2402,37 +2678,35 @@ public:
:Field_int((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
NONE, field_name_arg, 0, unsigned_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_long; }
- enum ha_base_keytype key_type() const
+ const Type_handler *type_handler() const override
+ { return type_handler_priv(); }
+ enum ha_base_keytype key_type() const override
{ return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
- double val_real(void);
- longlong val_int(void);
- bool send_binary(Protocol *protocol);
- String *val_str(String*,String *);
- 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;
- const Type_limits_int *type_limits_int() const
- {
- return type_handler_long.type_limits_int_by_unsigned_flag(is_unsigned());
- }
- virtual uchar *pack(uchar* to, const uchar *from,
- uint max_length __attribute__((unused)))
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int reset() override { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
+ double val_real() override;
+ longlong val_int() override;
+ bool send(Protocol *protocol) override;
+ String *val_str(String *, String *) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 4; }
+ const Type_limits_int *type_limits_int() const override
+ {
+ return type_handler_priv()->type_limits_int();
+ }
+ uchar *pack(uchar* to, const uchar *from, uint) override
{
return pack_int32(to, from);
}
- virtual const uchar *unpack(uchar* to, const uchar *from,
- const uchar *from_end,
- uint param_data __attribute__((unused)))
+ const uchar *unpack(uchar* to, const uchar *from,
+ const uchar *from_end, uint) override
{
return unpack_int32(to, from, from_end);
}
- virtual ulonglong get_max_int_value() const
+ ulonglong get_max_int_value() const override
{
return unsigned_flag ? 0xFFFFFFFFULL : 0x7FFFFFFFULL;
}
@@ -2441,6 +2715,12 @@ public:
class Field_longlong :public Field_int
{
+ const Type_handler_general_purpose_int *type_handler_priv() const
+ {
+ if (is_unsigned())
+ return &type_handler_ulonglong;
+ return &type_handler_slonglong;
+ }
public:
Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2455,42 +2735,41 @@ public:
:Field_int((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
NONE, field_name_arg, 0, unsigned_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_longlong; }
- enum ha_base_keytype key_type() const
- { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void)
+ const Type_handler *type_handler() const override
+ { return type_handler_priv(); }
+ enum ha_base_keytype key_type() const override
+ { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; }
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int reset() override
{
ptr[0]=ptr[1]=ptr[2]=ptr[3]=ptr[4]=ptr[5]=ptr[6]=ptr[7]=0;
return 0;
}
- 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 { return 8; }
- void sql_type(String &str) const;
- const Type_limits_int *type_limits_int() const
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 8; }
+ const Type_limits_int *type_limits_int() const override
{
- return type_handler_longlong.type_limits_int_by_unsigned_flag(is_unsigned());
+ return type_handler_priv()->type_limits_int();
}
- virtual uchar *pack(uchar* to, const uchar *from,
- uint max_length __attribute__((unused)))
+ uchar *pack(uchar* to, const uchar *from, uint) override
{
return pack_int64(to, from);
}
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
- uint param_data __attribute__((unused)))
+ uint) override
{
return unpack_int64(to, from, from_end);
}
- void set_max();
- bool is_max();
- virtual ulonglong get_max_int_value() const
+ void set_max() override;
+ bool is_max() override;
+ ulonglong get_max_int_value() const override
{
return unsigned_flag ? 0xFFFFFFFFFFFFFFFFULL : 0x7FFFFFFFFFFFFFFFULL;
}
@@ -2510,28 +2789,28 @@ public:
unsigned_arg),
cached(0)
{}
- const Type_handler *type_handler() const { return &type_handler_vers_trx_id; }
- uint size_of() const { return sizeof(*this); }
+ const Type_handler *type_handler() const override
+ { return &type_handler_vers_trx_id; }
+ uint size_of() const override { return sizeof *this; }
bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate, ulonglong trx_id);
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{
return get_date(ltime, fuzzydate, (ulonglong) val_int());
}
- bool test_if_equality_guarantees_uniqueness(const Item *item) const;
- bool can_optimize_keypart_ref(const Item_bool_func *cond,
- const Item *item) const
+ bool test_if_equality_guarantees_uniqueness(const Item *item) const override;
+ bool can_optimize_keypart_ref(const Item_bool_func *, const Item *)
+ const override
{
return true;
}
- bool can_optimize_group_min_max(const Item_bool_func *cond,
- const Item *const_item) const
+ bool can_optimize_group_min_max(const Item_bool_func *, const Item *)
+ const override
{
return true;
}
- bool can_optimize_range(const Item_bool_func *cond,
- const Item *item,
- bool is_eq_func) const
+ bool can_optimize_range(const Item_bool_func *, const Item *, bool)
+ const override
{
return true;
}
@@ -2540,7 +2819,7 @@ public:
};
-class Field_float :public Field_real {
+class Field_float final :public Field_real {
public:
Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -2561,30 +2840,29 @@ public:
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- const Type_handler *type_handler() const { return &type_handler_float; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void) { bzero(ptr,sizeof(float)); return 0; }
- 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 { return sizeof(float); }
- uint row_pack_length() const { return pack_length(); }
- void sql_type(String &str) const;
- virtual ulonglong get_max_int_value() const
+ const Type_handler *type_handler() const override
+ { return &type_handler_float; }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_FLOAT; }
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int reset() override { bzero(ptr,sizeof(float)); return 0; }
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff, uint length) override;
+ uint32 pack_length() const override { return sizeof(float); }
+ uint row_pack_length() const override { return pack_length(); }
+ ulonglong get_max_int_value() const override
{
/*
We use the maximum as per IEEE754-2008 standard, 2^24
*/
return 0x1000000ULL;
}
-private:
- int save_field_metadata(uchar *first_byte);
+ Binlog_type_info binlog_type_info() const override;
};
@@ -2620,36 +2898,35 @@ public:
if (dec_arg >= FLOATING_POINT_DECIMALS)
dec_arg= NOT_FIXED_DEC;
}
- void init_for_tmp_table(Field *org_field, TABLE *new_table)
+ void init_for_tmp_table(Field *org_field, TABLE *new_table) override
{
Field::init_for_tmp_table(org_field, new_table);
not_fixed= true;
}
- const Type_handler *type_handler() const { return &type_handler_double; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int reset(void) { bzero(ptr,sizeof(double)); return 0; }
- double val_real(void);
- longlong val_int(void) { return val_int_from_real(false); }
- ulonglong val_uint(void) { return (ulonglong) val_int_from_real(true); }
- 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 { return sizeof(double); }
- uint row_pack_length() const { return pack_length(); }
- void sql_type(String &str) const;
- virtual ulonglong get_max_int_value() const
+ const Type_handler *type_handler() const override
+ { return &type_handler_double; }
+ enum ha_base_keytype key_type() const override final { return HA_KEYTYPE_DOUBLE; }
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override final;
+ int store(double nr) override final;
+ int store(longlong nr, bool unsigned_val) override final;
+ int reset() override final { bzero(ptr,sizeof(double)); return 0; }
+ double val_real() override final;
+ longlong val_int() override final { return val_int_from_real(false); }
+ ulonglong val_uint() override final { return (ulonglong) val_int_from_real(true); }
+ String *val_str(String *, String *) override final;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override final;
+ void sort_string(uchar *buff, uint length) override final;
+ uint32 pack_length() const override final { return sizeof(double); }
+ uint row_pack_length() const override final { return pack_length(); }
+ ulonglong get_max_int_value() const override final
{
/*
We use the maximum as per IEEE754-2008 standard, 2^53
*/
return 0x20000000000000ULL;
}
-private:
- int save_field_metadata(uchar *first_byte);
+ Binlog_type_info binlog_type_info() const override final;
};
@@ -2664,50 +2941,54 @@ public:
:Field_str(ptr_arg, len_arg, null, 1,
unireg_check_arg, field_name_arg, collation)
{}
- const Type_handler *type_handler() const { return &type_handler_null; }
+ const Type_handler *type_handler() const override
+ { return &type_handler_null; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
Information_schema_character_attributes
- information_schema_character_attributes() const
+ information_schema_character_attributes() const override
{
return Information_schema_character_attributes();
}
- Copy_func *get_copy_func(const Field *from) const
+ Copy_func *get_copy_func(const Field *from) const override
{
return do_field_string;
}
- int store(const char *to, size_t length, CHARSET_INFO *cs)
+ int store(const char *to, size_t length, CHARSET_INFO *cs) override final
{ null[0]=1; return 0; }
- int store(double nr) { null[0]=1; return 0; }
- int store(longlong nr, bool unsigned_val) { null[0]=1; return 0; }
- int store_decimal(const my_decimal *d) { null[0]=1; return 0; }
- int reset(void) { return 0; }
- double val_real(void) { return 0.0;}
- longlong val_int(void) { return 0;}
- bool val_bool(void) { return false; }
- my_decimal *val_decimal(my_decimal *) { return 0; }
- String *val_str(String *value,String *value2)
+ int store(double nr) override final { null[0]=1; return 0; }
+ int store(longlong nr, bool unsigned_val) override final { null[0]=1; return 0; }
+ int store_decimal(const my_decimal *d) override final { null[0]=1; return 0; }
+ int reset() override final { return 0; }
+ double val_real() override final { return 0.0;}
+ longlong val_int() override final { return 0;}
+ bool val_bool() override final { return false; }
+ my_decimal *val_decimal(my_decimal *) override final { return 0; }
+ String *val_str(String *value,String *value2) override final
{ value2->length(0); return value2;}
- bool is_equal(const Column_definition &new_field) const;
- int cmp(const uchar *a, const uchar *b) { return 0;}
- void sort_string(uchar *buff, uint length) {}
- uint32 pack_length() const { return 0; }
- void sql_type(String &str) const;
- uint size_of() const { return sizeof(*this); }
- uint32 max_display_length() const { return 4; }
- void move_field_offset(my_ptrdiff_t ptr_diff) {}
+ bool is_equal(const Column_definition &new_field) const override final;
+ int cmp(const uchar *a, const uchar *b) const override final { return 0;}
+ void sort_string(uchar *buff, uint length) override final {}
+ uint32 pack_length() const override final { return 0; }
+ void sql_type(String &str) const override final;
+ uint size_of() const override final { return sizeof *this; }
+ uint32 max_display_length() const override final { return 4; }
+ void move_field_offset(my_ptrdiff_t ptr_diff) override final {}
bool can_optimize_keypart_ref(const Item_bool_func *cond,
- const Item *item) const
+ const Item *item) const override final
{
return false;
}
bool can_optimize_group_min_max(const Item_bool_func *cond,
- const Item *const_item) const
+ const Item *const_item) const override final
{
return false;
}
};
-class Field_temporal: public Field {
+class Field_temporal :public Field {
protected:
Item *get_equal_const_item_datetime(THD *thd, const Context &ctx,
Item *const_item);
@@ -2740,6 +3021,22 @@ protected:
set_warnings(level, str, MYSQL_TIME_WARN_TRUNCATED, typestr);
return 1;
}
+ void sql_type_comment(String &str,
+ const Name &name,
+ const Name &comment) const;
+ void sql_type_dec_comment(String &str,
+ const Name &name, uint dec,
+ const Name &comment) const;
+ void sql_type_opt_dec_comment(String &str,
+ const Name &name, uint dec,
+ const Name &comment) const
+ {
+ if (dec)
+ sql_type_dec_comment(str, name, dec, comment);
+ else
+ sql_type_comment(str, name, comment);
+ }
+ static const Name &type_version_mysql56();
public:
Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -2747,13 +3044,13 @@ public:
:Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
field_name_arg)
{ flags|= BINARY_FLAG; }
- int store_hex_hybrid(const char *str, size_t length)
+ int store_hex_hybrid(const char *str, size_t length) override
{
return store(str, length, &my_charset_bin);
}
- sql_mode_t can_handle_sql_mode_dependency_on_store() const;
- Copy_func *get_copy_func(const Field *from) const;
- int save_in_field(Field *to)
+ sql_mode_t can_handle_sql_mode_dependency_on_store() const override;
+ Copy_func *get_copy_func(const Field *from) const override;
+ int save_in_field(Field *to) override
{
MYSQL_TIME ltime;
// For temporal types no truncation needed. Rounding mode is not important.
@@ -2761,38 +3058,43 @@ public:
return to->reset();
return to->store_time_dec(&ltime, decimals());
}
- bool memcpy_field_possible(const Field *from) const;
- uint32 max_display_length() const { 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; }
- CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
- bool binary() const { return true; }
- bool val_bool() { return val_real() != 0e0; }
- bool is_equal(const Column_definition &new_field) const;
- bool eq_def(const Field *field) const
+ bool memcpy_field_possible(const Field *from) const override;
+ uint32 max_display_length() const override { return field_length; }
+ bool str_needs_quotes() const override { return true; }
+ CHARSET_INFO *charset() const override
+ {
+ return DTCollation_numeric::singleton().collation;
+ }
+ const DTCollation &dtcollation() const override
+ {
+ return DTCollation_numeric::singleton();
+ }
+ CHARSET_INFO *sort_charset() const override { return &my_charset_bin; }
+ bool binary() const override { return true; }
+ bool val_bool() override { return val_real() != 0e0; }
+ bool is_equal(const Column_definition &new_field) const override;
+ bool eq_def(const Field *field) const override
{
return (Field::eq_def(field) && decimals() == field->decimals());
}
- my_decimal *val_decimal(my_decimal*);
- double pos_in_interval(Field *min, Field *max)
+ my_decimal *val_decimal(my_decimal*) override;
+ double pos_in_interval(Field *min, Field *max) override
{
return pos_in_interval_val_real(min, max);
}
bool can_optimize_keypart_ref(const Item_bool_func *cond,
- const Item *item) const;
+ const Item *item) const override;
bool can_optimize_group_min_max(const Item_bool_func *cond,
- const Item *const_item) const;
+ const Item *const_item) const override;
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
- bool is_eq_func) const
+ bool is_eq_func) const override
{
return true;
}
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
- scalar_comparison_op op, Item *value);
+ scalar_comparison_op op, Item *value) override;
};
@@ -2803,7 +3105,7 @@ public:
- DATETIME(1..6)
- DATETIME(0..6) - MySQL56 version
*/
-class Field_temporal_with_date: public Field_temporal {
+class Field_temporal_with_date :public Field_temporal {
protected:
virtual void store_TIME(const MYSQL_TIME *ltime) = 0;
void store_datetime(const Datetime &dt)
@@ -2837,10 +3139,7 @@ class Field_timestamp :public Field_temporal {
protected:
int store_TIME_with_warning(THD *, const Datetime *,
const ErrConv *, int warn);
- virtual void store_TIMEVAL(const timeval &tv)
- {
- int4store(ptr, tv.tv_sec);
- }
+ virtual void store_TIMEVAL(const timeval &tv)= 0;
void store_TIMESTAMP(const Timestamp &ts)
{
store_TIMEVAL(ts.tv());
@@ -2852,33 +3151,23 @@ public:
enum utype unireg_check_arg,
const LEX_CSTRING *field_name_arg,
TABLE_SHARE *share);
- const Type_handler *type_handler() const { return &type_handler_timestamp; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
- Copy_func *get_copy_func(const Field *from) const;
- sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const;
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- int store_decimal(const my_decimal *);
- int store_timestamp_dec(const timeval &ts, uint dec);
- int save_in_field(Field *to);
- 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 { return 4; }
- void sql_type(String &str) const;
- bool zero_pack() const { return 0; }
- int set_time();
- /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */
- my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
- my_time_t get_timestamp(ulong *sec_part) const
- {
- return get_timestamp(ptr, sec_part);
- }
+ const Type_handler *type_handler() const override
+ { return &type_handler_timestamp; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ Copy_func *get_copy_func(const Field *from) const override;
+ sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const override;
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ int store_decimal(const my_decimal *) override;
+ int store_timestamp_dec(const timeval &ts, uint dec) override;
+ int save_in_field(Field *to) override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool zero_pack() const override { return false; }
/*
This method is used by storage/perfschema and
Item_func_now_local::save_in_field().
@@ -2889,27 +3178,63 @@ public:
time_round_mode_t mode= Datetime::default_round_mode(get_thd());
store_TIMESTAMP(Timestamp(ts, sec_part).round(decimals(), mode, &warn));
}
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
- int store_native(const Native &value);
- bool val_native(Native *to);
- uchar *pack(uchar *to, const uchar *from,
- uint max_length __attribute__((unused)))
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
+ int store_native(const Native &value) override;
+ bool validate_value_in_record(THD *thd, const uchar *record) const override;
+ Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override
+ {
+ return get_equal_const_item_datetime(thd, ctx, const_item);
+ }
+ bool load_data_set_null(THD *thd) override;
+ bool load_data_set_no_data(THD *thd, bool fixed_format) override;
+};
+
+
+class Field_timestamp0 :public Field_timestamp
+{
+ void store_TIMEVAL(const timeval &tv) override
+ {
+ int4store(ptr, tv.tv_sec);
+ }
+public:
+ Field_timestamp0(uchar *ptr_arg, uint32 len_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg,
+ TABLE_SHARE *share)
+ :Field_timestamp(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, share)
+ { }
+ enum ha_base_keytype key_type() const override
+ { return HA_KEYTYPE_ULONG_INT; }
+ void sql_type(String &str) const override
+ {
+ sql_type_comment(str, Field_timestamp0::type_handler()->name(),
+ Type_handler::version_mariadb53());
+ }
+ double val_real() override
+ {
+ return (double) Field_timestamp0::val_int();
+ }
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 4; }
+ int set_time() override;
+ /* Get TIMESTAMP field value as seconds since begging of Unix Epoch */
+ my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const override;
+ bool val_native(Native *to) override;
+ uchar *pack(uchar *to, const uchar *from, uint) override
{
return pack_int32(to, from);
}
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
- uint param_data __attribute__((unused)))
+ uint) override
{
return unpack_int32(to, from, from_end);
}
- bool validate_value_in_record(THD *thd, const uchar *record) const;
- Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
- {
- return get_equal_const_item_datetime(thd, ctx, const_item);
- }
- bool load_data_set_null(THD *thd);
- bool load_data_set_no_data(THD *thd, bool fixed_format);
- uint size_of() const { return sizeof(*this); }
+ uint size_of() const override { return sizeof *this; }
};
@@ -2934,23 +3259,23 @@ public:
{
DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
}
- uint decimals() const { return dec; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- uchar *pack(uchar *to, const uchar *from, uint max_length)
+ uint decimals() const override { return dec; }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; }
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override
{ return Field::pack(to, from, max_length); }
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
- uint param_data)
+ uint param_data) override
{ return Field::unpack(to, from, from_end, param_data); }
- void make_send_field(Send_field *field);
- void sort_string(uchar *to, uint length)
+ void make_send_field(Send_field *field) override;
+ void sort_string(uchar *to, uint length) override
{
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();
+ bool send(Protocol *protocol) override;
+ double val_real() override;
+ my_decimal* val_decimal(my_decimal*) override;
+ int set_time() override;
};
@@ -2959,7 +3284,7 @@ class Field_timestamp_hires :public Field_timestamp_with_dec {
{
return Type_handler_timestamp::sec_part_bytes(dec);
}
- void store_TIMEVAL(const timeval &tv);
+ void store_TIMEVAL(const timeval &tv) override;
public:
Field_timestamp_hires(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
@@ -2971,24 +3296,24 @@ public:
{
DBUG_ASSERT(dec);
}
- bool val_native(Native *to);
- my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
- int cmp(const uchar *,const uchar *);
- uint32 pack_length() const { return 4 + sec_part_bytes(dec); }
- uint size_of() const { return sizeof(*this); }
+ void sql_type(String &str) const override
+ {
+ sql_type_dec_comment(str, Field_timestamp_hires::type_handler()->name(),
+ dec, Type_handler::version_mariadb53());
+ }
+ bool val_native(Native *to) override;
+ my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const override;
+ int cmp(const uchar *,const uchar *) const override;
+ uint32 pack_length() const override { return 4 + sec_part_bytes(dec); }
+ uint size_of() const override { return sizeof *this; }
};
/**
TIMESTAMP(0..6) - MySQL56 version
*/
-class Field_timestampf :public Field_timestamp_with_dec {
- int save_field_metadata(uchar *metadata_ptr)
- {
- *metadata_ptr= (uchar) decimals();
- return 1;
- }
- void store_TIMEVAL(const timeval &tv);
+class Field_timestampf final :public Field_timestamp_with_dec {
+ void store_TIMEVAL(const timeval &tv) override;
public:
Field_timestampf(uchar *ptr_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
@@ -2998,36 +3323,44 @@ public:
Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, share, dec_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_timestamp2; }
- enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; }
- uint32 pack_length() const
+ const Type_handler *type_handler() const override
+ { return &type_handler_timestamp2; }
+ enum_field_types binlog_type() const override
+ { return MYSQL_TYPE_TIMESTAMP2; }
+ void sql_type(String &str) const override
+ {
+ sql_type_opt_dec_comment(str, Field_timestampf::type_handler()->name(),
+ dec, type_version_mysql56());
+
+ }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ uint32 pack_length() const override
{
return my_timestamp_binary_length(dec);
}
- uint row_pack_length() const { return pack_length(); }
- uint pack_length_from_metadata(uint field_metadata)
+ uint row_pack_length() const override { return pack_length(); }
+ uint pack_length_from_metadata(uint field_metadata) const override
{
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)
+ int cmp(const uchar *a_ptr,const uchar *b_ptr) const override
{
return memcmp(a_ptr, b_ptr, pack_length());
}
- void set_max();
- bool is_max();
- my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const;
- my_time_t get_timestamp(ulong *sec_part) const
- {
- return get_timestamp(ptr, sec_part);
- }
- bool val_native(Native *to);
- uint size_of() const { return sizeof(*this); }
+ void set_max() override;
+ bool is_max() override;
+ my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const override;
+ bool val_native(Native *to) override;
+ uint size_of() const override { return sizeof *this; }
+ Binlog_type_info binlog_type_info() const override;
};
-class Field_year :public Field_tiny {
+class Field_year final :public Field_tiny {
public:
Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
@@ -3035,11 +3368,14 @@ public:
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, 1, 1)
{}
- const Type_handler *type_handler() const
+ const Type_handler *type_handler() const override
{
return field_length == 2 ? &type_handler_year2 : &type_handler_year;
}
- Copy_func *get_copy_func(const Field *from) const
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ Copy_func *get_copy_func(const Field *from) const override
{
if (eq_def(from))
return get_identical_copy_func();
@@ -3066,26 +3402,26 @@ public:
}
return do_field_int;
}
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
- bool send_binary(Protocol *protocol);
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
+ bool send(Protocol *protocol) override;
Information_schema_numeric_attributes
- information_schema_numeric_attributes() const
+ information_schema_numeric_attributes() const override
{
return Information_schema_numeric_attributes();
}
- uint32 max_display_length() const { return field_length; }
- void sql_type(String &str) const;
+ uint32 max_display_length() const override { return field_length; }
+ void sql_type(String &str) const override;
};
-class Field_date_common: public Field_temporal_with_date
+class Field_date_common :public Field_temporal_with_date
{
protected:
int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str,
@@ -3098,80 +3434,91 @@ public:
null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
- Copy_func *get_copy_func(const Field *from) const;
+ Copy_func *get_copy_func(const Field *from) const override;
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
- scalar_comparison_op op, Item *value);
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- int store_decimal(const my_decimal *);
+ scalar_comparison_op op, Item *value) override;
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ int store_decimal(const my_decimal *) override;
};
-class Field_date :public Field_date_common
+class Field_date final :public Field_date_common
{
- void store_TIME(const MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
+ void store_TIME(const MYSQL_TIME *ltime) override;
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate)
+ const override;
public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg) {}
- const Type_handler *type_handler() const { return &type_handler_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; }
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ const Type_handler *type_handler() const override
+ { return &type_handler_date; }
+ enum ha_base_keytype key_type() const override
+ { return HA_KEYTYPE_ULONG_INT; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ int reset() override { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{ return Field_date::get_TIME(ltime, ptr, fuzzydate); }
- 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 { return 4; }
- void sql_type(String &str) const;
- uchar *pack(uchar* to, const uchar *from,
- uint max_length __attribute__((unused)))
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 4; }
+ void sql_type(String &str) const override;
+ uchar *pack(uchar* to, const uchar *from, uint) override
{
return pack_int32(to, from);
}
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
- uint param_data __attribute__((unused)))
+ uint) override
{
return unpack_int32(to, from, from_end);
}
- uint size_of() const { return sizeof(*this); }
+ uint size_of() const override { return sizeof *this; }
};
-class Field_newdate :public Field_date_common
+class Field_newdate final :public Field_date_common
{
- void store_TIME(const MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
+ void store_TIME(const MYSQL_TIME *ltime) override;
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate)
+ const override;
public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg)
:Field_date_common(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_newdate; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
- int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
- 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 { return 3; }
- void sql_type(String &str) const;
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ const Type_handler *type_handler() const override
+ { return &type_handler_newdate; }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_UINT24; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ int reset() override { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 3; }
+ void sql_type(String &str) const override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{ return Field_newdate::get_TIME(ltime, ptr, fuzzydate); }
- longlong val_datetime_packed(THD *thd);
- uint size_of() const { return sizeof(*this); }
- Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
+ longlong val_datetime_packed(THD *thd) override;
+ uint size_of() const override { return sizeof *this; }
+ Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override;
};
@@ -3183,11 +3530,8 @@ class Field_time :public Field_temporal {
*/
long curdays;
protected:
- virtual void store_TIME(const MYSQL_TIME *ltime);
- void store_TIME(const Time &t)
- {
- return store_TIME(t.get_mysql_time());
- }
+ virtual void store_TIME(const MYSQL_TIME *ltime)= 0;
+ void store_TIME(const Time &t) { return store_TIME(t.get_mysql_time()); }
int store_TIME_with_warning(const Time *ltime, const ErrConv *str, int warn);
bool check_zero_in_date_with_warn(date_mode_t fuzzydate);
static void do_field_time(Copy_field *copy);
@@ -3199,10 +3543,13 @@ public:
unireg_check_arg, field_name_arg), curdays(0)
{}
bool can_be_substituted_to_equal_item(const Context &ctx,
- const Item_equal *item_equal);
- const Type_handler *type_handler() const { return &type_handler_time; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
- Copy_func *get_copy_func(const Field *from) const
+ const Item_equal *item_equal) override;
+ const Type_handler *type_handler() const override
+ { return &type_handler_time; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ Copy_func *get_copy_func(const Field *from) const override
{
return from->cmp_type() == REAL_RESULT ? do_field_string : // MDEV-9344
from->type() == MYSQL_TYPE_YEAR ? do_field_int :
@@ -3210,34 +3557,54 @@ public:
eq_def(from) ? get_identical_copy_func() :
do_field_time;
}
- bool memcpy_field_possible(const Field *from) const
+ bool memcpy_field_possible(const Field *from) const override
{
return real_type() == from->real_type() &&
decimals() == from->decimals();
}
- sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const;
- int store_native(const Native &value);
- bool val_native(Native *to);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_decimal(const my_decimal *);
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
- bool send_binary(Protocol *protocol);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
- uint32 pack_length() const { return 3; }
- void sql_type(String &str) const;
- uint size_of() const { return sizeof(*this); }
+ sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const override;
+ int store_native(const Native &value) override;
+ bool val_native(Native *to) override;
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_decimal(const my_decimal *) override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
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);
- Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
+ uchar *new_null_ptr, uint new_null_bit) override;
+ Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override;
+};
+
+
+class Field_time0 final :public Field_time
+{
+protected:
+ void store_TIME(const MYSQL_TIME *ltime) override;
+public:
+ Field_time0(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg)
+ :Field_time(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
+ { }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_INT24; }
+ void sql_type(String &str) const override
+ {
+ sql_type_comment(str, Field_time0::type_handler()->name(),
+ Type_handler::version_mariadb53());
+ }
+ double val_real() override;
+ longlong val_int() override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 3; }
+ uint size_of() const override { return sizeof *this; }
};
@@ -3260,20 +3627,20 @@ public:
{
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_send_field(Send_field *);
+ uint decimals() const override { return dec; }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; }
+ longlong val_int() override;
+ double val_real() override;
+ void make_send_field(Send_field *) override;
};
/**
TIME(1..6)
*/
-class Field_time_hires :public Field_time_with_dec {
+class Field_time_hires final :public Field_time_with_dec {
longlong zero_point;
- void store_TIME(const MYSQL_TIME *);
+ void store_TIME(const MYSQL_TIME *) override;
public:
Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
@@ -3286,25 +3653,26 @@ public:
zero_point= sec_part_shift(
((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec);
}
- int reset(void);
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
- uint32 pack_length() const { return Type_handler_time::hires_bytes(dec); }
- uint size_of() const { return sizeof(*this); }
+ void sql_type(String &str) const override
+ {
+ sql_type_dec_comment(str, Field_time_hires::type_handler()->name(),
+ dec, Type_handler::version_mariadb53());
+ }
+ int reset() override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override
+ { return Type_handler_time::hires_bytes(dec); }
+ uint size_of() const override { return sizeof *this; }
};
/**
TIME(0..6) - MySQL56 version
*/
-class Field_timef :public Field_time_with_dec {
- void store_TIME(const MYSQL_TIME *ltime);
- int save_field_metadata(uchar *metadata_ptr)
- {
- *metadata_ptr= (uchar) decimals();
- return 1;
- }
+class Field_timef final :public Field_time_with_dec {
+ void store_TIME(const MYSQL_TIME *ltime) override;
public:
Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
@@ -3315,40 +3683,48 @@ public:
{
DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
}
- const Type_handler *type_handler() const { return &type_handler_time2; }
- enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; }
- uint32 pack_length() const
+ const Type_handler *type_handler() const override
+ { return &type_handler_time2; }
+ enum_field_types binlog_type() const override { return MYSQL_TYPE_TIME2; }
+ void sql_type(String &str) const override
+ {
+ sql_type_opt_dec_comment(str, Field_timef::type_handler()->name(),
+ dec, type_version_mysql56());
+ }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ uint32 pack_length() const override
{
return my_time_binary_length(dec);
}
- uint row_pack_length() const { return pack_length(); }
- uint pack_length_from_metadata(uint field_metadata)
+ uint row_pack_length() const override { return pack_length(); }
+ uint pack_length_from_metadata(uint field_metadata) const override
{
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)
+ void sort_string(uchar *to, uint length) override
{
DBUG_ASSERT(length == Field_timef::pack_length());
memcpy(to, ptr, length);
}
- int cmp(const uchar *a_ptr, const uchar *b_ptr)
+ int cmp(const uchar *a_ptr, const uchar *b_ptr) const override
{
return memcmp(a_ptr, b_ptr, pack_length());
}
- int reset();
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
- longlong val_time_packed(THD *thd);
- int store_native(const Native &value);
- bool val_native(Native *to);
- uint size_of() const { return sizeof(*this); }
+ int reset() override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override;
+ longlong val_time_packed(THD *thd) override;
+ int store_native(const Native &value) override;
+ bool val_native(Native *to) override;
+ uint size_of() const override { return sizeof *this; }
+ Binlog_type_info binlog_type_info() const override;
};
class Field_datetime :public Field_temporal_with_date {
- void store_TIME(const MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
protected:
int store_TIME_with_warning(const Datetime *ltime, const ErrConv *str,
int was_cut);
@@ -3363,40 +3739,71 @@ public:
unireg_check == TIMESTAMP_DNUN_FIELD)
flags|= ON_UPDATE_NOW_FLAG;
}
- const Type_handler *type_handler() const { return &type_handler_datetime; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
- sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const;
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(const MYSQL_TIME *ltime, uint dec);
- int store_decimal(const my_decimal *);
- 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 { return 8; }
- void sql_type(String &str) const;
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
- { return Field_datetime::get_TIME(ltime, ptr, fuzzydate); }
- int set_time();
- uchar *pack(uchar* to, const uchar *from,
- uint max_length __attribute__((unused)))
+ const Type_handler *type_handler() const override
+ { return &type_handler_datetime; }
+ sql_mode_t conversion_depends_on_sql_mode(THD *, Item *) const override;
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_time_dec(const MYSQL_TIME *ltime, uint dec) override;
+ int store_decimal(const my_decimal *) override;
+ int set_time() override;
+ Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
+ override
+ {
+ return get_equal_const_item_datetime(thd, ctx, const_item);
+ }
+};
+
+
+/*
+ Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte
+*/
+
+class Field_datetime0 final :public Field_datetime
+{
+ void store_TIME(const MYSQL_TIME *ltime) override;
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate)
+ const override;
+public:
+ Field_datetime0(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, enum utype unireg_check_arg,
+ const LEX_CSTRING *field_name_arg)
+ :Field_datetime(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
+ {}
+ enum ha_base_keytype key_type() const override
+ { return HA_KEYTYPE_ULONGLONG; }
+ void sql_type(String &str) const override
+ {
+ sql_type_comment(str, Field_datetime0::type_handler()->name(),
+ Type_handler::version_mariadb53());
+ }
+ double val_real() override
+ {
+ return (double) Field_datetime0::val_int();
+ }
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return 8; }
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
+ { return Field_datetime0::get_TIME(ltime, ptr, fuzzydate); }
+ uchar *pack(uchar* to, const uchar *from, uint) override
{
return pack_int64(to, from);
}
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
- uint param_data __attribute__((unused)))
+ uint) override
{
return unpack_int64(to, from, from_end);
}
- Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item)
- {
- return get_equal_const_item_datetime(thd, ctx, const_item);
- }
- uint size_of() const { return sizeof(*this); }
+ uint size_of() const override { return sizeof *this; }
};
@@ -3418,32 +3825,33 @@ public:
{
DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
}
- uint decimals() const { return dec; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- void make_send_field(Send_field *field);
- bool send_binary(Protocol *protocol);
- uchar *pack(uchar *to, const uchar *from, uint max_length)
+ uint decimals() const override final { return dec; }
+ enum ha_base_keytype key_type() const override final { return HA_KEYTYPE_BINARY; }
+ void make_send_field(Send_field *field) override final;
+ bool send(Protocol *protocol) override final;
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override final
{ return Field::pack(to, from, max_length); }
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
- uint param_data)
+ uint param_data) override final
{ return Field::unpack(to, from, from_end, param_data); }
- void sort_string(uchar *to, uint length)
+ void sort_string(uchar *to, uint length) override final
{
DBUG_ASSERT(length == pack_length());
memcpy(to, ptr, length);
}
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
+ double val_real() override final;
+ longlong val_int() override final;
+ String *val_str(String *, String *) override final;
};
/**
DATETIME(1..6)
*/
-class Field_datetime_hires :public Field_datetime_with_dec {
- void store_TIME(const MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
+class Field_datetime_hires final :public Field_datetime_with_dec {
+ void store_TIME(const MYSQL_TIME *ltime) override;
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate)
+ const override;
public:
Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -3453,25 +3861,28 @@ public:
{
DBUG_ASSERT(dec);
}
- int cmp(const uchar *,const uchar *);
- uint32 pack_length() const { return Type_handler_datetime::hires_bytes(dec); }
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ void sql_type(String &str) const override
+ {
+ sql_type_dec_comment(str, Field_datetime_hires::type_handler()->name(),
+ dec, Type_handler::version_mariadb53());
+ }
+ int cmp(const uchar *,const uchar *) const override;
+ uint32 pack_length() const override
+ { return Type_handler_datetime::hires_bytes(dec); }
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{ return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); }
- uint size_of() const { return sizeof(*this); }
+ uint size_of() const override { return sizeof *this; }
};
/**
DATETIME(0..6) - MySQL56 version
*/
-class Field_datetimef :public Field_datetime_with_dec {
- void store_TIME(const MYSQL_TIME *ltime);
- bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate) const;
- int save_field_metadata(uchar *metadata_ptr)
- {
- *metadata_ptr= (uchar) decimals();
- return 1;
- }
+
+class Field_datetimef final :public Field_datetime_with_dec {
+ void store_TIME(const MYSQL_TIME *ltime) override;
+ bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, date_mode_t fuzzydate)
+ const override;
public:
Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -3479,28 +3890,39 @@ public:
:Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, dec_arg)
{}
- const Type_handler *type_handler() const { return &type_handler_datetime2; }
- enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; }
- uint32 pack_length() const
+ const Type_handler *type_handler() const override
+ { return &type_handler_datetime2; }
+ enum_field_types binlog_type() const override
+ { return MYSQL_TYPE_DATETIME2; }
+ void sql_type(String &str) const override
+ {
+ sql_type_opt_dec_comment(str, Field_datetimef::type_handler()->name(),
+ dec, type_version_mysql56());
+ }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ uint32 pack_length() const override
{
return my_datetime_binary_length(dec);
}
- uint row_pack_length() const { return pack_length(); }
- uint pack_length_from_metadata(uint field_metadata)
+ uint row_pack_length() const override { return pack_length(); }
+ uint pack_length_from_metadata(uint field_metadata) const override
{
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)
+ int cmp(const uchar *a_ptr, const uchar *b_ptr) const override
{
return memcmp(a_ptr, b_ptr, pack_length());
}
- int reset();
- bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ int reset() override;
+ bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{ return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); }
- longlong val_datetime_packed(THD *thd);
- uint size_of() const { return sizeof(*this); }
+ longlong val_datetime_packed(THD *thd) override;
+ uint size_of() const override { return sizeof *this; }
+ Binlog_type_info binlog_type_info() const override;
};
@@ -3512,8 +3934,8 @@ new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit,
{
if (dec==0)
return new (root)
- Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr,
- null_bit, unireg_check, field_name, share);
+ Field_timestamp0(ptr, MAX_DATETIME_WIDTH, null_ptr,
+ null_bit, unireg_check, field_name, share);
if (dec >= FLOATING_POINT_DECIMALS)
dec= MAX_DATETIME_PRECISION;
return new (root)
@@ -3528,8 +3950,8 @@ new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit,
{
if (dec == 0)
return new (root)
- Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, unireg_check,
- field_name);
+ Field_time0(ptr, MIN_TIME_WIDTH, null_ptr, null_bit, unireg_check,
+ field_name);
if (dec >= FLOATING_POINT_DECIMALS)
dec= MAX_DATETIME_PRECISION;
return new (root)
@@ -3543,8 +3965,8 @@ new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit,
{
if (dec == 0)
return new (root)
- Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit,
- unireg_check, field_name);
+ Field_datetime0(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit,
+ unireg_check, field_name);
if (dec >= FLOATING_POINT_DECIMALS)
dec= MAX_DATETIME_PRECISION;
return new (root)
@@ -3552,7 +3974,7 @@ new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit,
unireg_check, field_name, dec);
}
-class Field_string :public Field_longstr {
+class Field_string final :public Field_longstr {
class Warn_filter_string: public Warn_filter
{
public:
@@ -3582,78 +4004,91 @@ public:
NONE, field_name_arg, collation),
can_alter_field_type(1) {};
- const Type_handler *type_handler() const
+ const Type_handler *type_handler() const override
{
if (is_var_string())
return &type_handler_var_string;
return &type_handler_string;
}
- enum ha_base_keytype key_type() const
+ enum ha_base_keytype key_type() const override
{ return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
- bool zero_pack() const { return 0; }
- Copy_func *get_copy_func(const Field *from) const;
- int reset(void)
+ en_fieldtype tmp_engine_column_type(bool use_packed_rows) const override;
+ bool zero_pack() const override { return false; }
+ Copy_func *get_copy_func(const Field *from) const override;
+ int reset() override
{
- charset()->cset->fill(charset(),(char*) ptr, field_length,
- (has_charset() ? ' ' : 0));
+ charset()->fill((char*) ptr, field_length, (has_charset() ? ' ' : 0));
return 0;
}
- int store(const char *to,size_t length,CHARSET_INFO *charset);
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
using Field_str::store;
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- my_decimal *val_decimal(my_decimal *);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
- void sql_type(String &str) const;
- void sql_rpl_type(String*) const;
- bool is_equal(const Column_definition &new_field) const;
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ void update_data_type_statistics(Data_type_statistics *st) const override
+ {
+ st->m_fixed_string_count++;
+ st->m_fixed_string_total_length+= pack_length();
+ }
+ void sql_type(String &str) const override;
+ void sql_rpl_type(String*) const override;
+ bool is_equal(const Column_definition &new_field) const override;
bool can_be_converted_by_engine(const Column_definition &new_type) const
+ override
{
return table->file->can_convert_string(this, new_type);
}
- virtual uchar *pack(uchar *to, const uchar *from,
- uint max_length);
- virtual const uchar *unpack(uchar* to, const uchar *from,
- const uchar *from_end,uint param_data);
- uint pack_length_from_metadata(uint field_metadata)
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override;
+ const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
+ uint param_data) override;
+ uint pack_length_from_metadata(uint field_metadata) const override
{
DBUG_PRINT("debug", ("field_metadata: 0x%04x", field_metadata));
if (field_metadata == 0)
return row_pack_length();
return (((field_metadata >> 4) & 0x300) ^ 0x300) + (field_metadata & 0x00ff);
}
- bool compatible_field_size(uint field_metadata, Relay_log_info *rli,
- uint16 mflags, int *order_var);
- uint row_pack_length() const { return field_length; }
+ bool compatible_field_size(uint field_metadata, const Relay_log_info *rli,
+ uint16 mflags, int *order_var) const override;
+ uint row_pack_length() const override { return field_length; }
int pack_cmp(const uchar *a,const uchar *b,uint key_length,
bool insert_or_update);
int pack_cmp(const uchar *b,uint key_length,bool insert_or_update);
- uint packed_col_length(const uchar *to, uint length);
- uint max_packed_col_length(uint max_length);
- uint size_of() const { return sizeof(*this); }
- bool has_charset(void) const
- { return charset() == &my_charset_bin ? FALSE : TRUE; }
- Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
- virtual uint get_key_image(uchar *buff,uint length, imagetype type);
- sql_mode_t value_depends_on_sql_mode() const;
- sql_mode_t can_handle_sql_mode_dependency_on_store() const;
- void print_key_value(String *out, uint32 length);
-private:
- int save_field_metadata(uchar *first_byte);
+ uint packed_col_length(const uchar *to, uint length) override;
+ uint max_packed_col_length(uint max_length) override;
+ uint size_of() const override { return sizeof *this; }
+ bool has_charset() const override { return charset() != &my_charset_bin; }
+ Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type)
+ override;
+ uint get_key_image(uchar *buff, uint length,
+ const uchar *ptr_arg, imagetype type) const override;
+ sql_mode_t value_depends_on_sql_mode() const override;
+ sql_mode_t can_handle_sql_mode_dependency_on_store() const override;
+ void print_key_value(String *out, uint32 length) override;
+ Binlog_type_info binlog_type_info() const override;
};
class Field_varstring :public Field_longstr {
public:
- uchar *get_data() const
+ const uchar *get_data() const
{
- return ptr + length_bytes;
+ return get_data(ptr);
+ }
+ const uchar *get_data(const uchar *ptr_arg) const
+ {
+ return ptr_arg + length_bytes;
}
uint get_length() const
{
- return length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
+ return get_length(ptr);
+ }
+ uint get_length(const uchar *ptr_arg) const
+ {
+ return length_bytes == 1 ? (uint) *ptr_arg : uint2korr(ptr_arg);
}
protected:
void store_length(uint32 number)
@@ -3663,6 +4098,7 @@ protected:
else
int2store(ptr, number);
}
+ virtual void val_str_from_ptr(String *val, const uchar *ptr) const;
public:
/*
The maximum space available in a Field_varstring, in bytes. See
@@ -3692,66 +4128,87 @@ public:
share->varchar_fields++;
}
- const Type_handler *type_handler() const { return &type_handler_varchar; }
- enum ha_base_keytype key_type() const;
- uint row_pack_length() const { return field_length; }
- bool zero_pack() const { return 0; }
- int reset(void) { bzero(ptr,field_length+length_bytes); return 0; }
- uint32 pack_length() const { return (uint32) field_length+length_bytes; }
- uint32 key_length() const { return (uint32) field_length; }
- uint32 sort_length() const
+ const Type_handler *type_handler() const override
+ { return &type_handler_varchar; }
+ en_fieldtype tmp_engine_column_type(bool use_packed_rows) const override
+ {
+ return FIELD_VARCHAR;
+ }
+ enum ha_base_keytype key_type() const override;
+ uint16 key_part_flag() const override { return HA_VAR_LENGTH_PART; }
+ uint16 key_part_length_bytes() const override { return HA_KEY_BLOB_LENGTH; }
+ uint row_pack_length() const override { return field_length; }
+ bool zero_pack() const override { return false; }
+ int reset() override { bzero(ptr,field_length+length_bytes); return 0; }
+ uint32 pack_length() const override
+ { return (uint32) field_length+length_bytes; }
+ uint32 key_length() const override { return (uint32) field_length; }
+ uint32 sort_length() const override
+ {
+ return (uint32) field_length + sort_suffix_length();
+ }
+ virtual uint32 sort_suffix_length() const override
+ {
+ return (field_charset() == &my_charset_bin ? length_bytes : 0);
+ }
+ Copy_func *get_copy_func(const Field *from) const override;
+ bool memcpy_field_possible(const Field *from) const override;
+ void update_data_type_statistics(Data_type_statistics *st) const override
{
- return (uint32) field_length + (field_charset == &my_charset_bin ?
- length_bytes : 0);
+ st->m_variable_string_count++;
+ st->m_variable_string_total_length+= pack_length();
}
- Copy_func *get_copy_func(const Field *from) const;
- bool memcpy_field_possible(const Field *from) const;
- int store(const char *to,size_t length,CHARSET_INFO *charset);
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
using Field_str::store;
#ifdef HAVE_valgrind
- void mark_unused_memory_as_defined();
+ void mark_unused_memory_as_defined() override;
#endif
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- my_decimal *val_decimal(my_decimal *);
- int cmp(const uchar *a,const uchar *b);
- int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len);
- void sort_string(uchar *buff,uint length);
- uint get_key_image(uchar *buff,uint length, imagetype type);
- void set_key_image(const uchar *buff,uint length);
- void sql_type(String &str) const;
- void sql_rpl_type(String*) const;
- virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
- virtual const uchar *unpack(uchar* to, const uchar *from,
- const uchar *from_end, uint param_data);
- int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U);
- int key_cmp(const uchar *,const uchar*);
- int key_cmp(const uchar *str, uint length);
- uint packed_col_length(const uchar *to, uint length);
- uint max_packed_col_length(uint max_length);
- uint32 data_length();
- uint size_of() const { return sizeof(*this); }
- bool has_charset(void) const
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ bool send(Protocol *protocol) override;
+ int cmp(const uchar *a,const uchar *b) const override;
+ int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len) const
+ override;
+ void sort_string(uchar *buff,uint length) override;
+ uint get_key_image(uchar *buff, uint length,
+ const uchar *ptr_arg, imagetype type) const override;
+ void set_key_image(const uchar *buff,uint length) override;
+ void sql_type(String &str) const override;
+ void sql_rpl_type(String*) const override;
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override;
+ const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
+ uint param_data) override;
+ int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U) const
+ override;
+ int key_cmp(const uchar *,const uchar*) const override;
+ int key_cmp(const uchar *str, uint length) const override;
+ uint packed_col_length(const uchar *to, uint length) override;
+ uint max_packed_col_length(uint max_length) override;
+ uint32 data_length() override;
+ uint size_of() const override { return sizeof *this; }
+ bool has_charset() const override
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
- Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
+ Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type)
+ override;
Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
- uchar *new_null_ptr, uint new_null_bit);
- bool is_equal(const Column_definition &new_field) const;
+ uchar *new_null_ptr, uint new_null_bit) override;
+ bool is_equal(const Column_definition &new_field) const override;
bool can_be_converted_by_engine(const Column_definition &new_type) const
+ override
{
return table->file->can_convert_varstring(this, new_type);
}
- void hash(ulong *nr, ulong *nr2);
- uint length_size() const { return length_bytes; }
- void print_key_value(String *out, uint32 length);
-private:
- int save_field_metadata(uchar *first_byte);
+ void hash(ulong *nr, ulong *nr2) override;
+ uint length_size() const override { return length_bytes; }
+ void print_key_value(String *out, uint32 length) override;
+ Binlog_type_info binlog_type_info() const override;
};
-class Field_varstring_compressed: public Field_varstring {
+class Field_varstring_compressed final :public Field_varstring {
public:
Field_varstring_compressed(uchar *ptr_arg,
uint32 len_arg, uint length_bytes_arg,
@@ -3764,38 +4221,50 @@ public:
null_bit_arg, unireg_check_arg, field_name_arg,
share, collation),
compression_method_ptr(compression_method_arg) { DBUG_ASSERT(len_arg > 0); }
- Compression_method *compression_method() const
+ Compression_method *compression_method() const override
{ return compression_method_ptr; }
private:
Compression_method *compression_method_ptr;
- int store(const char *to, size_t length, CHARSET_INFO *charset);
+ void val_str_from_ptr(String *val, const uchar *ptr) const override;
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
using Field_str::store;
- String *val_str(String *, String *);
- double val_real(void);
- longlong val_int(void);
- uint size_of() const { return sizeof(*this); }
- enum_field_types binlog_type() const { return MYSQL_TYPE_VARCHAR_COMPRESSED; }
- void sql_type(String &str) const
+ String *val_str(String *, String *) override;
+ double val_real() override;
+ longlong val_int() override;
+ uint size_of() const override { return sizeof *this; }
+ /*
+ We use the default Field::send() implementation,
+ because the derived optimized version (from Field_longstr)
+ is not suitable for compressed fields.
+ */
+ bool send(Protocol *protocol) override
+ {
+ return Field::send(protocol);
+ }
+ enum_field_types binlog_type() const override
+ { return MYSQL_TYPE_VARCHAR_COMPRESSED; }
+ void sql_type(String &str) const override
{
Field_varstring::sql_type(str);
str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/"));
}
- uint32 max_display_length() const { return field_length - 1; }
- uint32 character_octet_length() const { return field_length - 1; }
- uint32 char_length() const
+ uint32 max_display_length() const override { return field_length - 1; }
+ uint32 character_octet_length() const override { return field_length - 1; }
+ uint32 char_length() const override
{
- return (field_length - 1) / field_charset->mbmaxlen;
+ return (field_length - 1) / mbmaxlen();
}
- int cmp(const uchar *a_ptr, const uchar *b_ptr);
+ int cmp(const uchar *a_ptr, const uchar *b_ptr) const override;
/*
Compressed fields can't have keys as two rows may have different
compression methods or compression levels.
*/
- int key_cmp(const uchar *str, uint length)
+ int key_cmp(const uchar *str, uint length) const override
{ DBUG_ASSERT(0); return 0; }
using Field_varstring::key_cmp;
+ Binlog_type_info binlog_type_info() const override;
};
@@ -3836,6 +4305,30 @@ static inline longlong read_bigendian(const uchar *from, uint bytes)
}
}
+static inline void store_lowendian(ulonglong num, uchar *to, uint bytes)
+{
+ switch(bytes) {
+ case 1: *to= (uchar)num; break;
+ case 2: int2store(to, num); break;
+ case 3: int3store(to, num); break;
+ case 4: int4store(to, num); break;
+ case 8: int8store(to, num); break;
+ default: DBUG_ASSERT(0);
+ }
+}
+
+static inline longlong read_lowendian(const uchar *from, uint bytes)
+{
+ switch(bytes) {
+ case 1: return from[0];
+ case 2: return uint2korr(from);
+ case 3: return uint3korr(from);
+ case 4: return uint4korr(from);
+ case 8: return sint8korr(from);
+ default: DBUG_ASSERT(0); return 0;
+ }
+}
+
extern LEX_CSTRING temp_lex_str;
@@ -3859,6 +4352,7 @@ protected:
static void do_copy_blob(Copy_field *copy);
static void do_conv_blob(Copy_field *copy);
+ uint get_key_image_itRAW(const uchar *ptr_arg, uchar *buff, uint length) const;
public:
Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
@@ -3885,9 +4379,9 @@ public:
:Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, &temp_lex_str,
system_charset_info),
packlength(packlength_arg) {}
- const Type_handler *type_handler() const;
+ const Type_handler *type_handler() const override;
/* Note that the default copy constructor is used, in clone() */
- enum_field_types type() const
+ enum_field_types type() const override
{
/*
We cannot return type_handler()->field_type() here.
@@ -3899,27 +4393,36 @@ public:
*/
return MYSQL_TYPE_BLOB;
}
- enum_field_types real_type() const
+ enum_field_types real_type() const override
{
return MYSQL_TYPE_BLOB;
}
- enum ha_base_keytype key_type() const
+ enum ha_base_keytype key_type() const override
{ return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
- Type_std_attributes type_std_attributes() const
+ uint16 key_part_flag() const override { return HA_BLOB_PART; }
+ uint16 key_part_length_bytes() const override { return HA_KEY_BLOB_LENGTH; }
+ en_fieldtype tmp_engine_column_type(bool use_packed_rows) const override
{
- return Type_std_attributes(Field_blob::max_display_length(), decimals(),
- MY_TEST(flags & UNSIGNED_FLAG),
- dtcollation());
+ return FIELD_BLOB;
+ }
+ Type_numeric_attributes type_numeric_attributes() const override
+ {
+ return Type_numeric_attributes(Field_blob::max_display_length(),
+ decimals(), is_unsigned());
}
Information_schema_character_attributes
- information_schema_character_attributes() const
+ information_schema_character_attributes() const override
{
uint32 octets= Field_blob::character_octet_length();
- uint32 chars= octets / field_charset->mbminlen;
+ uint32 chars= octets / field_charset()->mbminlen;
return Information_schema_character_attributes(octets, chars);
}
- void make_send_field(Send_field *);
- Copy_func *get_copy_func(const Field *from) const
+ void update_data_type_statistics(Data_type_statistics *st) const override
+ {
+ st->m_blob_count++;
+ }
+ void make_send_field(Send_field *) override;
+ Copy_func *get_copy_func(const Field *from) const override
{
/*
TODO: MDEV-9331
@@ -3933,40 +4436,51 @@ public:
return do_copy_blob;
return get_identical_copy_func();
}
- int store_field(Field *from)
+ int store_field(Field *from) override
{ // Be sure the value is stored
+ if (field_charset() == &my_charset_bin &&
+ from->type_handler()->convert_to_binary_using_val_native())
+ {
+ NativeBuffer<64> tmp;
+ from->val_native(&tmp);
+ value.copy(tmp.ptr(), tmp.length(), &my_charset_bin);
+ return store(value.ptr(), value.length(), &my_charset_bin);
+ }
from->val_str(&value);
if (table->copy_blobs ||
(!value.is_alloced() && from->is_varchar_and_in_write_set()))
value.copy();
return store(value.ptr(), value.length(), from->charset());
}
- bool memcpy_field_possible(const Field *from) const
+ bool memcpy_field_possible(const Field *from) const override
{
return Field_str::memcpy_field_possible(from) &&
!compression_method() == !from->compression_method() &&
!table->copy_blobs;
}
- bool make_empty_rec_store_default_value(THD *thd, Item *item);
- int store(const char *to, size_t length, CHARSET_INFO *charset);
+ bool make_empty_rec_store_default_value(THD *thd, Item *item) override;
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
using Field_str::store;
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- my_decimal *val_decimal(my_decimal *);
- int cmp(const uchar *a,const uchar *b);
- int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len);
- int cmp(const uchar *a, uint32 a_length, const uchar *b, uint32 b_length);
- int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U);
- int key_cmp(const uchar *,const uchar*);
- int key_cmp(const uchar *str, uint length);
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ int cmp(const uchar *a, const uchar *b) const override;
+ int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len) const
+ override;
+ int cmp(const uchar *a, uint32 a_length, const uchar *b, uint32 b_length)
+ const;
+ int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0U) const
+ override;
+ int key_cmp(const uchar *,const uchar*) const override;
+ int key_cmp(const uchar *str, uint length) const override;
/* Never update the value of min_val for a blob field */
- bool update_min(Field *min_val, bool force_update) { return FALSE; }
+ bool update_min(Field *min_val, bool force_update) override { 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
+ bool update_max(Field *max_val, bool force_update) override { return false; }
+ uint32 key_length() const override { return 0; }
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override
{ return (uint32) (packlength + portable_sizeof_char_ptr); }
/**
@@ -3979,18 +4493,23 @@ public:
*/
uint32 pack_length_no_ptr() const
{ 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
+ uint row_pack_length() const override { return pack_length_no_ptr(); }
+ uint32 sort_length() const override;
+ uint32 sort_suffix_length() const override;
+ uint32 value_length() override { return get_length(); }
+ virtual uint32 max_data_length() const override
{
return (uint32) (((ulonglong) 1 << (packlength*8)) -1);
}
- int reset(void) { bzero(ptr, packlength+sizeof(uchar*)); return 0; }
- void reset_fields() { bzero((uchar*) &value,sizeof(value)); bzero((uchar*) &read_value,sizeof(read_value)); }
- uint32 get_field_buffer_size(void) { return value.alloced_length(); }
+ int reset() override { bzero(ptr, packlength+sizeof(uchar*)); return 0; }
+ void reset_fields() override
+ {
+ bzero((uchar*) &value, sizeof value);
+ bzero((uchar*) &read_value, sizeof read_value);
+ }
+ uint32 get_field_buffer_size() { return value.alloced_length(); }
void store_length(uchar *i_ptr, uint i_packlength, uint32 i_number);
- inline void store_length(size_t number)
+ void store_length(size_t number)
{
DBUG_ASSERT(number < UINT_MAX32);
store_length(ptr, packlength, (uint32)number);
@@ -4000,11 +4519,11 @@ public:
uint32 get_length(const uchar *ptr, uint packlength) const;
uint32 get_length(const uchar *ptr_arg) const
{ return get_length(ptr_arg, this->packlength); }
- inline uchar *get_ptr() const { return get_ptr(0); }
- inline uchar *get_ptr(my_ptrdiff_t row_offset) const
+ inline uchar *get_ptr() const { return get_ptr(ptr); }
+ inline uchar *get_ptr(const uchar *ptr_arg) const
{
uchar *s;
- memcpy(&s, ptr + packlength + row_offset, sizeof(uchar*));
+ memcpy(&s, ptr_arg + packlength, sizeof(uchar*));
return s;
}
inline void set_ptr(uchar *length, uchar *data)
@@ -4023,12 +4542,17 @@ public:
set_ptr_offset(0, length, data);
}
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);
+ uint get_key_image(uchar *buff, uint length,
+ const uchar *ptr_arg, imagetype type) const override
+ {
+ DBUG_ASSERT(type == itRAW);
+ return get_key_image_itRAW(ptr_arg, buff, length);
+ }
+ void set_key_image(const uchar *buff,uint length) override;
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;
+ uchar *new_null_ptr, uint new_null_bit) override;
+ void sql_type(String &str) const override;
inline bool copy()
{
uchar *tmp= get_ptr();
@@ -4051,12 +4575,12 @@ public:
/* Set value pointer. Lengths are not important */
value.reset((char*) data, 1, 1, &my_charset_bin);
}
- virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
- virtual const uchar *unpack(uchar *to, const uchar *from,
- const uchar *from_end, uint param_data);
- uint packed_col_length(const uchar *col_ptr, uint length);
- uint max_packed_col_length(uint max_length);
- void free()
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override;
+ const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
+ uint param_data) override;
+ uint packed_col_length(const uchar *col_ptr, uint length) override;
+ uint max_packed_col_length(uint max_length) override;
+ void free() override
{
value.free();
read_value.free();
@@ -4076,28 +4600,26 @@ public:
bzero((uchar*) &read_value, sizeof(read_value));
}
}
- uint size_of() const { return sizeof(*this); }
- bool has_charset(void) const
- { return charset() == &my_charset_bin ? FALSE : TRUE; }
- uint32 max_display_length() const;
- uint32 char_length() const;
- uint32 character_octet_length() const;
- bool is_equal(const Column_definition &new_field) const;
+ uint size_of() const override { return sizeof *this; }
+ bool has_charset() const override { return charset() != &my_charset_bin; }
+ uint32 max_display_length() const override;
+ uint32 char_length() const override;
+ uint32 character_octet_length() const override;
+ bool is_equal(const Column_definition &new_field) const override;
bool can_be_converted_by_engine(const Column_definition &new_type) const
+ override
{
return table->file->can_convert_blob(this, new_type);
}
- void print_key_value(String *out, uint32 length);
+ void print_key_value(String *out, uint32 length) override;
+ Binlog_type_info binlog_type_info() const override;
friend void TABLE::remember_blob_values(String *blob_storage);
friend void TABLE::restore_blob_values(String *blob_storage);
-
-private:
- int save_field_metadata(uchar *first_byte);
};
-class Field_blob_compressed: public Field_blob {
+class Field_blob_compressed final :public Field_blob {
public:
Field_blob_compressed(uchar *ptr_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
@@ -4107,18 +4629,28 @@ public:
Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
field_name_arg, share, blob_pack_length, collation),
compression_method_ptr(compression_method_arg) {}
- Compression_method *compression_method() const
+ Compression_method *compression_method() const override
{ return compression_method_ptr; }
private:
Compression_method *compression_method_ptr;
- int store(const char *to, size_t length, CHARSET_INFO *charset);
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
using Field_str::store;
- String *val_str(String *, String *);
- double val_real(void);
- longlong val_int(void);
- uint size_of() const { return sizeof(*this); }
- enum_field_types binlog_type() const { return MYSQL_TYPE_BLOB_COMPRESSED; }
- void sql_type(String &str) const
+ String *val_str(String *, String *) override;
+ double val_real() override;
+ longlong val_int() override;
+ /*
+ We use the default Field::send() implementation,
+ because the derived optimized version (from Field_longstr)
+ is not suitable for compressed fields.
+ */
+ bool send(Protocol *protocol) override
+ {
+ return Field::send(protocol);
+ }
+ uint size_of() const override { return sizeof *this; }
+ enum_field_types binlog_type() const override
+ { return MYSQL_TYPE_BLOB_COMPRESSED; }
+ void sql_type(String &str) const override
{
Field_blob::sql_type(str);
str.append(STRING_WITH_LEN(" /*!100301 COMPRESSED*/"));
@@ -4129,133 +4661,34 @@ private:
compression methods or compression levels.
*/
- uint get_key_image(uchar *buff, uint length, imagetype type_arg)
+ uint get_key_image(uchar *buff, uint length,
+ const uchar *ptr_arg, imagetype type_arg) const override
{ DBUG_ASSERT(0); return 0; }
- void set_key_image(const uchar *buff, uint length)
+ void set_key_image(const uchar *, uint) override
{ DBUG_ASSERT(0); }
- int key_cmp(const uchar *a, const uchar *b)
+ int key_cmp(const uchar *, const uchar *) const override
{ DBUG_ASSERT(0); return 0; }
- int key_cmp(const uchar *str, uint length)
+ int key_cmp(const uchar *, uint) const override
{ DBUG_ASSERT(0); return 0; }
- Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
- uchar *new_ptr, uint32 length,
- uchar *new_null_ptr, uint new_null_bit)
+ Field *new_key_field(MEM_ROOT *, TABLE *, uchar *, uint32, uchar *, uint)
+ override
{ DBUG_ASSERT(0); return 0; }
+ Binlog_type_info binlog_type_info() const override;
};
-#ifdef HAVE_SPATIAL
-class Field_geom :public Field_blob {
-public:
- enum geometry_type geom_type;
- uint srid;
- uint precision;
- enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1};
- enum storage_type storage;
-
- Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
- TABLE_SHARE *share, uint blob_pack_length,
- enum geometry_type geom_type_arg, uint field_srid)
- :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, share, blob_pack_length, &my_charset_bin)
- { geom_type= geom_type_arg; srid= field_srid; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
- const Type_handler *type_handler() const
- {
- return &type_handler_geometry;
- }
- enum_field_types type() const
- {
- return MYSQL_TYPE_GEOMETRY;
- }
- enum_field_types real_type() const
- {
- return MYSQL_TYPE_GEOMETRY;
- }
- Information_schema_character_attributes
- information_schema_character_attributes() const
- {
- return Information_schema_character_attributes();
- }
- void make_send_field(Send_field *to)
- {
- Field_longstr::make_send_field(to);
- }
- bool can_optimize_range(const Item_bool_func *cond,
- const Item *item,
- bool is_eq_func) const;
- void sql_type(String &str) const;
- Copy_func *get_copy_func(const Field *from) const
- {
- if (type_handler() == from->type_handler() &&
- (geom_type == GEOM_GEOMETRY ||
- geom_type == static_cast<const Field_geom*>(from)->geom_type))
- return get_identical_copy_func();
- return do_conv_blob;
- }
- bool memcpy_field_possible(const Field *from) const
- {
- return type_handler() == from->type_handler() &&
- (geom_type == GEOM_GEOMETRY ||
- geom_type == static_cast<const Field_geom*>(from)->geom_type) &&
- !table->copy_blobs;
- }
- bool is_equal(const Column_definition &new_field) const;
- bool can_be_converted_by_engine(const Column_definition &new_type) const
- {
- return table->file->can_convert_geom(this, new_type);
- }
-
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_decimal(const my_decimal *);
- uint size_of() const { return sizeof(*this); }
- /**
- Key length is provided only to support hash joins. (compared byte for byte)
- Ex: SELECT .. FROM t1,t2 WHERE t1.field_geom1=t2.field_geom2.
-
- The comparison is not very relevant, as identical geometry might be
- represented differently, but we need to support it either way.
- */
- uint32 key_length() const { return packlength; }
-
- /**
- Non-nullable GEOMETRY types cannot have defaults,
- but the underlying blob must still be reset.
- */
- int reset(void) { return Field_blob::reset() || !maybe_null(); }
- bool load_data_set_null(THD *thd);
- bool load_data_set_no_data(THD *thd, bool fixed_format);
-
- geometry_type get_geometry_type() const { return geom_type; };
- static geometry_type geometry_type_merge(geometry_type, geometry_type);
- uint get_srid() { return srid; }
- void print_key_value(String *out, uint32 length)
- {
- out->append(STRING_WITH_LEN("unprintable_geometry_value"));
- }
-};
-
-uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields);
-uint gis_field_options_read(const uchar *buf, size_t buf_len,
- Field_geom::storage_type *st_type,uint *precision, uint *scale, uint *srid);
-
-#endif /*HAVE_SPATIAL*/
-
-
class Field_enum :public Field_str {
static void do_field_enum(Copy_field *copy_field);
+ longlong val_int(const uchar *) const;
protected:
uint packlength;
public:
- TYPELIB *typelib;
+ const TYPELIB *typelib;
Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint packlength_arg,
- TYPELIB *typelib_arg,
+ const TYPELIB *typelib_arg,
const DTCollation &collation)
:Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg, collation),
@@ -4263,11 +4696,16 @@ public:
{
flags|=ENUM_FLAG;
}
- Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
- const Type_handler *type_handler() const { return &type_handler_enum; }
- enum ha_base_keytype key_type() const;
- sql_mode_t can_handle_sql_mode_dependency_on_store() const;
- Copy_func *get_copy_func(const Field *from) const
+ Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type)
+ override;
+ const Type_handler *type_handler() const override
+ { return &type_handler_enum; }
+ enum ha_base_keytype key_type() const override;
+ sql_mode_t can_handle_sql_mode_dependency_on_store() const override;
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ Copy_func *get_copy_func(const Field *from) const override
{
if (eq_def(from))
return get_identical_copy_func();
@@ -4278,7 +4716,7 @@ public:
return do_field_string;
return do_field_int;
}
- int store_field(Field *from)
+ int store_field(Field *from) override
{
if (from->real_type() == MYSQL_TYPE_ENUM && from->val_int() == 0)
{
@@ -4287,14 +4725,15 @@ public:
}
return from->save_in_field(this);
}
- int save_in_field(Field *to)
+ int save_in_field(Field *to) override
{
if (to->result_type() != STRING_RESULT)
return to->store(val_int(), 0);
return save_in_field_str(to);
}
- bool memcpy_field_possible(const Field *from) const { return false; }
- void make_empty_rec_reset(THD *thd)
+ bool memcpy_field_possible(const Field *from) const override
+ { return false; }
+ void make_empty_rec_reset(THD *) override
{
if (flags & NOT_NULL_FLAG)
{
@@ -4304,38 +4743,38 @@ public:
else
reset();
}
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
- uint32 pack_length() const { return (uint32) packlength; }
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String *, String *) override;
+ int cmp(const uchar *,const uchar *) const override;
+ void sort_string(uchar *buff,uint length) override;
+ uint32 pack_length() const override { return (uint32) packlength; }
void store_type(ulonglong value);
- void sql_type(String &str) const;
- uint size_of() const { return sizeof(*this); }
- uint pack_length_from_metadata(uint field_metadata)
+ void sql_type(String &str) const override;
+ uint size_of() const override { return sizeof *this; }
+ uint pack_length_from_metadata(uint field_metadata) const override
{ return (field_metadata & 0x00ff); }
- uint row_pack_length() const { return pack_length(); }
- virtual bool zero_pack() const { return 0; }
- bool optimize_range(uint idx, uint part) const { return 0; }
- bool eq_def(const Field *field) const;
- bool has_charset(void) const { return TRUE; }
+ uint row_pack_length() const override { return pack_length(); }
+ bool zero_pack() const override { return false; }
+ bool optimize_range(uint, uint) const override { return false; }
+ bool eq_def(const Field *field) const override;
+ bool has_charset() const override { return true; }
/* enum and set are sorted as integers */
- CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
- uint decimals() const { return 0; }
- TYPELIB *get_typelib() const { return typelib; }
+ CHARSET_INFO *sort_charset() const override { return &my_charset_bin; }
+ uint decimals() const override { return 0; }
+ const TYPELIB *get_typelib() const override { return typelib; }
- virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
- virtual const uchar *unpack(uchar *to, const uchar *from,
- const uchar *from_end, uint param_data);
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override;
+ const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
+ uint param_data) override;
bool can_optimize_keypart_ref(const Item_bool_func *cond,
- const Item *item) const;
- bool can_optimize_group_min_max(const Item_bool_func *cond,
- const Item *const_item) const
+ const Item *item) const override;
+ bool can_optimize_group_min_max(const Item_bool_func *, const Item *)
+ const override
{
/*
Can't use GROUP_MIN_MAX optimization for ENUM and SET,
@@ -4348,20 +4787,20 @@ public:
}
bool can_optimize_range(const Item_bool_func *cond,
const Item *item,
- bool is_eq_func) const;
+ bool is_eq_func) const override;
+ Binlog_type_info binlog_type_info() const override;
private:
- int save_field_metadata(uchar *first_byte);
- bool is_equal(const Column_definition &new_field) const;
+ bool is_equal(const Column_definition &new_field) const override;
};
-class Field_set :public Field_enum {
+class Field_set final :public Field_enum {
public:
Field_set(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
uint32 packlength_arg,
- TYPELIB *typelib_arg, const DTCollation &collation)
+ const TYPELIB *typelib_arg, const DTCollation &collation)
:Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
unireg_check_arg, field_name_arg,
packlength_arg,
@@ -4370,22 +4809,25 @@ public:
{
flags=(flags & ~ENUM_FLAG) | SET_FLAG;
}
- void make_empty_rec_reset(THD *thd)
+ void make_empty_rec_reset(THD *thd) override
{
Field::make_empty_rec_reset(thd);
}
- int store_field(Field *from) { return from->save_in_field(this); }
- int store(const char *to,size_t length,CHARSET_INFO *charset);
- int store(double nr) { return Field_set::store((longlong) nr, FALSE); }
- int store(longlong nr, bool unsigned_val);
-
- virtual bool zero_pack() const { return 1; }
- String *val_str(String*,String *);
- void sql_type(String &str) const;
- uint size_of() const { return sizeof(*this); }
- const Type_handler *type_handler() const { return &type_handler_set; }
- bool has_charset(void) const { return TRUE; }
+ int store_field(Field *from) override { return from->save_in_field(this); }
+ int store(const char *to,size_t length,CHARSET_INFO *charset) override;
+ int store(double nr) override
+ { return Field_set::store((longlong) nr, FALSE); }
+ int store(longlong nr, bool unsigned_val) override;
+
+ bool zero_pack() const override { return true; }
+ String *val_str(String *, String *) override;
+ void sql_type(String &str) const override;
+ uint size_of() const override { return sizeof *this; }
+ const Type_handler *type_handler() const override
+ { return &type_handler_set; }
+ bool has_charset() const override { return true; }
+ Binlog_type_info binlog_type_info() const override;
private:
const String empty_set_string;
};
@@ -4414,42 +4856,55 @@ public:
Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg);
- const Type_handler *type_handler() const { return &type_handler_bit; }
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; }
- uint32 key_length() const { return (uint32) (field_length + 7) / 8; }
- uint32 max_data_length() const { return (field_length + 7) / 8; }
- uint32 max_display_length() const { return field_length; }
+ const Type_handler *type_handler() const override
+ { return &type_handler_bit; }
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BIT; }
+ uint16 key_part_flag() const override { return HA_BIT_PART; }
+ uint32 key_length() const override
+ { return (uint32) (field_length + 7) / 8; }
+ uint32 max_data_length() const override { return key_length(); }
+ uint32 max_display_length() const override { return field_length; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ CHARSET_INFO *charset() const override { return &my_charset_bin; }
+ const DTCollation & dtcollation() const override;
Information_schema_numeric_attributes
- information_schema_numeric_attributes() const
+ information_schema_numeric_attributes() const override
{
return Information_schema_numeric_attributes(field_length);
}
- uint size_of() const { return sizeof(*this); }
- int reset(void) {
+ void update_data_type_statistics(Data_type_statistics *st) const override
+ {
+ st->m_uneven_bit_length+= field_length & 7;
+ }
+ uint size_of() const override { return sizeof *this; }
+ int reset() override
+ {
bzero(ptr, bytes_in_rec);
if (bit_ptr && (bit_len > 0)) // reset odd bits among null bits
clr_rec_bits(bit_ptr, bit_ofs, bit_len);
return 0;
}
- Copy_func *get_copy_func(const Field *from) const
+ Copy_func *get_copy_func(const Field *from) const override
{
if (from->cmp_type() == DECIMAL_RESULT)
return do_field_decimal;
return do_field_int;
}
- int save_in_field(Field *to) { return to->store(val_int(), true); }
- bool memcpy_field_possible(const Field *from) const { return false; }
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_decimal(const my_decimal *);
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*, String *);
- virtual bool str_needs_quotes() { return TRUE; }
- my_decimal *val_decimal(my_decimal *);
- bool val_bool() { return val_int() != 0; }
- int cmp(const uchar *a, const uchar *b)
+ int save_in_field(Field *to) override { return to->store(val_int(), true); }
+ bool memcpy_field_possible(const Field *from) const override{ return false; }
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_decimal(const my_decimal *) override;
+ double val_real() override;
+ longlong val_int() override;
+ String *val_str(String*, String *) override;
+ bool str_needs_quotes() const override { return true; }
+ my_decimal *val_decimal(my_decimal *) override;
+ bool val_bool() override { return val_int() != 0; }
+ int cmp(const uchar *a, const uchar *b) const override
{
DBUG_ASSERT(ptr == a || ptr == b);
if (ptr == a)
@@ -4457,15 +4912,16 @@ public:
else
return Field_bit::key_cmp(a, bytes_in_rec + MY_TEST(bit_len)) * -1;
}
- int cmp_binary_offset(uint row_offset)
+ int cmp_binary_offset(uint row_offset) override
{ return cmp_offset(row_offset); }
- int cmp_prefix(const uchar *a, const uchar *b, size_t prefix_len);
- int key_cmp(const uchar *a, const uchar *b)
+ int cmp_prefix(const uchar *a, const uchar *b,
+ size_t max_length) const override;
+ int key_cmp(const uchar *a, const uchar *b) const override
{ return cmp_binary((uchar *) a, (uchar *) b); }
- int key_cmp(const uchar *str, uint length);
- int cmp_offset(my_ptrdiff_t row_offset);
- bool update_min(Field *min_val, bool force_update)
- {
+ int key_cmp(const uchar *str, uint length) const override;
+ int cmp_offset(my_ptrdiff_t row_offset) override;
+ bool update_min(Field *min_val, bool force_update) override
+ {
longlong val= val_int();
bool update_fl= force_update || val < min_val->val_int();
if (update_fl)
@@ -4475,8 +4931,8 @@ public:
}
return update_fl;
}
- bool update_max(Field *max_val, bool force_update)
- {
+ bool update_max(Field *max_val, bool force_update) override
+ {
longlong val= val_int();
bool update_fl= force_update || val > max_val->val_int();
if (update_fl)
@@ -4486,72 +4942,97 @@ public:
}
return update_fl;
}
- void store_field_value(uchar *val, uint len)
+ void store_field_value(uchar *val, uint) override
{
store(*((longlong *)val), TRUE);
}
- double pos_in_interval(Field *min, Field *max)
+ double pos_in_interval(Field *min, Field *max) override
{
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)
+ void get_image(uchar *buff, uint length,
+ const uchar *ptr_arg, CHARSET_INFO *cs) const override
+ { get_key_image(buff, length, ptr_arg, itRAW); }
+ void set_image(const uchar *buff,uint length, CHARSET_INFO *cs) override
{ Field_bit::store((char *) buff, length, cs); }
- uint get_key_image(uchar *buff, uint length, imagetype type);
- void set_key_image(const uchar *buff, uint length)
+ uint get_key_image(uchar *buff, uint length,
+ const uchar *ptr_arg, imagetype type) const override;
+ void set_key_image(const uchar *buff, uint length) override
{ Field_bit::store((char*) buff, length, &my_charset_bin); }
- void sort_string(uchar *buff, uint length)
- { get_key_image(buff, length, itRAW); }
- uint32 pack_length() const { return (uint32) (field_length + 7) / 8; }
- uint32 pack_length_in_rec() const { return bytes_in_rec; }
- uint pack_length_from_metadata(uint field_metadata);
- uint row_pack_length() const
+ void sort_string(uchar *buff, uint length) override
+ { get_key_image(buff, length, ptr, itRAW); }
+ uint32 pack_length() const override
+ { return (uint32) (field_length + 7) / 8; }
+ uint32 pack_length_in_rec() const override { return bytes_in_rec; }
+ uint pack_length_from_metadata(uint field_metadata) const override;
+ uint row_pack_length() const override
{ return (bytes_in_rec + ((bit_len > 0) ? 1 : 0)); }
- bool compatible_field_size(uint metadata, Relay_log_info *rli,
- uint16 mflags, int *order_var);
- void sql_type(String &str) const;
- virtual uchar *pack(uchar *to, const uchar *from, uint max_length);
- virtual const uchar *unpack(uchar *to, const uchar *from,
- const uchar *from_end, uint param_data);
- virtual int set_default();
+ bool compatible_field_size(uint metadata, const Relay_log_info *rli,
+ uint16 mflags, int *order_var) const override;
+ void sql_type(String &str) const override;
+ uchar *pack(uchar *to, const uchar *from, uint max_length) override;
+ const uchar *unpack(uchar *to, const uchar *from,
+ const uchar *from_end, uint param_data) override;
+ int set_default() override;
Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
uchar *new_ptr, uint32 length,
- uchar *new_null_ptr, uint new_null_bit);
+ uchar *new_null_ptr, uint new_null_bit) override;
void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg)
{
bit_ptr= bit_ptr_arg;
bit_ofs= bit_ofs_arg;
}
- bool eq(Field *field)
+ bool eq(Field *field) override
{
return (Field::eq(field) &&
bit_ptr == ((Field_bit *)field)->bit_ptr &&
bit_ofs == ((Field_bit *)field)->bit_ofs);
}
- bool is_equal(const Column_definition &new_field) const;
- void move_field_offset(my_ptrdiff_t ptr_diff)
+ bool is_equal(const Column_definition &new_field) const override;
+ void move_field_offset(my_ptrdiff_t ptr_diff) override
{
Field::move_field_offset(ptr_diff);
bit_ptr= ADD_TO_PTR(bit_ptr, ptr_diff, uchar*);
}
- void hash(ulong *nr, ulong *nr2);
+ void hash(ulong *nr, ulong *nr2) override;
SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, KEY_PART *key_part,
const Item_bool_func *cond,
- scalar_comparison_op op, Item *value)
+ scalar_comparison_op op, Item *value) override
{
return get_mm_leaf_int(param, key_part, cond, op, value, true);
}
- void print_key_value(String *out, uint32 length)
+ void print_key_value(String *out, uint32 length) override
{
val_int_as_str(out, 1);
}
+ /**
+ Save the field metadata for bit fields.
+ Saves the bit length in the first byte and bytes in record in the
+ second byte of the field metadata array at index of *metadata_ptr and
+ *(metadata_ptr + 1).
+
+ @param metadata_ptr First byte of field metadata
+
+ @returns number of bytes written to metadata_ptr
+ */
+ Binlog_type_info binlog_type_info() const override
+ {
+ DBUG_PRINT("debug", ("bit_len: %d, bytes_in_rec: %d",
+ bit_len, bytes_in_rec));
+ /*
+ Since this class and Field_bit_as_char have different ideas of
+ what should be stored here, we compute the values of the metadata
+ explicitly using the field_length.
+ */
+ return Binlog_type_info(type(),
+ static_cast<uint16>((field_length & 7) |
+ ((field_length / 8) << 8)), 2);
+ }
private:
- virtual size_t do_last_null_byte() const;
- int save_field_metadata(uchar *first_byte);
+ size_t do_last_null_byte() const override;
};
@@ -4562,22 +5043,22 @@ private:
an extended version of Field_bit_as_char and not the other way
around. Hence, we should refactor it to fix the hierarchy order.
*/
-class Field_bit_as_char: public Field_bit {
+class Field_bit_as_char final :public Field_bit {
public:
Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg,
enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg);
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- uint size_of() const { return sizeof(*this); }
- int store(const char *to, size_t length, CHARSET_INFO *charset);
- int store(double nr) { return Field_bit::store(nr); }
- int store(longlong nr, bool unsigned_val)
+ enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; }
+ uint size_of() const override { return sizeof *this; }
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
+ int store(double nr) override { return Field_bit::store(nr); }
+ int store(longlong nr, bool unsigned_val) override
{ return Field_bit::store(nr, unsigned_val); }
- void sql_type(String &str) const;
+ void sql_type(String &str) const override;
};
-class Field_row: public Field_null
+class Field_row final :public Field_null
{
class Virtual_tmp_table *m_table;
public:
@@ -4586,6 +5067,18 @@ public:
m_table(NULL)
{}
~Field_row();
+ en_fieldtype tmp_engine_column_type(bool use_packed_rows) const
+ {
+ DBUG_ASSERT(0);
+ return Field::tmp_engine_column_type(use_packed_rows);
+ }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+ {
+ DBUG_ASSERT(0);
+ return CONV_TYPE_IMPOSSIBLE;
+ }
Virtual_tmp_table **virtual_tmp_table_addr() { return &m_table; }
bool sp_prepare_and_store_item(THD *thd, Item **value);
};
@@ -4601,22 +5094,23 @@ public:
max number of characters.
*/
ulonglong length;
+ uint decimals;
Field::utype unireg_check;
- TYPELIB *interval; // Which interval to use
+ const TYPELIB *interval; // Which interval to use
CHARSET_INFO *charset;
uint32 srid;
- Field::geometry_type geom_type;
uint pack_flag;
Column_definition_attributes()
:length(0),
+ decimals(0),
unireg_check(Field::NONE),
interval(NULL),
charset(&my_charset_bin),
srid(0),
- geom_type(Field::GEOM_GEOMETRY),
pack_flag(0)
{ }
Column_definition_attributes(const Field *field);
+ Column_definition_attributes(const Type_all_attributes &attr);
Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root,
const Record_addr *rec,
const Type_handler *handler,
@@ -4629,8 +5123,13 @@ public:
uint pack_flag_to_pack_length() const;
void frm_pack_basic(uchar *buff) const;
void frm_pack_charset(uchar *buff) const;
+ void frm_pack_numeric_with_dec(uchar *buff) const;
void frm_unpack_basic(const uchar *buff);
bool frm_unpack_charset(TABLE_SHARE *share, const uchar *buff);
+ bool frm_unpack_numeric_with_dec(TABLE_SHARE *share, const uchar *buff);
+ bool frm_unpack_temporal_with_dec(TABLE_SHARE *share, uint intlen,
+ const uchar *buff);
+ void set_length_and_dec(const Lex_length_and_dec_st &attr);
};
@@ -4674,7 +5173,7 @@ class Column_definition: public Sql_alloc,
for (pos= interval->type_names, len= interval->type_lengths;
*pos ; pos++, len++)
{
- size_t length= charset->cset->numchars(charset, *pos, *pos + *len);
+ size_t length= charset->numchars(*pos, *pos + *len);
DBUG_ASSERT(length < UINT_MAX32);
*tot_length+= (uint) length;
set_if_bigger(*max_length, (uint32)length);
@@ -4700,7 +5199,7 @@ public:
for most of the types, or of bytes for BLOBs or numeric types.
*/
uint32 char_length;
- uint decimals, flags, pack_length, key_length;
+ uint flags, pack_length;
List<String> interval_list;
engine_option_value *option_list;
@@ -4723,8 +5222,8 @@ public:
:Type_handler_hybrid_field_type(&type_handler_null),
compression_method_ptr(0),
comment(null_clex_str),
- on_update(NULL), invisible(VISIBLE), char_length(0), decimals(0),
- flags(0), pack_length(0), key_length(0),
+ on_update(NULL), invisible(VISIBLE), char_length(0),
+ flags(0), pack_length(0),
option_list(NULL),
vcol_info(0), default_value(0), check_constraint(0),
versioning(VERSIONING_NOT_SET), period(NULL)
@@ -4733,15 +5232,18 @@ public:
}
Column_definition(THD *thd, Field *field, Field *orig_field);
- void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs);
+ bool set_attributes(THD *thd,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type);
void create_length_to_internal_length_null()
{
DBUG_ASSERT(length == 0);
- key_length= pack_length= 0;
+ pack_length= 0;
}
void create_length_to_internal_length_simple()
{
- key_length= pack_length= type_handler()->calc_pack_length((uint32) length);
+ pack_length= type_handler()->calc_pack_length((uint32) length);
}
void create_length_to_internal_length_string()
{
@@ -4749,14 +5251,12 @@ public:
if (real_field_type() == MYSQL_TYPE_VARCHAR && compression_method())
length++;
set_if_smaller(length, UINT_MAX32);
- key_length= (uint) length;
pack_length= type_handler()->calc_pack_length((uint32) length);
}
void create_length_to_internal_length_typelib()
{
/* Pack_length already calculated in sql_parse.cc */
length*= charset->mbmaxlen;
- key_length= pack_length;
}
bool vers_sys_field() const
{
@@ -4824,7 +5324,7 @@ public:
bool prepare_stage2_varchar(ulonglong table_flags);
bool prepare_stage2_typelib(const char *type_name, uint field_flags,
uint *dup_val_count);
- uint pack_flag_numeric(uint dec) const;
+ uint pack_flag_numeric() const;
uint sign_length() const { return flags & UNSIGNED_FLAG ? 0 : 1; }
bool check_length(uint mysql_errno, uint max_allowed_length) const;
bool fix_attributes_real(uint default_length);
@@ -4886,12 +5386,10 @@ public:
decimals= other.decimals;
flags= other.flags;
pack_length= other.pack_length;
- key_length= other.key_length;
unireg_check= other.unireg_check;
interval= other.interval;
charset= other.charset;
srid= other.srid;
- geom_type= other.geom_type;
pack_flag= other.pack_flag;
}
@@ -4909,6 +5407,8 @@ public:
{ compression_method_ptr= compression_method_arg; }
Compression_method *compression_method() const
{ return compression_method_ptr; }
+
+ bool check_vcol_for_key(THD *thd) const;
};
@@ -5085,7 +5585,7 @@ public:
LEX_CSTRING change; // If done with alter table
LEX_CSTRING after; // Put column after this one
Field *field; // For alter table
- TYPELIB *save_interval; // Temporary copy for the above
+ const TYPELIB *save_interval; // Temporary copy for the above
// Used only for UCS2 intervals
/** structure with parsed options (for comparing fields in ALTER TABLE) */
@@ -5119,25 +5619,25 @@ public:
*/
class Send_field :public Sql_alloc,
- public Type_handler_hybrid_field_type
+ public Type_handler_hybrid_field_type,
+ public Send_field_extended_metadata
{
public:
- const char *db_name;
- const char *table_name,*org_table_name;
+ LEX_CSTRING db_name;
+ LEX_CSTRING table_name, org_table_name;
LEX_CSTRING col_name, org_col_name;
ulong length;
uint flags, decimals;
- Send_field() {}
Send_field(Field *field)
{
field->make_send_field(this);
- DBUG_ASSERT(table_name != 0);
+ DBUG_ASSERT(table_name.str != 0);
normalize();
}
Send_field(THD *thd, Item *item);
Send_field(Field *field,
- const char *db_name_arg,
- const char *table_name_arg)
+ const LEX_CSTRING &db_name_arg,
+ const LEX_CSTRING &table_name_arg)
:Type_handler_hybrid_field_type(field->type_handler()),
db_name(db_name_arg),
table_name(table_name_arg),
@@ -5165,9 +5665,9 @@ public:
uint32 max_char_length(CHARSET_INFO *cs) const
{
return type_handler()->field_type() >= MYSQL_TYPE_TINY_BLOB &&
- type_handler()->field_type() <= MYSQL_TYPE_BLOB ?
- length / cs->mbminlen :
- length / cs->mbmaxlen;
+ type_handler()->field_type() <= MYSQL_TYPE_BLOB
+ ? static_cast<uint32>(length / cs->mbminlen)
+ : static_cast<uint32>(length / cs->mbmaxlen);
}
uint32 max_octet_length(CHARSET_INFO *from, CHARSET_INFO *to) const
{
@@ -5192,12 +5692,27 @@ public:
}
// This should move to Type_handler eventually
- bool is_sane() const
+ bool is_sane_float() const
{
return (decimals <= FLOATING_POINT_DECIMALS ||
(type_handler()->field_type() != MYSQL_TYPE_FLOAT &&
type_handler()->field_type() != MYSQL_TYPE_DOUBLE));
}
+ bool is_sane_signess() const
+ {
+ if (type_handler() == type_handler()->type_handler_signed() &&
+ type_handler() == type_handler()->type_handler_unsigned())
+ return true; // Any signess is allowed, e.g. DOUBLE, DECIMAL
+ /*
+ We are here e.g. in case of INT data type.
+ The UNSIGNED_FLAG bit must match in flags and in the type handler.
+ */
+ return ((bool) (flags & UNSIGNED_FLAG)) == type_handler()->is_unsigned();
+ }
+ bool is_sane() const
+ {
+ return is_sane_float() && is_sane_signess();
+ }
};
@@ -5246,8 +5761,8 @@ enum_field_types get_blob_type_from_length(ulong length);
int set_field_to_null(Field *field);
int set_field_to_null_with_conversions(Field *field, bool no_conversions);
int convert_null_to_field_value_or_error(Field *field);
-bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
- enum_vcol_info_type type);
+bool check_expression(Virtual_column_info *vcol, const LEX_CSTRING *name,
+ enum_vcol_info_type type, Alter_info *alter_info= NULL);
/*
The following are for the interface with the .frm file
@@ -5272,6 +5787,8 @@ bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name,
#define FIELDFLAG_DEC_SHIFT 8
#define FIELDFLAG_MAX_DEC 63U
+#define FIELDFLAG_DEC_MASK 0x3F00U
+
#define MTYP_TYPENR(type) ((type) & 127U) // Remove bits from type
#define f_is_dec(x) ((x) & FIELDFLAG_DECIMAL)
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 7ec93e032e6..ff6d60e7626 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, MariaDB Corporation
+ Copyright (c) 2010, 2020, MariaDB Corporation
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,7 +30,7 @@
#include "sql_class.h" // THD
#include <m_ctype.h>
-static void do_field_eq(Copy_field *copy)
+void Field::do_field_eq(Copy_field *copy)
{
memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
}
@@ -168,7 +168,7 @@ int convert_null_to_field_value_or_error(Field *field)
{
if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
return 0;
}
@@ -315,7 +315,7 @@ static void do_copy_timestamp(Copy_field *copy)
if (*copy->from_null_ptr & copy->from_bit)
{
/* Same as in set_field_to_null_with_conversions() */
- ((Field_timestamp*) copy->to_field)->set_time();
+ copy->to_field->set_time();
}
else
(copy->do_copy2)(copy);
@@ -465,10 +465,9 @@ static void do_cut_string(Copy_field *copy)
memcpy(copy->to_ptr,copy->from_ptr,copy->to_length);
/* Check if we loosed any important characters */
- if (cs->cset->scan(cs,
- (char*) copy->from_ptr + copy->to_length,
- (char*) copy->from_ptr + copy->from_length,
- MY_SEQ_SPACES) < copy->from_length - copy->to_length)
+ if (cs->scan((char*) copy->from_ptr + copy->to_length,
+ (char*) copy->from_ptr + copy->from_length,
+ MY_SEQ_SPACES) < copy->from_length - copy->to_length)
{
copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
@@ -496,9 +495,9 @@ static void do_cut_string_complex(Copy_field *copy)
/* Check if we lost any important characters */
if (unlikely(prefix.well_formed_error_pos() ||
- cs->cset->scan(cs, (char*) copy->from_ptr + copy_length,
- (char*) from_end,
- MY_SEQ_SPACES) <
+ cs->scan((char*) copy->from_ptr + copy_length,
+ (char*) from_end,
+ MY_SEQ_SPACES) <
(copy->from_length - copy_length)))
{
copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
@@ -506,8 +505,8 @@ static void do_cut_string_complex(Copy_field *copy)
}
if (copy_length < copy->to_length)
- cs->cset->fill(cs, (char*) copy->to_ptr + copy_length,
- copy->to_length - copy_length, ' ');
+ cs->fill((char*) copy->to_ptr + copy_length,
+ copy->to_length - copy_length, ' ');
}
@@ -517,8 +516,8 @@ static void do_expand_binary(Copy_field *copy)
{
CHARSET_INFO *cs= copy->from_field->charset();
memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
- cs->cset->fill(cs, (char*) copy->to_ptr+copy->from_length,
- copy->to_length-copy->from_length, '\0');
+ cs->fill((char*) copy->to_ptr+copy->from_length,
+ copy->to_length-copy->from_length, '\0');
}
@@ -527,8 +526,8 @@ static void do_expand_string(Copy_field *copy)
{
CHARSET_INFO *cs= copy->from_field->charset();
memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
- cs->cset->fill(cs, (char*) copy->to_ptr+copy->from_length,
- copy->to_length-copy->from_length, ' ');
+ cs->fill((char*) copy->to_ptr+copy->from_length,
+ copy->to_length-copy->from_length, ' ');
}
@@ -638,7 +637,7 @@ void Copy_field::set(uchar *to,Field *from)
else
{
to_null_ptr= 0; // For easy debugging
- do_copy= do_field_eq;
+ do_copy= Field::do_field_eq;
}
}
@@ -719,7 +718,7 @@ void Copy_field::set(Field *to,Field *from,bool save)
if ((to->flags & BLOB_FLAG) && save)
do_copy2= do_save_blob;
else
- do_copy2= to->get_copy_func(from);
+ do_copy2= from->get_copy_func_to(to);
if (!do_copy) // Not null
do_copy=do_copy2;
}
@@ -786,7 +785,7 @@ Field::Copy_func *Field_string::get_copy_func(const Field *from) const
{
if (from->type() == MYSQL_TYPE_BIT)
return do_field_int;
- if (Field_string::real_type() != from->real_type() ||
+ if (Field_string::type_handler() != from->type_handler() ||
Field_string::charset() != from->charset())
return do_field_string;
if (Field_string::pack_length() < from->pack_length())
diff --git a/sql/filesort.cc b/sql/filesort.cc
index aa25474be1a..b261f089313 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -48,25 +48,54 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
ha_rows *found_rows);
static bool write_keys(Sort_param *param, SORT_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 uint make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos,
+ bool using_packed_sortkeys= false);
+static uint make_sortkey(Sort_param *param, uchar *to);
+static uint make_packed_sortkey(Sort_param *param, uchar *to);
+
static void register_used_fields(Sort_param *param);
static bool save_index(Sort_param *param, uint count,
SORT_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(TABLE *table, uint sortlength,
- LEX_STRING *addon_buf);
-static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
- uchar *buff, uchar *buff_end);
+static uint sortlength(THD *thd, Sort_keys *sortorder,
+ bool *allow_packing_for_sortkeys);
+static Addon_fields *get_addon_fields(TABLE *table, uint sortlength,
+ uint *addon_length,
+ uint *m_packable_length);
+
static bool check_if_pq_applicable(Sort_param *param, SORT_INFO *info,
TABLE *table,
ha_rows records, size_t memory_available);
+static void store_key_part_length(uint32 num, uchar *to, uint bytes)
+{
+ switch(bytes) {
+ case 1: *to= (uchar)num; break;
+ case 2: int2store(to, num); break;
+ case 3: int3store(to, num); break;
+ case 4: int4store(to, num); break;
+ default: DBUG_ASSERT(0);
+ }
+}
+
+
+static uint32 read_keypart_length(const uchar *from, uint bytes)
+{
+ switch(bytes) {
+ case 1: return from[0];
+ case 2: return uint2korr(from);
+ case 3: return uint3korr(from);
+ case 4: return uint4korr(from);
+ default: DBUG_ASSERT(0); return 0;
+ }
+}
+
+
+// @param sortlen [Maximum] length of the sort key
void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
ha_rows maxrows, bool sort_positions)
{
- DBUG_ASSERT(addon_field == 0 && addon_buf.length == 0);
+ DBUG_ASSERT(addon_fields == NULL);
sort_length= sortlen;
ref_length= table->file->ref_length;
@@ -77,12 +106,13 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
Get the descriptors of all fields whose values are appended
to sorted fields and get its total length in addon_buf.length
*/
- addon_field= get_addon_fields(table, sort_length, &addon_buf);
+ addon_fields= get_addon_fields(table, sort_length, &addon_length,
+ &m_packable_length);
}
- if (addon_field)
+ if (using_addon_fields())
{
- DBUG_ASSERT(addon_buf.length < UINT_MAX32);
- res_length= (uint)addon_buf.length;
+ DBUG_ASSERT(addon_length < UINT_MAX32);
+ res_length= addon_length;
}
else
{
@@ -93,11 +123,42 @@ void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
*/
sort_length+= ref_length;
}
- rec_length= sort_length + (uint)addon_buf.length;
+ rec_length= sort_length + addon_length;
max_rows= maxrows;
}
+void Sort_param::try_to_pack_addons(ulong max_length_for_sort_data)
+{
+ if (!using_addon_fields() || // no addons, or
+ using_packed_addons()) // already packed
+ return;
+
+ if (!Addon_fields::can_pack_addon_fields(res_length))
+ return;
+
+ const uint sz= Addon_fields::size_of_length_field;
+
+ // Heuristic: skip packing if potential savings are less than 10 bytes.
+ if (m_packable_length < (10 + sz))
+ return;
+
+ SORT_ADDON_FIELD *addonf= addon_fields->begin();
+ for (;addonf != addon_fields->end(); ++addonf)
+ {
+ addonf->offset+= sz;
+ addonf->null_offset+= sz;
+ }
+
+ addon_fields->set_using_packed_addons(true);
+ m_using_packed_addons= true;
+ m_packed_format= true;
+
+ addon_length+= sz;
+ res_length+= sz;
+ rec_length+= sz;
+}
+
/**
Sort a table.
Creates a set of pointers that can be used to read the rows
@@ -134,24 +195,27 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
DBUG_ASSERT(thd->variables.sortbuff_size <= SIZE_T_MAX);
size_t memory_available= (size_t)thd->variables.sortbuff_size;
uint maxbuffer;
- BUFFPEK *buffpek;
+ Merge_chunk *buffpek;
ha_rows num_rows= HA_POS_ERROR;
IO_CACHE tempfile, buffpek_pointers, *outfile;
Sort_param param;
- bool multi_byte_charset;
+ bool allow_packing_for_sortkeys;
Bounded_queue<uchar, uchar> pq;
SQL_SELECT *const select= filesort->select;
ha_rows max_rows= filesort->limit;
uint s_length= 0;
+ Sort_keys *sort_keys;
DBUG_ENTER("filesort");
- if (!(s_length= filesort->make_sortorder(thd, join, first_table_bit)))
+ if (!(sort_keys= filesort->make_sortorder(thd, join, first_table_bit)))
DBUG_RETURN(NULL); /* purecov: inspected */
- DBUG_EXECUTE("info",TEST_filesort(filesort->sortorder,s_length););
+ s_length= static_cast<uint>(sort_keys->size());
+
+ DBUG_EXECUTE("info",TEST_filesort(filesort->sortorder, s_length););
#ifdef SKIP_DBUG_IN_FILESORT
- DBUG_PUSH(""); /* No DBUG here */
+ DBUG_PUSH_EMPTY; /* No DBUG here */
#endif
SORT_INFO *sort;
TABLE_LIST *tab= table->pos_in_table_list;
@@ -164,13 +228,16 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
if (subselect && subselect->filesort_buffer.is_allocated())
{
- /* Reuse cache from last call */
+ // Reuse cache from last call
sort->filesort_buffer= subselect->filesort_buffer;
sort->buffpek= subselect->sortbuffer;
subselect->filesort_buffer.reset();
subselect->sortbuffer.str=0;
}
+ DBUG_ASSERT(sort->sorted_result_in_fsbuf == FALSE ||
+ sort->record_pointers == NULL);
+
outfile= &sort->io_cache;
my_b_clear(&tempfile);
@@ -179,24 +246,20 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
error= 1;
sort->found_rows= HA_POS_ERROR;
- param.init_for_filesort(sortlength(thd, filesort->sortorder, s_length,
- &multi_byte_charset),
- table, max_rows, filesort->sort_positions);
+ param.sort_keys= sort_keys;
+ uint sort_len= sortlength(thd, sort_keys, &allow_packing_for_sortkeys);
- sort->addon_buf= param.addon_buf;
- sort->addon_field= param.addon_field;
- sort->unpack= unpack_addon_fields;
- if (multi_byte_charset &&
- !(param.tmp_buffer= (char*) my_malloc(param.sort_length,
- MYF(MY_WME | MY_THREAD_SPECIFIC))))
- goto err;
+ param.init_for_filesort(sort_len, table, max_rows, filesort->sort_positions);
+
+ sort->addon_fields= param.addon_fields;
+ sort->sort_keys= param.sort_keys;
if (select && select->quick)
thd->inc_status_sort_range();
else
thd->inc_status_sort_scan();
thd->query_plan_flags|= QPLAN_FILESORT;
- tracker->report_use(max_rows);
+ tracker->report_use(thd, max_rows);
// If number of rows is not known, use as much of sort buffer as possible.
num_rows= table->file->estimate_rows_upper_bound();
@@ -208,7 +271,16 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
thd->query_plan_flags|= QPLAN_FILESORT_PRIORITY_QUEUE;
status_var_increment(thd->status_var.filesort_pq_sorts_);
tracker->incr_pq_used();
+ param.using_pq= true;
const size_t compare_length= param.sort_length;
+ DBUG_ASSERT(param.using_packed_sortkeys() == false);
+ /*
+ For PQ queries (with limit) we know exactly how many pointers/records
+ we have in the buffer, so to simplify things, we initialize
+ all pointers here. (We cannot pack fields anyways, so there is no
+ point in doing lazy initialization).
+ */
+ sort->init_record_pointers();
if (pq.init(param.max_rows,
true, // max_at_top
NULL, // compare_function
@@ -223,22 +295,28 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
DBUG_ASSERT(thd->is_error());
goto err;
}
- // For PQ queries (with limit) we initialize all pointers.
- sort->init_record_pointers();
}
else
{
DBUG_PRINT("info", ("filesort PQ is not applicable"));
+ if (allow_packing_for_sortkeys)
+ param.try_to_pack_sortkeys();
+
+ param.try_to_pack_addons(thd->variables.max_length_for_sort_data);
+ tracker->report_sort_keys_format(param.using_packed_sortkeys());
+ param.using_pq= false;
+
size_t min_sort_memory= MY_MAX(MIN_SORT_MEMORY,
param.sort_length*MERGEBUFF2);
- set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
+ set_if_bigger(min_sort_memory, sizeof(Merge_chunk*)*MERGEBUFF2);
while (memory_available >= min_sort_memory)
{
ulonglong keys= memory_available / (param.rec_length + sizeof(char*));
param.max_keys_per_buffer= (uint) MY_MAX(MERGEBUFF2,
MY_MIN(num_rows, keys));
- if (sort->alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length))
+ sort->alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length);
+ if (sort->sort_buffer_size() > 0)
break;
size_t old_memory_available= memory_available;
memory_available= memory_available/4*3;
@@ -254,12 +332,23 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
tracker->report_sort_buffer_size(sort->sort_buffer_size());
}
+ if (param.using_addon_fields())
+ {
+ // report information whether addon fields are packed or not
+ tracker->report_addon_fields_format(param.using_packed_addons());
+ }
+
+ if (param.tmp_buffer.alloc(param.sort_length))
+ goto err;
+
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
DISK_BUFFER_SIZE, MYF(MY_WME)))
goto err;
param.sort_form= table;
- param.end=(param.local_sortorder=filesort->sortorder)+s_length;
+ param.local_sortorder=
+ Bounds_checked_array<SORT_FIELD>(filesort->sortorder, s_length);
+
num_rows= find_all_keys(thd, &param, select,
sort,
&buffpek_pointers,
@@ -288,12 +377,20 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
my_free(sort->buffpek.str);
sort->buffpek.str= 0;
}
+
+ if (param.using_addon_fields())
+ {
+ DBUG_ASSERT(sort->addon_fields);
+ if (!sort->addon_fields->allocate_addon_buf(param.addon_length))
+ goto err;
+ }
+
if (!(sort->buffpek.str=
(char *) read_buffpek_from_file(&buffpek_pointers, maxbuffer,
(uchar*) sort->buffpek.str)))
goto err;
sort->buffpek.length= maxbuffer;
- buffpek= (BUFFPEK *) sort->buffpek.str;
+ buffpek= (Merge_chunk *) sort->buffpek.str;
close_cached_file(&buffpek_pointers);
/* Open cached file if it isn't open */
if (! my_b_inited(outfile) &&
@@ -307,25 +404,25 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
Use also the space previously used by string pointers in sort_buffer
for temporary key storage.
*/
- param.max_keys_per_buffer=((param.max_keys_per_buffer *
- (param.rec_length + sizeof(char*))) /
- param.rec_length - 1);
+
+ param.max_keys_per_buffer= static_cast<uint>(sort->sort_buffer_size()) /
+ param.rec_length;
set_if_bigger(param.max_keys_per_buffer, 1);
maxbuffer--; // Offset from 0
- if (merge_many_buff(&param,
- (uchar*) sort->get_sort_keys(),
+
+ if (merge_many_buff(&param, sort->get_raw_buf(),
buffpek,&maxbuffer,
- &tempfile))
+ &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->get_sort_keys(),
+ sort->get_raw_buf(),
buffpek,
maxbuffer,
&tempfile,
- outfile))
+ outfile))
goto err;
}
@@ -337,10 +434,10 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
error= 0;
err:
- my_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- sort->free_sort_buffer();
+ if (!param.using_addon_fields())
+ sort->free_sort_buffer();
my_free(sort->buffpek.str);
}
else
@@ -348,7 +445,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
/* Remember sort buffers for next subquery call */
subselect->filesort_buffer= sort->filesort_buffer;
subselect->sortbuffer= sort->buffpek;
- sort->filesort_buffer.reset(); // Don't free this
+ sort->filesort_buffer.reset(); // Don't free this*/
}
sort->buffpek.str= 0;
@@ -362,11 +459,11 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
my_off_t save_pos=outfile->pos_in_file;
/* For following reads */
if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
- error=1;
+ error=1;
outfile->end_of_file=save_pos;
}
}
- tracker->report_merge_passes_at_end(thd->query_plan_fsort_passes);
+ tracker->report_merge_passes_at_end(thd, thd->query_plan_fsort_passes);
if (unlikely(error))
{
int kill_errno= thd->killed_errno();
@@ -396,7 +493,7 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
sort->examined_rows= param.examined_rows;
sort->return_rows= num_rows;
#ifdef SKIP_DBUG_IN_FILESORT
- DBUG_POP(); /* Ok to DBUG */
+ DBUG_POP_EMPTY; /* Ok to DBUG */
#endif
DBUG_PRINT("exit",
@@ -424,24 +521,42 @@ void Filesort::cleanup()
}
-uint Filesort::make_sortorder(THD *thd, JOIN *join, table_map first_table_bit)
+/*
+ Create the Sort_keys array and fill the sort_keys[i]->{item|field}.
+
+ This indicates which field/item values will be used as sort keys.
+ Attributes like lengths are not filled yet.
+*/
+
+Sort_keys*
+Filesort::make_sortorder(THD *thd, JOIN *join, table_map first_table_bit)
{
uint count;
SORT_FIELD *sort,*pos;
ORDER *ord;
DBUG_ENTER("make_sortorder");
-
count=0;
for (ord = order; ord; ord= ord->next)
count++;
- if (!sortorder)
- sortorder= (SORT_FIELD*) thd->alloc(sizeof(SORT_FIELD) * (count + 1));
+
+ if (sortorder)
+ DBUG_RETURN(sort_keys);
+
+ DBUG_ASSERT(sort_keys == NULL);
+
+ sortorder= (SORT_FIELD*) thd->alloc(sizeof(SORT_FIELD) * count);
pos= sort= sortorder;
if (!pos)
DBUG_RETURN(0);
+ sort_keys= new Sort_keys(sortorder, count);
+
+ if (!sort_keys)
+ DBUG_RETURN(0);
+
+ pos= sort_keys->begin();
for (ord= order; ord; ord= ord->next, pos++)
{
Item *first= ord->item[0];
@@ -489,7 +604,7 @@ uint Filesort::make_sortorder(THD *thd, JOIN *join, table_map first_table_bit)
pos->reverse= (ord->direction == ORDER::ORDER_DESC);
DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
- DBUG_RETURN(count);
+ DBUG_RETURN(sort_keys);
}
@@ -498,13 +613,14 @@ uint Filesort::make_sortorder(THD *thd, JOIN *join, table_map first_table_bit)
static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
uchar *buf)
{
- size_t length= sizeof(BUFFPEK)*count;
+ size_t length= sizeof(Merge_chunk)*count;
uchar *tmp= buf;
DBUG_ENTER("read_buffpek_from_file");
- if (count > UINT_MAX/sizeof(BUFFPEK))
+ if (count > UINT_MAX/sizeof(Merge_chunk))
return 0; /* sizeof(BUFFPEK)*count will overflow */
if (!tmp)
- tmp= (uchar *)my_malloc(length, MYF(MY_WME | MY_THREAD_SPECIFIC));
+ tmp= (uchar *)my_malloc(key_memory_Filesort_info_merge, length,
+ MYF(MY_WME | MY_THREAD_SPECIFIC));
if (tmp)
{
if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
@@ -698,7 +814,7 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
SORT_INFO *fs_info,
- IO_CACHE *buffpek_pointers,
+ IO_CACHE *buffpek_pointers,
IO_CACHE *tempfile,
Bounded_queue<uchar, uchar> *pq,
ha_rows *found_rows)
@@ -710,7 +826,10 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
handler *file;
MY_BITMAP *save_read_set, *save_write_set;
Item *sort_cond;
- ha_rows retval;
+ ha_rows num_records= 0;
+ const bool packed_format= param->is_packed_format();
+ const bool using_packed_sortkeys= param->using_packed_sortkeys();
+
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",
(select ? select->quick ? "ranges" : "where":
@@ -766,6 +885,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
}
DEBUG_SYNC(thd, "after_index_merge_phase1");
+
for (;;)
{
if (quick_select)
@@ -818,23 +938,28 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
if (write_record)
{
- ++(*found_rows);
if (pq)
- {
pq->push(ref_pos);
- idx= pq->num_elements();
- }
else
{
- if (idx == param->max_keys_per_buffer)
+ if (fs_info->isfull())
{
if (write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
goto err;
- idx= 0;
- indexpos++;
+ idx= 0;
+ indexpos++;
}
- make_sortkey(param, fs_info->get_record_buffer(idx++), ref_pos);
+ if (idx == 0)
+ fs_info->init_next_record_pointer();
+ uchar *start_of_rec= fs_info->get_next_record_pointer();
+
+ const uint rec_sz= make_sortkey(param, start_of_rec,
+ ref_pos, using_packed_sortkeys);
+ if (packed_format && rec_sz != param->rec_length)
+ fs_info->adjust_next_record_pointer(rec_sz);
+ idx++;
}
+ num_records++;
}
/* It does not make sense to read more keys in case of a fatal error */
@@ -870,11 +995,14 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
if (indexpos && idx &&
write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
- retval= (my_b_inited(tempfile) ?
- (ha_rows) (my_b_tell(tempfile)/param->rec_length) :
- idx);
- DBUG_PRINT("info", ("find_all_keys return %llu", (ulonglong) retval));
- DBUG_RETURN(retval);
+
+ (*found_rows)= num_records;
+ if (pq)
+ num_records= pq->num_elements();
+
+
+ DBUG_PRINT("info", ("find_all_keys return %llu", (ulonglong) num_records));
+ DBUG_RETURN(num_records);
err:
sort_form->column_bitmaps_set(save_read_set, save_write_set);
@@ -908,45 +1036,45 @@ static bool
write_keys(Sort_param *param, SORT_INFO *fs_info, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
- size_t rec_length;
- uchar **end;
- BUFFPEK buffpek;
+ Merge_chunk buffpek;
DBUG_ENTER("write_keys");
- rec_length= param->rec_length;
- 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)))
- goto err; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
/* check we won't have more buffpeks than we can possibly keep in memory */
- if (my_b_tell(buffpek_pointers) + sizeof(BUFFPEK) > (ulonglong)UINT_MAX)
- goto err;
- bzero(&buffpek, sizeof(buffpek));
- buffpek.file_pos= my_b_tell(tempfile);
+ if (my_b_tell(buffpek_pointers) + sizeof(Merge_chunk) > (ulonglong)UINT_MAX)
+ DBUG_RETURN(1);
+
+ buffpek.set_file_position(my_b_tell(tempfile));
if ((ha_rows) count > param->max_rows)
count=(uint) param->max_rows; /* purecov: inspected */
- buffpek.count=(ha_rows) count;
- for (end=sort_keys+count ; sort_keys != end ; sort_keys++)
- if (my_b_write(tempfile, (uchar*) *sort_keys, (uint) rec_length))
- goto err;
+ buffpek.set_rowcount(static_cast<ha_rows>(count));
+
+ for (uint ix= 0; ix < count; ++ix)
+ {
+ uchar *record= fs_info->get_sorted_record(ix);
+
+
+ if (my_b_write(tempfile, record, param->get_record_length(record)))
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+
if (my_b_write(buffpek_pointers, (uchar*) &buffpek, sizeof(buffpek)))
- goto err;
+ DBUG_RETURN(1);
+
DBUG_RETURN(0);
-err:
- DBUG_RETURN(1);
} /* write_keys */
/**
- Store length as suffix in high-byte-first order.
+ Store length in high-byte-first order.
*/
-
-static inline void store_length(uchar *to, uint length, uint pack_length)
+void store_length(uchar *to, uint length, uint pack_length)
{
switch (pack_length) {
case 1:
@@ -966,19 +1094,17 @@ static inline void store_length(uchar *to, uint length, uint pack_length)
void
-Type_handler_string_result::make_sort_key(uchar *to, Item *item,
- const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+Type_handler_string_result::make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
{
CHARSET_INFO *cs= item->collation.collation;
bool maybe_null= item->maybe_null;
if (maybe_null)
*to++= 1;
- char *tmp_buffer= param->tmp_buffer ? param->tmp_buffer : (char*) to;
- String tmp(tmp_buffer, param->tmp_buffer ? param->sort_length :
- sort_field->length, cs);
- String *res= item->str_result(&tmp);
+
+ String *res= item->str_result(&param->tmp_buffer);
if (!res)
{
if (maybe_null)
@@ -1005,12 +1131,11 @@ Type_handler_string_result::make_sort_key(uchar *to, Item *item,
#ifdef DBUG_ASSERT_EXISTS
size_t tmp_length=
#endif
- cs->coll->strnxfrm(cs, to, sort_field->length,
- item->max_char_length() *
- cs->strxfrm_multiply,
- (uchar*) res->ptr(), res->length(),
- MY_STRXFRM_PAD_WITH_SPACE |
- MY_STRXFRM_PAD_TO_MAXLEN);
+ cs->strnxfrm(to, sort_field->length,
+ item->max_char_length() * cs->strxfrm_multiply,
+ (uchar*) res->ptr(), res->length(),
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(tmp_length == sort_field->length);
}
else
@@ -1031,17 +1156,17 @@ Type_handler_string_result::make_sort_key(uchar *to, Item *item,
store_length(to + sort_field_length, length, sort_field->suffix_length);
}
/* apply cs->sort_order for case-insensitive comparison if needed */
- my_strnxfrm(cs,(uchar*)to,length,(const uchar*)res->ptr(),length);
+ cs->strnxfrm((uchar*)to, length, (const uchar*) res->ptr(), length);
char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
- cs->cset->fill(cs, (char *)to+length,diff,fill_char);
+ cs->fill((char *) to + length, diff, fill_char);
}
}
void
-Type_handler_int_result::make_sort_key(uchar *to, Item *item,
- const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+Type_handler_int_result::make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
{
longlong value= item->val_int_result();
make_sort_key_longlong(to, item->maybe_null, item->null_value,
@@ -1050,7 +1175,7 @@ Type_handler_int_result::make_sort_key(uchar *to, Item *item,
void
-Type_handler_temporal_result::make_sort_key(uchar *to, Item *item,
+Type_handler_temporal_result::make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const
{
@@ -1072,7 +1197,7 @@ Type_handler_temporal_result::make_sort_key(uchar *to, Item *item,
void
-Type_handler_timestamp_common::make_sort_key(uchar *to, Item *item,
+Type_handler_timestamp_common::make_sort_key_part(uchar *to, Item *item,
const SORT_FIELD_ATTR *sort_field,
Sort_param *param) const
{
@@ -1105,6 +1230,24 @@ Type_handler_timestamp_common::make_sort_key(uchar *to, Item *item,
void
+Type_handler::store_sort_key_longlong(uchar *to, bool unsigned_flag,
+ longlong value) const
+{
+ to[7]= (uchar) value;
+ to[6]= (uchar) (value >> 8);
+ to[5]= (uchar) (value >> 16);
+ to[4]= (uchar) (value >> 24);
+ to[3]= (uchar) (value >> 32);
+ to[2]= (uchar) (value >> 40);
+ to[1]= (uchar) (value >> 48);
+ if (unsigned_flag) /* Fix sign */
+ to[0]= (uchar) (value >> 56);
+ else
+ to[0]= (uchar) (value >> 56) ^ 128; /* Reverse signbit */
+}
+
+
+void
Type_handler::make_sort_key_longlong(uchar *to,
bool maybe_null,
bool null_value,
@@ -1121,24 +1264,35 @@ Type_handler::make_sort_key_longlong(uchar *to,
}
*to++= 1;
}
- to[7]= (uchar) value;
- to[6]= (uchar) (value >> 8);
- to[5]= (uchar) (value >> 16);
- to[4]= (uchar) (value >> 24);
- to[3]= (uchar) (value >> 32);
- to[2]= (uchar) (value >> 40);
- to[1]= (uchar) (value >> 48);
- if (unsigned_flag) /* Fix sign */
- to[0]= (uchar) (value >> 56);
- else
- to[0]= (uchar) (value >> 56) ^ 128; /* Reverse signbit */
+ store_sort_key_longlong(to, unsigned_flag, value);
+}
+
+
+uint
+Type_handler::make_packed_sort_key_longlong(uchar *to, bool maybe_null,
+ bool null_value, bool unsigned_flag,
+ longlong value,
+ const SORT_FIELD_ATTR *sort_field) const
+{
+ if (maybe_null)
+ {
+ if (null_value)
+ {
+ *to++= 0;
+ return 0;
+ }
+ *to++= 1;
+ }
+ store_sort_key_longlong(to, unsigned_flag, value);
+ DBUG_ASSERT(sort_field->original_length == sort_field->length);
+ return sort_field->original_length;
}
void
-Type_handler_decimal_result::make_sort_key(uchar *to, Item *item,
- const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+Type_handler_decimal_result::make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
{
my_decimal dec_buf, *dec_val= item->val_decimal_result(&dec_buf);
if (item->maybe_null)
@@ -1156,9 +1310,9 @@ Type_handler_decimal_result::make_sort_key(uchar *to, Item *item,
void
-Type_handler_real_result::make_sort_key(uchar *to, Item *item,
- const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+Type_handler_real_result::make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
{
double value= item->val_result();
if (item->maybe_null)
@@ -1176,49 +1330,16 @@ Type_handler_real_result::make_sort_key(uchar *to, Item *item,
/** Make a sort-key from record. */
-static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos)
+static uint make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos,
+ bool using_packed_sortkeys)
{
- Field *field;
- SORT_FIELD *sort_field;
- uint length;
+ uchar *orig_to= to;
- for (sort_field=param->local_sortorder ;
- sort_field != param->end ;
- sort_field++)
- {
- bool maybe_null=0;
- if ((field=sort_field->field))
- { // Field
- field->make_sort_key(to, sort_field->length);
- if ((maybe_null = field->maybe_null()))
- to++;
- }
- else
- { // Item
- sort_field->item->type_handler()->make_sort_key(to, sort_field->item,
- sort_field, param);
- if ((maybe_null= sort_field->item->maybe_null))
- to++;
- }
- if (sort_field->reverse)
- { /* Revers key */
- if (maybe_null && (to[-1]= !to[-1]))
- {
- to+= sort_field->length; // don't waste the time reversing all 0's
- continue;
- }
- length=sort_field->length;
- while (length--)
- {
- *to = (uchar) (~ *to);
- to++;
- }
- }
- else
- to+= sort_field->length;
- }
+ to+= using_packed_sortkeys ?
+ make_packed_sortkey(param, to) :
+ make_sortkey(param, to);
- if (param->addon_field)
+ if (param->using_addon_fields())
{
/*
Save field values appended to sorted fields.
@@ -1226,41 +1347,44 @@ static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos)
In this implementation we use fixed layout for field values -
the same for all records.
*/
- SORT_ADDON_FIELD *addonf= param->addon_field;
+ SORT_ADDON_FIELD *addonf= param->addon_fields->begin();
uchar *nulls= to;
+ uchar *p_len= to;
DBUG_ASSERT(addonf != 0);
+ const bool packed_addon_fields= param->addon_fields->using_packed_addons();
+ uint32 res_len= addonf->offset;
memset(nulls, 0, addonf->offset);
to+= addonf->offset;
- for ( ; (field= addonf->field) ; addonf++)
+ for ( ; addonf != param->addon_fields->end() ; addonf++)
{
+ Field *field= addonf->field;
if (addonf->null_bit && field->is_null())
{
nulls[addonf->null_offset]|= addonf->null_bit;
-#ifdef HAVE_valgrind
- bzero(to, addonf->length);
-#endif
+ if (!packed_addon_fields)
+ to+= addonf->length;
}
else
{
-#ifdef HAVE_valgrind
uchar *end= field->pack(to, field->ptr);
- uint length= (uint) ((to + addonf->length) - end);
- DBUG_ASSERT((int) length >= 0);
- if (length)
- bzero(end, length);
-#else
- (void) field->pack(to, field->ptr);
-#endif
+ int sz= static_cast<int>(end - to);
+ res_len += sz;
+ if (packed_addon_fields)
+ to+= sz;
+ else
+ to+= addonf->length;
}
- to+= addonf->length;
}
+ if (packed_addon_fields)
+ Addon_fields::store_addon_length(p_len, res_len);
}
else
{
/* Save filepos last */
memcpy((uchar*) to, ref_pos, (size_t) param->ref_length);
+ to+= param->ref_length;
}
- return;
+ return static_cast<uint>(to - orig_to);
}
@@ -1273,8 +1397,8 @@ static void register_used_fields(Sort_param *param)
SORT_FIELD *sort_field;
TABLE *table=param->sort_form;
- for (sort_field= param->local_sortorder ;
- sort_field != param->end ;
+ for (sort_field= param->local_sortorder.begin() ;
+ sort_field != param->local_sortorder.end() ;
sort_field++)
{
Field *field;
@@ -1289,12 +1413,14 @@ static void register_used_fields(Sort_param *param)
}
}
- if (param->addon_field)
+ if (param->using_addon_fields())
{
- SORT_ADDON_FIELD *addonf= param->addon_field;
- Field *field;
- for ( ; (field= addonf->field) ; addonf++)
+ SORT_ADDON_FIELD *addonf= param->addon_fields->begin();
+ for ( ; (addonf != param->addon_fields->end()) ; addonf++)
+ {
+ Field *field= addonf->field;
field->register_field_in_read_map();
+ }
}
else
{
@@ -1307,22 +1433,35 @@ static void register_used_fields(Sort_param *param)
static bool save_index(Sort_param *param, uint count,
SORT_INFO *table_sort)
{
- uint offset,res_length;
+ uint offset,res_length, length;
uchar *to;
DBUG_ENTER("save_index");
DBUG_ASSERT(table_sort->record_pointers == 0);
table_sort->sort_buffer(param, count);
+
+ if (param->using_addon_fields())
+ {
+ table_sort->sorted_result_in_fsbuf= TRUE;
+ table_sort->set_sort_length(param->sort_length);
+ DBUG_RETURN(0);
+ }
+
+ bool using_packed_sortkeys= param->using_packed_sortkeys();
res_length= param->res_length;
offset= param->rec_length-res_length;
if (!(to= table_sort->record_pointers=
- (uchar*) my_malloc(res_length*count,
- MYF(MY_WME | MY_THREAD_SPECIFIC))))
+ (uchar*) my_malloc(key_memory_Filesort_info_record_pointers,
+ 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++)
+ for (uint ix= 0; ix < count; ++ix)
{
- memcpy(to, *sort_keys+offset, res_length);
+ uchar *record= table_sort->get_sorted_record(ix);
+
+ length= using_packed_sortkeys ?
+ Sort_keys::read_sortkey_length(record) : offset;
+
+ memcpy(to, record + length, res_length);
to+= res_length;
}
DBUG_RETURN(0);
@@ -1393,8 +1532,9 @@ static bool check_if_pq_applicable(Sort_param *param,
// The whole source set fits into memory.
if (param->max_rows < num_rows/PQ_slowness )
{
- DBUG_RETURN(filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
- param->rec_length) != NULL);
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length);
+ DBUG_RETURN(filesort_info->sort_buffer_size() != 0);
}
else
{
@@ -1406,12 +1546,13 @@ static bool check_if_pq_applicable(Sort_param *param,
// Do we have space for LIMIT rows in memory?
if (param->max_keys_per_buffer < num_available_keys)
{
- DBUG_RETURN(filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
- param->rec_length) != NULL);
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length);
+ DBUG_RETURN(filesort_info->sort_buffer_size() != 0);
}
// Try to strip off addon fields.
- if (param->addon_field)
+ if (param->addon_fields)
{
const size_t row_length=
param->sort_length + param->ref_length + sizeof(char*);
@@ -1443,14 +1584,15 @@ static bool check_if_pq_applicable(Sort_param *param,
if (sort_merge_cost < pq_cost)
DBUG_RETURN(false);
- if (filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
- param->sort_length +
- param->ref_length))
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->sort_length + param->ref_length);
+
+ if (filesort_info->sort_buffer_size() > 0)
{
/* Make attached data to be references instead of fields. */
- my_free(filesort_info->addon_field);
- filesort_info->addon_field= NULL;
- param->addon_field= NULL;
+ my_free(filesort_info->addon_fields);
+ filesort_info->addon_fields= NULL;
+ param->addon_fields= NULL;
param->res_length= param->ref_length;
param->sort_length+= param->ref_length;
@@ -1466,12 +1608,12 @@ static bool check_if_pq_applicable(Sort_param *param,
/** Merge buffers to make < MERGEBUFF2 buffers. */
-int merge_many_buff(Sort_param *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
+int merge_many_buff(Sort_param *param, Sort_buffer sort_buffer,
+ Merge_chunk *buffpek, uint *maxbuffer, IO_CACHE *t_file)
{
uint i;
IO_CACHE t_file2,*from_file,*to_file,*temp;
- BUFFPEK *lastbuff;
+ Merge_chunk *lastbuff;
DBUG_ENTER("merge_many_buff");
if (*maxbuffer < MERGEBUFF2)
@@ -1491,11 +1633,11 @@ int merge_many_buff(Sort_param *param, uchar *sort_buffer,
lastbuff=buffpek;
for (i=0 ; i <= *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
{
- if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
+ if (merge_buffers(param,from_file,to_file,sort_buffer, lastbuff++,
buffpek+i,buffpek+i+MERGEBUFF-1,0))
goto cleanup;
}
- if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++,
+ if (merge_buffers(param,from_file,to_file,sort_buffer, lastbuff++,
buffpek+i,buffpek+ *maxbuffer,0))
break; /* purecov: inspected */
if (flush_io_cache(to_file))
@@ -1521,24 +1663,85 @@ cleanup:
(ulong)-1 if something goes wrong
*/
-ulong read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
- uint rec_length)
+ulong read_to_buffer(IO_CACHE *fromfile, Merge_chunk *buffpek,
+ Sort_param *param, bool packed_format)
{
- ulong count;
- ulong length= 0;
+ ha_rows count;
+ uint rec_length= param->rec_length;
- if ((count= (ulong) MY_MIN((ha_rows) buffpek->max_keys,buffpek->count)))
+ if ((count= MY_MIN(buffpek->max_keys(),buffpek->rowcount())))
{
- length= rec_length*count;
- if (unlikely(my_b_pread(fromfile, (uchar*) buffpek->base, length,
- buffpek->file_pos)))
+ size_t bytes_to_read;
+ if (packed_format)
+ {
+ count= buffpek->rowcount();
+ bytes_to_read= MY_MIN(buffpek->buffer_size(),
+ static_cast<size_t>(fromfile->end_of_file -
+ buffpek->file_position()));
+ }
+ else
+ bytes_to_read= rec_length * static_cast<size_t>(count);
+
+ if (unlikely(my_b_pread(fromfile, buffpek->buffer_start(),
+ bytes_to_read, buffpek->file_position())))
return ((ulong) -1);
- buffpek->key=buffpek->base;
- buffpek->file_pos+= length; /* New filepos */
- buffpek->count-= count;
- buffpek->mem_count= count;
+
+ size_t num_bytes_read;
+
+ if (packed_format)
+ {
+ /*
+ The last record read is most likely not complete here.
+ We need to loop through all the records, reading the length fields,
+ and then "chop off" the final incomplete record.
+ */
+ uchar *record= buffpek->buffer_start();
+ uint ix= 0;
+ uint size_of_addon_length= param->using_packed_addons() ?
+ Addon_fields::size_of_length_field : 0;
+
+ uint size_of_sort_length= param->using_packed_sortkeys() ?
+ Sort_keys::size_of_length_field : 0;
+
+ for (; ix < count; ++ix)
+ {
+ if (record + size_of_sort_length > buffpek->buffer_end())
+ break;
+ uint sort_length= param->using_packed_sortkeys() ?
+ Sort_keys::read_sortkey_length(record) :
+ param->sort_length;
+
+ DBUG_ASSERT(sort_length <= param->sort_length);
+
+ if (record + sort_length + size_of_addon_length >
+ buffpek->buffer_end())
+ break; // Incomplete record.
+
+ uchar *plen= record + sort_length;
+ uint res_length= param->get_result_length(plen);
+ if (plen + res_length > buffpek->buffer_end())
+ break; // Incomplete record.
+ DBUG_ASSERT(res_length > 0);
+ DBUG_ASSERT(sort_length + res_length <= param->rec_length);
+ record+= sort_length;
+ record+= res_length;
+ }
+ DBUG_ASSERT(ix > 0);
+ count= ix;
+ num_bytes_read= record - buffpek->buffer_start();
+ DBUG_PRINT("info", ("read %llu bytes of complete records",
+ static_cast<ulonglong>(bytes_to_read)));
+ }
+ else
+ num_bytes_read= bytes_to_read;
+
+ buffpek->init_current_key();
+ buffpek->advance_file_position(num_bytes_read); /* New filepos */
+ buffpek->decrement_rowcount(count);
+ buffpek->set_mem_count(count);
+ return (ulong) num_bytes_read;
}
- return (length);
+ return 0;
} /* read_to_buffer */
@@ -1553,25 +1756,15 @@ ulong read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
@param[in] key_length key length
*/
-void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
+void reuse_freed_buff(QUEUE *queue, Merge_chunk *reuse, uint key_length)
{
- uchar *reuse_end= reuse->base + reuse->max_keys * key_length;
for (uint i= queue_first_element(queue);
i <= queue_last_element(queue);
i++)
{
- BUFFPEK *bp= (BUFFPEK *) queue_element(queue, i);
- if (bp->base + bp->max_keys * key_length == reuse->base)
- {
- bp->max_keys+= reuse->max_keys;
+ Merge_chunk *bp= (Merge_chunk *) queue_element(queue, i);
+ if (reuse->merge_freed_buff(bp))
return;
- }
- else if (bp->base == reuse_end)
- {
- bp->base= reuse->base;
- bp->max_keys+= reuse->max_keys;
- return;
- }
}
DBUG_ASSERT(0);
}
@@ -1587,7 +1780,10 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
@param lastbuff OUT Store here BUFFPEK describing data written to to_file
@param Fb First element in source BUFFPEKs array
@param Tb Last element in source BUFFPEKs array
- @param flag
+ @param flag 0 <=> write {sort_key, addon_fields} pairs as further
+ sorting will be performed
+ 1 <=> write just addon_fields as this is the final
+ merge pass
@retval
0 OK
@@ -1596,8 +1792,8 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
*/
bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
- IO_CACHE *to_file, uchar *sort_buffer,
- BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
+ IO_CACHE *to_file, Sort_buffer sort_buffer,
+ Merge_chunk *lastbuff, Merge_chunk *Fb, Merge_chunk *Tb,
int flag)
{
bool error= 0;
@@ -1607,7 +1803,7 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
ha_rows max_rows,org_max_rows;
my_off_t to_start_filepos;
uchar *strpos;
- BUFFPEK *buffpek;
+ Merge_chunk *buffpek;
QUEUE queue;
qsort2_cmp cmp;
void *first_cmp_arg;
@@ -1631,9 +1827,14 @@ bool merge_buffers(Sort_param *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;
+
+ const bool using_packed_sortkeys= param->using_packed_sortkeys();
+ bool offset_for_packing= (flag == 1 && using_packed_sortkeys);
+ const bool packed_format= param->is_packed_format();
+
maxcount= (ulong) (param->max_keys_per_buffer/((uint) (Tb-Fb) +1));
to_start_filepos= my_b_tell(to_file);
- strpos= sort_buffer;
+ strpos= sort_buffer.array();
org_max_rows=max_rows= param->max_rows;
set_if_bigger(maxcount, 1);
@@ -1645,22 +1846,24 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
}
else
{
- cmp= get_ptr_compare(sort_length);
- first_cmp_arg= (void*) &sort_length;
+ cmp= param->get_compare_function();
+ first_cmp_arg= param->get_compare_argument(&sort_length);
}
- if (unlikely(init_queue(&queue, (uint) (Tb-Fb)+1, offsetof(BUFFPEK,key), 0,
+ if (unlikely(init_queue(&queue, (uint) (Tb-Fb)+1,
+ offsetof(Merge_chunk,m_current_key), 0,
(queue_compare) cmp, first_cmp_arg, 0, 0)))
DBUG_RETURN(1); /* purecov: inspected */
+ const size_t chunk_sz = (sort_buffer.size()/((uint) (Tb-Fb) +1));
for (buffpek= Fb ; buffpek <= Tb ; buffpek++)
{
- buffpek->base= strpos;
- buffpek->max_keys= maxcount;
- bytes_read= read_to_buffer(from_file, buffpek, rec_length);
+ buffpek->set_buffer(strpos, strpos + chunk_sz);
+ buffpek->set_max_keys(maxcount);
+ bytes_read= read_to_buffer(from_file, buffpek, param, packed_format);
if (unlikely(bytes_read == (ulong) -1))
goto err; /* purecov: inspected */
-
- strpos+= bytes_read;
- buffpek->max_keys= buffpek->mem_count; // If less data in buffers than expected
+ strpos+= chunk_sz;
+ // If less data in buffers than expected
+ buffpek->set_max_keys(buffpek->mem_count());
queue_insert(&queue, (uchar*) buffpek);
}
@@ -1671,16 +1874,17 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
Copy the first argument to unique_buff for unique removal.
Store it also in 'to_file'.
*/
- buffpek= (BUFFPEK*) queue_top(&queue);
- memcpy(unique_buff, buffpek->key, rec_length);
+ buffpek= (Merge_chunk*) queue_top(&queue);
+ memcpy(unique_buff, buffpek->current_key(), rec_length);
if (min_dupl_count)
memcpy(&dupl_count, unique_buff+dupl_count_ofs,
sizeof(dupl_count));
- buffpek->key+= rec_length;
- if (! --buffpek->mem_count)
+ buffpek->advance_current_key(rec_length);
+ buffpek->decrement_mem_count();
+ if (buffpek->mem_count() == 0)
{
if (unlikely(!(bytes_read= read_to_buffer(from_file, buffpek,
- rec_length))))
+ param, packed_format))))
{
(void) queue_remove_top(&queue);
reuse_freed_buff(&queue, buffpek, rec_length);
@@ -1700,61 +1904,72 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
for (;;)
{
- buffpek= (BUFFPEK*) queue_top(&queue);
- src= buffpek->key;
+ buffpek= (Merge_chunk*) queue_top(&queue);
+ src= buffpek->current_key();
if (cmp) // Remove duplicates
{
- if (!(*cmp)(first_cmp_arg, &unique_buff,
- (uchar**) &buffpek->key))
- {
+ uchar *current_key= buffpek->current_key();
+ if (!(*cmp)(first_cmp_arg, &unique_buff, &current_key))
+ {
if (min_dupl_count)
- {
+ {
element_count cnt;
- memcpy(&cnt, (uchar *) buffpek->key+dupl_count_ofs, sizeof(cnt));
+ memcpy(&cnt, buffpek->current_key() + dupl_count_ofs, sizeof(cnt));
dupl_count+= cnt;
}
goto skip_duplicate;
}
if (min_dupl_count)
- {
+ {
memcpy(unique_buff+dupl_count_ofs, &dupl_count,
sizeof(dupl_count));
}
- src= unique_buff;
- }
-
- /*
- Do not write into the output file if this is the final merge called
- for a Unique object used for intersection and dupl_count is less
- than min_dupl_count.
- If the Unique object is used to intersect N sets of unique elements
- then for any element:
- dupl_count >= N <=> the element is occurred in each of these N sets.
- */
- if (!check_dupl_count || dupl_count >= min_dupl_count)
- {
- if (my_b_write(to_file, src+wr_offset, wr_len))
- goto err; /* purecov: inspected */
- }
- if (cmp)
- {
- memcpy(unique_buff, (uchar*) buffpek->key, rec_length);
- if (min_dupl_count)
- memcpy(&dupl_count, unique_buff+dupl_count_ofs,
- sizeof(dupl_count));
+ src= unique_buff;
}
- if (!--max_rows)
+
{
- /* Nothing more to do */
- goto end; /* purecov: inspected */
- }
+ param->get_rec_and_res_len(buffpek->current_key(),
+ &rec_length, &res_length);
+ const uint bytes_to_write= (flag == 0) ? rec_length : res_length;
+ /*
+ Do not write into the output file if this is the final merge called
+ for a Unique object used for intersection and dupl_count is less
+ than min_dupl_count.
+ If the Unique object is used to intersect N sets of unique elements
+ then for any element:
+ dupl_count >= N <=> the element is occurred in each of these N sets.
+ */
+ if (!check_dupl_count || dupl_count >= min_dupl_count)
+ {
+ if(my_b_write(to_file,
+ src + (offset_for_packing ?
+ rec_length - res_length : // sort length
+ wr_offset),
+ bytes_to_write))
+ goto err; /* purecov: inspected */
+ }
+ if (cmp)
+ {
+ memcpy(unique_buff, buffpek->current_key(), rec_length);
+ if (min_dupl_count)
+ memcpy(&dupl_count, unique_buff+dupl_count_ofs,
+ sizeof(dupl_count));
+ }
+ if (!--max_rows)
+ {
+ /* Nothing more to do */
+ goto end; /* purecov: inspected */
+ }
+ }
skip_duplicate:
- buffpek->key+= rec_length;
- if (! --buffpek->mem_count)
+ buffpek->advance_current_key(rec_length);
+ buffpek->decrement_mem_count();
+
+ if (buffpek->mem_count() == 0)
{
if (unlikely(!(bytes_read= read_to_buffer(from_file, buffpek,
- rec_length))))
+ param, packed_format))))
{
(void) queue_remove_top(&queue);
reuse_freed_buff(&queue, buffpek, rec_length);
@@ -1766,9 +1981,10 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
queue_replace_top(&queue); /* Top element has been replaced */
}
}
- buffpek= (BUFFPEK*) queue_top(&queue);
- buffpek->base= (uchar*) sort_buffer;
- buffpek->max_keys= param->max_keys_per_buffer;
+ buffpek= (Merge_chunk*) queue_top(&queue);
+ buffpek->set_buffer(sort_buffer.array(),
+ sort_buffer.array() + sort_buffer.size());
+ buffpek->set_max_keys(param->max_keys_per_buffer);
/*
As we know all entries in the buffer are unique, we only have to
@@ -1776,16 +1992,17 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
*/
if (cmp)
{
- if (!(*cmp)(first_cmp_arg, &unique_buff, (uchar**) &buffpek->key))
+ uchar *current_key= buffpek->current_key();
+ if (!(*cmp)(first_cmp_arg, &unique_buff, &current_key))
{
if (min_dupl_count)
{
element_count cnt;
- memcpy(&cnt, (uchar *) buffpek->key+dupl_count_ofs, sizeof(cnt));
+ memcpy(&cnt, buffpek->current_key() + dupl_count_ofs, sizeof(cnt));
dupl_count+= cnt;
}
- buffpek->key+= rec_length;
- --buffpek->mem_count;
+ buffpek->advance_current_key(rec_length);
+ buffpek->decrement_mem_count();
}
if (min_dupl_count)
@@ -1804,45 +2021,44 @@ bool merge_buffers(Sort_param *param, IO_CACHE *from_file,
do
{
- if ((ha_rows) buffpek->mem_count > max_rows)
+ if (buffpek->mem_count() > max_rows)
{ /* Don't write too many records */
- buffpek->mem_count= (uint) max_rows;
- buffpek->count= 0; /* Don't read more */
+ buffpek->set_mem_count(max_rows);
+ buffpek->set_rowcount(0); /* Don't read more */
}
- max_rows-= buffpek->mem_count;
- if (flag == 0)
- {
- if (my_b_write(to_file, (uchar*) buffpek->key,
- (size_t)(rec_length*buffpek->mem_count)))
- goto err; /* purecov: inspected */
- }
- else
+ max_rows-= buffpek->mem_count();
+ for (uint ix= 0; ix < buffpek->mem_count(); ++ix)
{
- uchar *end;
- src= buffpek->key+offset;
- for (end= src+buffpek->mem_count*rec_length ;
- src != end ;
- src+= rec_length)
+ uchar *src= buffpek->current_key();
+ param->get_rec_and_res_len(src,
+ &rec_length, &res_length);
+ const uint bytes_to_write= (flag == 0) ? rec_length : res_length;
+ if (check_dupl_count)
{
- if (check_dupl_count)
- {
- memcpy((uchar *) &dupl_count, src+dupl_count_ofs, sizeof(dupl_count));
- if (dupl_count < min_dupl_count)
- continue;
- }
- if (my_b_write(to_file, src, wr_len))
- goto err;
+ memcpy((uchar *) &dupl_count,
+ buffpek->current_key() + offset + dupl_count_ofs,
+ sizeof(dupl_count));
+ if (dupl_count < min_dupl_count)
+ continue;
}
+ if(my_b_write(to_file,
+ src + (offset_for_packing ?
+ rec_length - res_length : // sort length
+ wr_offset),
+ bytes_to_write))
+ goto err;
+ buffpek->advance_current_key(rec_length);
}
}
while (likely(!(error=
- (bytes_read= read_to_buffer(from_file, buffpek,
- rec_length)) == (ulong) -1)) &&
+ (bytes_read= read_to_buffer(from_file, buffpek, param,
+ packed_format)) == (ulong) -1)) &&
bytes_read != 0);
end:
- lastbuff->count= MY_MIN(org_max_rows-max_rows, param->max_rows);
- lastbuff->file_pos= to_start_filepos;
+ lastbuff->set_rowcount(MY_MIN(org_max_rows-max_rows, param->max_rows));
+ lastbuff->set_file_position(to_start_filepos);
+
cleanup:
delete_queue(&queue);
DBUG_RETURN(error);
@@ -1856,13 +2072,13 @@ err:
/* Do a merge to output-file (save only positions) */
-int merge_index(Sort_param *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint maxbuffer,
- IO_CACHE *tempfile, IO_CACHE *outfile)
+int merge_index(Sort_param *param, Sort_buffer sort_buffer,
+ Merge_chunk *buffpek, uint maxbuffer,
+ IO_CACHE *tempfile, IO_CACHE *outfile)
{
DBUG_ENTER("merge_index");
- if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
- buffpek+maxbuffer,1))
+ if (merge_buffers(param, tempfile, outfile, sort_buffer, buffpek, buffpek,
+ buffpek + maxbuffer, 1))
DBUG_RETURN(1); /* purecov: inspected */
DBUG_RETURN(0);
} /* merge_index */
@@ -1881,70 +2097,77 @@ static uint suffix_length(ulong string_length)
void
-Type_handler_string_result::sortlength(THD *thd,
+Type_handler_string_result::sort_length(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *sortorder) const
{
CHARSET_INFO *cs;
- sortorder->length= item->max_length;
- set_if_smaller(sortorder->length, thd->variables.max_sort_length);
+ sortorder->set_length_and_original_length(thd, item->max_length);
+
if (use_strnxfrm((cs= item->collation.collation)))
{
- sortorder->length= (uint)cs->coll->strnxfrmlen(cs, sortorder->length);
+ sortorder->length= (uint) cs->strnxfrmlen(sortorder->length);
}
else if (cs == &my_charset_bin)
{
/* Store length last to be able to sort blob/varbinary */
- sortorder->suffix_length= suffix_length(sortorder->length);
+ sortorder->suffix_length= suffix_length(item->max_length);
+ DBUG_ASSERT(sortorder->length <= UINT_MAX32 - sortorder->suffix_length);
sortorder->length+= sortorder->suffix_length;
+ if (sortorder->original_length >= UINT_MAX32 - sortorder->suffix_length)
+ sortorder->original_length= UINT_MAX32;
+ else
+ sortorder->original_length+= sortorder->suffix_length;
}
}
void
-Type_handler_temporal_result::sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *sortorder) const
+Type_handler_temporal_result::sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *sortorder) const
{
- sortorder->length= 8; // Sizof intern longlong
+ sortorder->original_length= sortorder->length= 8; // Sizof intern longlong
}
void
-Type_handler_timestamp_common::sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *sortorder) const
+Type_handler_timestamp_common::sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *sortorder) const
{
sortorder->length= my_timestamp_binary_length(item->decimals);
+ sortorder->original_length= sortorder->length;
}
void
-Type_handler_int_result::sortlength(THD *thd,
+Type_handler_int_result::sort_length(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *sortorder) const
{
- sortorder->length= 8; // Sizof intern longlong
+ sortorder->original_length= sortorder->length= 8; // Sizof intern longlong
}
void
-Type_handler_real_result::sortlength(THD *thd,
+Type_handler_real_result::sort_length(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *sortorder) const
{
- sortorder->length= sizeof(double);
+ sortorder->original_length= sortorder->length= sizeof(double);
}
void
-Type_handler_decimal_result::sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *sortorder) const
+Type_handler_decimal_result::sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *sortorder) const
{
sortorder->length=
my_decimal_get_binary_size(item->max_length - (item->decimals ? 1 : 0),
item->decimals);
+ sortorder->original_length= sortorder->length;
}
@@ -1954,63 +2177,125 @@ Type_handler_decimal_result::sortlength(THD *thd,
@param thd Thread handler
@param sortorder Order of items to sort
@param s_length Number of items to sort
- @param[out] multi_byte_charset Set to 1 if we are using multi-byte charset
- (In which case we have to use strxnfrm())
+ @param allow_packing_for_sortkeys [out] set to false if packing sort keys is not
+ allowed
@note
- sortorder->length is updated for each sort item.
+ * sortorder->length and other members are updated for each sort item.
+ * TODO what is the meaning of this value if some fields are using packing while
+ others are not?
@return
Total length of sort buffer in bytes
*/
static uint
-sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
- bool *multi_byte_charset)
+sortlength(THD *thd, Sort_keys *sort_keys, bool *allow_packing_for_sortkeys)
{
uint length;
- *multi_byte_charset= 0;
+ *allow_packing_for_sortkeys= true;
+ bool allow_packing_for_keys= true;
length=0;
- for (; s_length-- ; sortorder++)
+ uint nullable_cols=0;
+
+ if (sort_keys->is_parameters_computed())
+ {
+ *allow_packing_for_sortkeys= sort_keys->using_packed_sortkeys();
+ return sort_keys->get_sort_length_with_memcmp_values();
+ }
+
+ for (SORT_FIELD *sortorder= sort_keys->begin();
+ sortorder != sort_keys->end();
+ sortorder++)
{
sortorder->suffix_length= 0;
+ sortorder->length_bytes= 0;
if (sortorder->field)
{
+ Field *field= sortorder->field;
CHARSET_INFO *cs= sortorder->field->sort_charset();
- sortorder->length= sortorder->field->sort_length();
+ sortorder->set_length_and_original_length(thd, field->sort_length());
+
+ sortorder->suffix_length= sortorder->field->sort_suffix_length();
+ sortorder->type= field->is_packable() ?
+ SORT_FIELD_ATTR::VARIABLE_SIZE :
+ SORT_FIELD_ATTR::FIXED_SIZE;
+ sortorder->cs= cs;
+
if (use_strnxfrm((cs=sortorder->field->sort_charset())))
+ sortorder->length= (uint) cs->strnxfrmlen(sortorder->length);
+
+ if (sortorder->is_variable_sized() && allow_packing_for_keys)
{
- *multi_byte_charset= true;
- sortorder->length= (uint)cs->coll->strnxfrmlen(cs, sortorder->length);
+ allow_packing_for_keys= sortorder->check_if_packing_possible(thd);
+ sortorder->length_bytes=
+ number_storage_requirement(MY_MIN(sortorder->original_length,
+ thd->variables.max_sort_length));
}
- if (sortorder->field->maybe_null())
- length++; // Place for NULL marker
+
+ if ((sortorder->maybe_null= sortorder->field->maybe_null()))
+ nullable_cols++; // Place for NULL marker
}
else
{
- sortorder->item->type_handler()->sortlength(thd, sortorder->item,
- sortorder);
- if (use_strnxfrm(sortorder->item->collation.collation))
+ sortorder->item->type_handler()->sort_length(thd, sortorder->item,
+ sortorder);
+ sortorder->type= sortorder->item->type_handler()->is_packable() ?
+ SORT_FIELD_ATTR::VARIABLE_SIZE :
+ SORT_FIELD_ATTR::FIXED_SIZE;
+ sortorder->cs= sortorder->item->collation.collation;
+ if (sortorder->is_variable_sized() && allow_packing_for_keys)
{
- *multi_byte_charset= true;
+ allow_packing_for_keys= sortorder->check_if_packing_possible(thd);
+ sortorder->length_bytes=
+ number_storage_requirement(MY_MIN(sortorder->original_length,
+ thd->variables.max_sort_length));
}
- if (sortorder->item->maybe_null)
- length++; // Place for NULL marker
+
+ if ((sortorder->maybe_null= sortorder->item->maybe_null))
+ nullable_cols++; // Place for NULL marker
}
set_if_smaller(sortorder->length, thd->variables.max_sort_length);
+ set_if_smaller(sortorder->original_length, thd->variables.max_sort_length);
length+=sortorder->length;
+
+ sort_keys->increment_size_of_packable_fields(sortorder->length_bytes);
+ sort_keys->increment_original_sort_length(sortorder->original_length);
}
- sortorder->field= (Field*) 0; // end marker
+ // add bytes for nullable_cols
+ sort_keys->increment_original_sort_length(nullable_cols);
+ *allow_packing_for_sortkeys= allow_packing_for_keys;
+ sort_keys->set_sort_length_with_memcmp_values(length + nullable_cols);
+ sort_keys->set_parameters_computed(true);
DBUG_PRINT("info",("sort_length: %d",length));
- return length;
+ return length + nullable_cols;
}
+
+/*
+ Check whether addon fields can be used or not.
+
+ @param table Table structure
+ @param sortlength Length of sort key [strxfrm form]
+ @param length [OUT] Max length of addon fields
+ @param fields [OUT] Number of addon fields
+ @param null_fields [OUT] Number of nullable addon fields
+ @param packable_length [OUT] Max length of addon fields that can be
+ packed
+
+ @retval
+ TRUE Addon fields can be used
+ FALSE Otherwise
+*/
+
bool filesort_use_addons(TABLE *table, uint sortlength,
- uint *length, uint *fields, uint *null_fields)
+ uint *length, uint *fields, uint *null_fields,
+ uint *packable_length)
{
Field **pfield, *field;
- *length= *fields= *null_fields= 0;
+ *length= *fields= *null_fields= *packable_length= 0;
+ uint field_length=0;
for (pfield= table->field; (field= *pfield) ; pfield++)
{
@@ -2018,7 +2303,12 @@ bool filesort_use_addons(TABLE *table, uint sortlength,
continue;
if (field->flags & BLOB_FLAG)
return false;
- (*length)+= field->max_packed_col_length(field->pack_length());
+ field_length= field->max_packed_col_length(field->pack_length());
+ (*length)+= field_length;
+
+ if (field->maybe_null() || field->is_packable())
+ (*packable_length)+= field_length;
+
if (field->maybe_null())
(*null_fields)++;
(*fields)++;
@@ -2027,6 +2317,15 @@ bool filesort_use_addons(TABLE *table, uint sortlength,
return false;
(*length)+= (*null_fields+7)/8;
+ /*
+ sortlength used here is unpacked key length (the strxfrm form). This is
+ done because unpacked key length is a good upper bound for packed sort
+ key length.
+ But for some collations the max packed length may be greater than the
+ length obtained from the strxfrm form.
+ Example: for utf8_general_ci, the original string form can be longer than
+ its mem-comparable form (note that this is rarely achieved in practice).
+ */
return *length + sortlength <
table->in_use->variables.max_length_for_sort_data;
}
@@ -2043,11 +2342,11 @@ bool filesort_use_addons(TABLE *table, uint sortlength,
layouts for the values of the non-sorted fields in the buffer and
fills them.
- @param thd Current thread
- @param ptabfield Array of references to the table fields
- @param sortlength Total length of sorted fields
- @param [out] addon_buf Buffer to us for appended fields
-
+ @param table Table structure
+ @param sortlength Total length of sorted fields
+ @param addon_length [OUT] Length of addon fields
+ @param m_packable_length [OUT] Length of the addon fields that can be
+ packed
@note
The null bits for the appended values are supposed to be put together
and stored the buffer just ahead of the value of the first field.
@@ -2058,13 +2357,13 @@ bool filesort_use_addons(TABLE *table, uint sortlength,
NULL if we do not store field values with sort data.
*/
-static SORT_ADDON_FIELD *
-get_addon_fields(TABLE *table, uint sortlength, LEX_STRING *addon_buf)
+static Addon_fields*
+get_addon_fields(TABLE *table, uint sortlength,
+ uint *addon_length, uint *m_packable_length)
{
Field **pfield;
Field *field;
- SORT_ADDON_FIELD *addonf;
- uint length, fields, null_fields;
+ uint length, fields, null_fields, packable_length;
MY_BITMAP *read_set= table->read_set;
DBUG_ENTER("get_addon_fields");
@@ -2078,23 +2377,34 @@ get_addon_fields(TABLE *table, uint sortlength, LEX_STRING *addon_buf)
the values directly from sorted fields.
But beware the case when item->cmp_type() != item->result_type()
*/
- addon_buf->str= 0;
- addon_buf->length= 0;
// see remove_const() for HA_SLOW_RND_POS explanation
if (table->file->ha_table_flags() & HA_SLOW_RND_POS)
sortlength= 0;
- if (!filesort_use_addons(table, sortlength, &length, &fields, &null_fields) ||
- !my_multi_malloc(MYF(MY_WME | MY_THREAD_SPECIFIC), &addonf,
- sizeof(SORT_ADDON_FIELD) * (fields+1),
- &addon_buf->str, length, NullS))
+ void *raw_mem_addon_field, *raw_mem;
+ if (!filesort_use_addons(table, sortlength, &length, &fields, &null_fields,
+ &packable_length) ||
+ !(my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME | MY_THREAD_SPECIFIC),
+ &raw_mem, sizeof(Addon_fields),
+ &raw_mem_addon_field,
+ sizeof(SORT_ADDON_FIELD) * fields,
+ NullS)))
DBUG_RETURN(0);
- addon_buf->length= length;
+ Addon_fields_array
+ addon_array(static_cast<SORT_ADDON_FIELD*>(raw_mem_addon_field), fields);
+ Addon_fields *addon_fields= new (raw_mem) Addon_fields(addon_array);
+
+ DBUG_ASSERT(addon_fields);
+
+ (*addon_length)= length;
+ (*m_packable_length)= packable_length;
+
length= (null_fields+7)/8;
null_fields= 0;
+ SORT_ADDON_FIELD* addonf= addon_fields->begin();
for (pfield= table->field; (field= *pfield) ; pfield++)
{
if (!bitmap_is_set(read_set, field->field_index))
@@ -2116,47 +2426,12 @@ get_addon_fields(TABLE *table, uint sortlength, LEX_STRING *addon_buf)
length+= addonf->length;
addonf++;
}
- addonf->field= 0; // Put end marker
DBUG_PRINT("info",("addon_length: %d",length));
- DBUG_RETURN(addonf-fields);
+ DBUG_RETURN(addon_fields);
}
-/**
- Copy (unpack) values appended to sorted fields from a buffer back to
- their regular positions specified by the Field::ptr pointers.
-
- @param addon_field Array of descriptors for appended fields
- @param buff Buffer which to unpack the value from
-
- @note
- The function is supposed to be used only as a callback function
- when getting field values for the sorted result set.
-
- @return
- void.
-*/
-
-static void
-unpack_addon_fields(struct st_sort_addon_field *addon_field, uchar *buff,
- uchar *buff_end)
-{
- Field *field;
- SORT_ADDON_FIELD *addonf= addon_field;
-
- for ( ; (field= addonf->field) ; addonf++)
- {
- if (addonf->null_bit && (addonf->null_bit & buff[addonf->null_offset]))
- {
- field->set_null();
- continue;
- }
- field->set_notnull();
- field->unpack(field->ptr, buff + addonf->offset, buff_end, 0);
- }
-}
-
/*
** functions to change a double or float to a sortable string
** The following should work for IEEE
@@ -2205,6 +2480,25 @@ void change_double_for_sort(double nr,uchar *to)
}
}
+bool SORT_INFO::using_packed_addons()
+{
+ return addon_fields != NULL && addon_fields->using_packed_addons();
+}
+
+void SORT_INFO::free_addon_buff()
+{
+ if (addon_fields)
+ addon_fields->free_addon_buff();
+}
+
+/*
+ Check if packed sortkeys are used or not
+*/
+bool SORT_INFO::using_packed_sortkeys()
+{
+ return sort_keys != NULL && sort_keys->using_packed_sortkeys();
+}
+
/**
Free SORT_INFO
*/
@@ -2215,3 +2509,568 @@ SORT_INFO::~SORT_INFO()
free_data();
DBUG_VOID_RETURN;
}
+
+
+void Sort_param::try_to_pack_sortkeys()
+{
+ #ifdef WITHOUT_PACKED_SORT_KEYS
+ return;
+ #endif
+
+ uint size_of_packable_fields= sort_keys->get_size_of_packable_fields();
+
+ /*
+ Disable packing when all fields are fixed-size fields.
+ */
+ if (size_of_packable_fields == 0)
+ return;
+
+ const uint sz= Sort_keys::size_of_length_field;
+ uint sort_len= sort_keys->get_sort_length_with_original_values();
+
+ /*
+ Heuristic introduced, skip packing sort keys if saving less than 128 bytes
+ */
+
+ if (sort_len < 128 + sz + size_of_packable_fields)
+ return;
+
+ sort_keys->set_using_packed_sortkeys(true);
+ m_packed_format= true;
+ m_using_packed_sortkeys= true;
+ sort_length= sort_len + sz + size_of_packable_fields +
+ (using_addon_fields() ? 0 : res_length);
+ /* Only the record length needs to be updated, the res_length does not need
+ to be updated
+ */
+ rec_length= sort_length + addon_length;
+}
+
+
+uint
+Type_handler_string_result::make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ CHARSET_INFO *cs= item->collation.collation;
+ bool maybe_null= item->maybe_null;
+
+ if (maybe_null)
+ *to++= 1;
+
+ Binary_string *res= item->str_result(&param->tmp_buffer);
+ if (!res)
+ {
+ if (maybe_null)
+ {
+ *(to-1)= 0;
+ return 0;
+ }
+ else
+ {
+ /* purecov: begin deadcode */
+ /*
+ This should only happen during extreme conditions if we run out
+ of memory or have an item marked not null when it can be null.
+ This code is here mainly to avoid a hard crash in this case.
+ */
+ DBUG_ASSERT(0);
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ memset(to, 0, sort_field->length); // Avoid crash
+ /* purecov: end */
+ return sort_field->original_length;
+ }
+ }
+ return sort_field->pack_sort_string(to, res, cs);
+}
+
+
+uint
+Type_handler_int_result::make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ longlong value= item->val_int_result();
+ return make_packed_sort_key_longlong(to, item->maybe_null,
+ item->null_value, item->unsigned_flag,
+ value, sort_field);
+}
+
+
+uint
+Type_handler_decimal_result::make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ my_decimal dec_buf, *dec_val= item->val_decimal_result(&dec_buf);
+ if (item->maybe_null)
+ {
+ if (item->null_value)
+ {
+ *to++=0;
+ return 0;
+ }
+ *to++= 1;
+ }
+ dec_val->to_binary(to, item->max_length - (item->decimals ? 1 : 0),
+ item->decimals);
+ DBUG_ASSERT(sort_field->original_length == sort_field->length);
+ return sort_field->original_length;
+}
+
+
+uint
+Type_handler_real_result::make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ double value= item->val_result();
+ if (item->maybe_null)
+ {
+ if (item->null_value)
+ {
+ *to++=0;
+ return 0;
+ }
+ *to++= 1;
+ }
+ change_double_for_sort(value, to);
+ DBUG_ASSERT(sort_field->original_length == sort_field->length);
+ return sort_field->original_length;
+}
+
+
+uint
+Type_handler_temporal_result::make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ MYSQL_TIME buf;
+ // This is a temporal type. No nanoseconds. Rounding mode is not important.
+ DBUG_ASSERT(item->cmp_type() == TIME_RESULT);
+ static const Temporal::Options opt(TIME_INVALID_DATES, TIME_FRAC_NONE);
+ if (item->get_date_result(current_thd, &buf, opt))
+ {
+ DBUG_ASSERT(item->maybe_null);
+ DBUG_ASSERT(item->null_value);
+ return make_packed_sort_key_longlong(to, item->maybe_null, true,
+ item->unsigned_flag, 0, sort_field);
+ }
+ return make_packed_sort_key_longlong(to, item->maybe_null, false,
+ item->unsigned_flag, pack_time(&buf),
+ sort_field);
+}
+
+
+uint
+Type_handler_timestamp_common::make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const
+{
+ THD *thd= current_thd;
+ uint binlen= my_timestamp_binary_length(item->decimals);
+ Timestamp_or_zero_datetime_native_null native(thd, item);
+ if (native.is_null() || native.is_zero_datetime())
+ {
+ // NULL or '0000-00-00 00:00:00'
+ if (item->maybe_null)
+ {
+ *to++=0;
+ return 0;
+ }
+ else
+ {
+ bzero(to, binlen);
+ return binlen;
+ }
+ }
+ else
+ {
+ if (item->maybe_null)
+ *to++= 1;
+ if (native.length() != binlen)
+ {
+ /*
+ Some items can return native representation with a different
+ number of fractional digits, e.g.: GREATEST(ts_3, ts_4) can
+ return a value with 3 fractional digits, although its fractional
+ precision is 4. Re-pack with a proper precision now.
+ */
+ Timestamp(native).to_native(&native, item->datetime_precision(thd));
+ }
+ DBUG_ASSERT(native.length() == binlen);
+ memcpy((char *) to, native.ptr(), binlen);
+ return binlen;
+ }
+}
+
+
+/*
+ @brief
+ Reverse the key for DESC clause
+ @param to buffer where values are written
+ @param maybe_null nullability of a column
+ @param sort_field Sort field structure
+ @details
+ used for mem-comparable sort keys
+*/
+
+void reverse_key(uchar *to, const SORT_FIELD_ATTR *sort_field)
+{
+ uint length;
+ if (sort_field->maybe_null && (to[-1]= !to[-1]))
+ {
+ to+= sort_field->length; // don't waste the time reversing all 0's
+ return;
+ }
+ length=sort_field->length;
+ while (length--)
+ {
+ *to = (uchar) (~ *to);
+ to++;
+ }
+}
+
+
+/*
+ @brief
+ Check if packing sort keys is allowed
+ @param THD thread structure
+ @retval
+ TRUE packing allowed
+ FALSE packing not allowed
+*/
+bool SORT_FIELD_ATTR::check_if_packing_possible(THD *thd) const
+{
+ /*
+ Packing not allowed when original length is greater than max_sort_length
+ and we have a complex collation because cutting a prefix is not safe in
+ such a case
+ */
+ if (original_length > thd->variables.max_sort_length &&
+ cs->state & MY_CS_NON1TO1)
+ return false;
+ return true;
+}
+
+
+void SORT_FIELD_ATTR::set_length_and_original_length(THD *thd, uint length_arg)
+{
+ length= length_arg;
+ set_if_smaller(length, thd->variables.max_sort_length);
+ original_length= length_arg;
+}
+
+
+/*
+ Compare function used for packing sort keys
+*/
+
+qsort2_cmp get_packed_keys_compare_ptr()
+{
+ return (qsort2_cmp) compare_packed_sort_keys;
+}
+
+
+/*
+ Compare two varstrings.
+
+ The strings are in this data format:
+
+ [null_byte] [length of string + suffix_bytes] [the string] [suffix_bytes]
+
+ suffix_bytes are used only for binary columns.
+*/
+
+int SORT_FIELD_ATTR::compare_packed_varstrings(uchar *a, size_t *a_len,
+ uchar *b, size_t *b_len)
+{
+ int retval;
+ size_t a_length, b_length;
+ if (maybe_null)
+ {
+ *a_len= *b_len= 1; // NULL bytes are always stored
+ if (*a != *b)
+ {
+ // Note we don't return a proper value in *{a|b}_len for the non-NULL
+ // value but that's ok
+ if (*a == 0)
+ return -1;
+ else
+ return 1;
+ }
+ else
+ {
+ if (*a == 0)
+ return 0;
+ }
+ a++;
+ b++;
+ }
+ else
+ *a_len= *b_len= 0;
+
+ a_length= read_keypart_length(a, length_bytes);
+ b_length= read_keypart_length(b, length_bytes);
+
+ *a_len+= length_bytes + a_length;
+ *b_len+= length_bytes + b_length;
+
+ retval= cs->strnncollsp(a + length_bytes,
+ a_length - suffix_length,
+ b + length_bytes,
+ b_length - suffix_length);
+
+ if (!retval && suffix_length)
+ {
+ DBUG_ASSERT(cs == &my_charset_bin);
+ // comparing the length stored in suffix bytes for binary strings
+ a= a + length_bytes + a_length - suffix_length;
+ b= b + length_bytes + b_length - suffix_length;
+ retval= memcmp(a, b, suffix_length);
+ }
+
+ return retval;
+}
+
+
+/*
+ A value comparison function that has a signature that's suitable for
+ comparing packed values, but actually compares fixed-size values with memcmp.
+
+ This is used for ordering fixed-size columns when the sorting procedure used
+ packed-value format.
+*/
+
+int SORT_FIELD_ATTR::compare_packed_fixed_size_vals(uchar *a, size_t *a_len,
+ uchar *b, size_t *b_len)
+{
+ if (maybe_null)
+ {
+ *a_len=1;
+ *b_len=1;
+ if (*a != *b)
+ {
+ if (*a == 0)
+ return -1;
+ else
+ return 1;
+ }
+ else
+ {
+ if (*a == 0)
+ return 0;
+ }
+ a++;
+ b++;
+ }
+ else
+ *a_len= *b_len= 0;
+
+ *a_len+= length;
+ *b_len+= length;
+ return memcmp(a,b, length);
+}
+
+
+/*
+ @brief
+ Comparison function to compare two packed sort keys
+
+ @param sort_param cmp argument
+ @param a_ptr packed sort key
+ @param b_ptr packed sort key
+
+ @retval
+ >0 key a_ptr greater than b_ptr
+ =0 key a_ptr equal to b_ptr
+ <0 key a_ptr less than b_ptr
+
+*/
+
+int compare_packed_sort_keys(void *sort_param,
+ unsigned char **a_ptr, unsigned char **b_ptr)
+{
+ int retval= 0;
+ size_t a_len, b_len;
+ Sort_param *param= (Sort_param*)sort_param;
+ Sort_keys *sort_keys= param->sort_keys;
+ uchar *a= *a_ptr;
+ uchar *b= *b_ptr;
+
+ a+= Sort_keys::size_of_length_field;
+ b+= Sort_keys::size_of_length_field;
+ for (SORT_FIELD *sort_field= sort_keys->begin();
+ sort_field != sort_keys->end(); sort_field++)
+ {
+ retval= sort_field->is_variable_sized() ?
+ sort_field->compare_packed_varstrings(a, &a_len, b, &b_len) :
+ sort_field->compare_packed_fixed_size_vals(a, &a_len, b, &b_len);
+
+ if (retval)
+ return sort_field->reverse ? -retval : retval;
+
+ a+= a_len;
+ b+= b_len;
+
+ }
+ /*
+ this comparison is done for the case when the sort keys is appended with
+ the ROW_ID pointer. For such cases we don't have addon fields
+ so we can make a memcmp check over both the sort keys
+ */
+ if (!param->using_addon_fields())
+ retval= memcmp(a, b, param->res_length);
+ return retval;
+}
+
+
+/*
+ @brief
+ Store a packed string in the buffer
+
+ @param to buffer
+ @param str packed string value
+ @param cs character set
+
+ @details
+ This function writes to the buffer the packed value of a key_part
+ of the sort key.
+
+ The values written to the buffer are in this order
+ - value for null byte
+ - length of the string
+ - value of the string
+ - suffix length (for binary character set)
+*/
+
+uint
+SORT_FIELD_ATTR::pack_sort_string(uchar *to, const Binary_string *str,
+ CHARSET_INFO *cs) const
+{
+ uchar *orig_to= to;
+ uint32 length, data_length;
+ DBUG_ASSERT(str->length() <= UINT32_MAX);
+ length= (uint32) str->length();
+
+ if (length + suffix_length <= original_length)
+ data_length= length;
+ else
+ data_length= original_length - suffix_length;
+
+ // length stored in lowendian form
+ store_key_part_length(data_length + suffix_length, to, length_bytes);
+ to+= length_bytes;
+ // copying data length bytes to the buffer
+ memcpy(to, (uchar*)str->ptr(), data_length);
+ to+= data_length;
+
+ if (cs == &my_charset_bin && suffix_length)
+ {
+ // suffix length stored in bigendian form
+ store_bigendian(length, to, suffix_length);
+ to+= suffix_length;
+ }
+ return static_cast<uint>(to - orig_to);
+}
+
+
+/*
+ @brief
+ Create a mem-comparable sort key
+
+ @param param sort param structure
+ @param to buffer where values are written
+
+ @retval
+ length of the bytes written including the NULL bytes
+*/
+
+static uint make_sortkey(Sort_param *param, uchar *to)
+{
+ Field *field;
+ SORT_FIELD *sort_field;
+ uchar *orig_to= to;
+
+ for (sort_field=param->local_sortorder.begin() ;
+ sort_field != param->local_sortorder.end() ;
+ sort_field++)
+ {
+ bool maybe_null=0;
+ if ((field=sort_field->field))
+ {
+ // Field
+ field->make_sort_key_part(to, sort_field->length);
+ if ((maybe_null= field->maybe_null()))
+ to++;
+ }
+ else
+ { // Item
+ sort_field->item->type_handler()->make_sort_key_part(to,
+ sort_field->item,
+ sort_field, param);
+ if ((maybe_null= sort_field->item->maybe_null))
+ to++;
+ }
+
+ if (sort_field->reverse)
+ reverse_key(to, sort_field);
+ to+= sort_field->length;
+ }
+
+ DBUG_ASSERT(static_cast<uint>(to - orig_to) <= param->sort_length);
+ return static_cast<uint>(to - orig_to);
+}
+
+
+/*
+ @brief
+ create a compact sort key which can be compared with a comparison
+ function. They are called packed sort keys
+
+ @param param sort param structure
+ @param to buffer where values are written
+
+ @retval
+ length of the bytes written including the NULL bytes
+*/
+
+static uint make_packed_sortkey(Sort_param *param, uchar *to)
+{
+ Field *field;
+ SORT_FIELD *sort_field;
+ uint length;
+ uchar *orig_to= to;
+
+ to+= Sort_keys::size_of_length_field;
+
+ for (sort_field=param->local_sortorder.begin() ;
+ sort_field != param->local_sortorder.end() ;
+ sort_field++)
+ {
+ bool maybe_null=0;
+ if ((field=sort_field->field))
+ {
+ // Field
+ length= field->make_packed_sort_key_part(to, sort_field);
+ if ((maybe_null= field->maybe_null()))
+ to++;
+ }
+ else
+ { // Item
+ Item *item= sort_field->item;
+ length= item->type_handler()->make_packed_sort_key_part(to, item,
+ sort_field,
+ param);
+ if ((maybe_null= sort_field->item->maybe_null))
+ to++;
+ }
+ to+= length;
+ }
+
+ length= static_cast<int>(to - orig_to);
+ DBUG_ASSERT(length <= param->sort_length);
+ Sort_keys::store_sortkey_length(orig_to, length);
+ return length;
+}
diff --git a/sql/filesort.h b/sql/filesort.h
index 5f79a5095cc..9f71da02c96 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -25,9 +25,12 @@ class THD;
struct TABLE;
class Filesort_tracker;
struct SORT_FIELD;
+struct SORT_FIELD_ATTR;
typedef struct st_order ORDER;
class JOIN;
-
+class Addon_fields;
+class Sort_keys;
+
/**
Sorting related info.
@@ -57,6 +60,7 @@ public:
bool sort_positions;
Filesort_tracker *tracker;
+ Sort_keys *sort_keys;
Filesort(ORDER *order_arg, ha_rows limit_arg, bool sort_positions_arg,
SQL_SELECT *select_arg):
@@ -66,14 +70,15 @@ public:
select(select_arg),
own_select(false),
using_pq(false),
- sort_positions(sort_positions_arg)
+ sort_positions(sort_positions_arg),
+ sort_keys(NULL)
{
DBUG_ASSERT(order);
};
~Filesort() { cleanup(); }
/* Prepare ORDER BY list for sorting. */
- uint make_sortorder(THD *thd, JOIN *join, table_map first_table_bit);
+ Sort_keys* make_sortorder(THD *thd, JOIN *join, table_map first_table_bit);
private:
void cleanup();
@@ -87,7 +92,9 @@ class SORT_INFO
public:
SORT_INFO()
- :addon_field(0), record_pointers(0)
+ :addon_fields(NULL), record_pointers(0),
+ sort_keys(NULL),
+ sorted_result_in_fsbuf(FALSE)
{
buffpek.str= 0;
my_b_clear(&io_cache);
@@ -98,9 +105,11 @@ public:
void free_data()
{
close_cached_file(&io_cache);
+ free_addon_buff();
my_free(record_pointers);
my_free(buffpek.str);
- my_free(addon_field);
+ my_free(addon_fields);
+ free_sort_buffer();
}
void reset()
@@ -108,17 +117,27 @@ public:
free_data();
record_pointers= 0;
buffpek.str= 0;
- addon_field= 0;
+ addon_fields= 0;
+ sorted_result_in_fsbuf= false;
}
+ void free_addon_buff();
IO_CACHE io_cache; /* If sorted through filesort */
LEX_STRING buffpek; /* Buffer for buffpek structures */
- LEX_STRING addon_buf; /* Pointer to a buffer if sorted with fields */
- struct st_sort_addon_field *addon_field; /* Pointer to the fields info */
- /* To unpack back */
- void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *);
+ Addon_fields *addon_fields; /* Addon field descriptors */
uchar *record_pointers; /* If sorted in memory */
+ Sort_keys *sort_keys; /* Sort key descriptors*/
+
+ /**
+ If the entire result of filesort fits in memory, we skip the merge phase.
+ We may leave the result in filesort_buffer
+ (indicated by sorted_result_in_fsbuf), or we may strip away
+ the sort keys, and copy the sorted result into a new buffer.
+ @see save_index()
+ */
+ bool sorted_result_in_fsbuf;
+
/*
How many rows in final result.
Also how many rows in record_pointers, if used
@@ -131,27 +150,66 @@ public:
void sort_buffer(Sort_param *param, uint count)
{ filesort_buffer.sort_buffer(param, count); }
- /**
- 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)
+ uchar *get_sorted_record(uint ix)
+ { return filesort_buffer.get_sorted_record(ix); }
+
+ uchar *alloc_sort_buffer(uint num_records, uint record_length)
{ return filesort_buffer.alloc_sort_buffer(num_records, record_length); }
void free_sort_buffer()
{ filesort_buffer.free_sort_buffer(); }
+ bool isfull() const
+ { return filesort_buffer.isfull(); }
void init_record_pointers()
{ filesort_buffer.init_record_pointers(); }
+ void init_next_record_pointer()
+ { filesort_buffer.init_next_record_pointer(); }
+ uchar *get_next_record_pointer()
+ { return filesort_buffer.get_next_record_pointer(); }
+ void adjust_next_record_pointer(uint val)
+ { filesort_buffer.adjust_next_record_pointer(val); }
+
+ Bounds_checked_array<uchar> get_raw_buf()
+ { return filesort_buffer.get_raw_buf(); }
size_t sort_buffer_size() const
{ return filesort_buffer.sort_buffer_size(); }
+ bool is_allocated() const
+ { return filesort_buffer.is_allocated(); }
+ void set_sort_length(uint val)
+ { filesort_buffer.set_sort_length(val); }
+ uint get_sort_length() const
+ { return filesort_buffer.get_sort_length(); }
+
+ bool has_filesort_result_in_memory() const
+ {
+ return record_pointers || sorted_result_in_fsbuf;
+ }
+
+ /// Are we using "addon fields"?
+ bool using_addon_fields() const
+ {
+ return addon_fields != NULL;
+ }
+
+ /// Are we using "packed addon fields"?
+ bool using_packed_addons();
+
+ /**
+ Copies (unpacks) values appended to sorted fields from a buffer back to
+ their regular positions specified by the Field::ptr pointers.
+ @param buff Buffer which to unpack the value from
+ */
+ template<bool Packed_addon_fields>
+ inline void unpack_addon_fields(uchar *buff);
+
+ bool using_packed_sortkeys();
+
friend SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
Filesort_tracker* tracker, JOIN *join,
table_map first_table_bit);
@@ -162,8 +220,12 @@ SORT_INFO *filesort(THD *thd, TABLE *table, Filesort *filesort,
table_map first_table_bit=0);
bool filesort_use_addons(TABLE *table, uint sortlength,
- uint *length, uint *fields, uint *null_fields);
+ uint *length, uint *fields, uint *null_fields,
+ uint *m_packable_length);
void change_double_for_sort(double nr,uchar *to);
+void store_length(uchar *to, uint length, uint pack_length);
+void
+reverse_key(uchar *to, const SORT_FIELD_ATTR *sort_field);
#endif /* FILESORT_INCLUDED */
diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc
index 849e6ee761a..5a51300a0fa 100644
--- a/sql/filesort_utils.cc
+++ b/sql/filesort_utils.cc
@@ -21,6 +21,8 @@
#include "table.h"
+PSI_memory_key key_memory_Filesort_buffer_sort_keys;
+
namespace {
/**
A local helper function. See comments for get_merge_buffers_cost().
@@ -51,9 +53,9 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
// 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;
+ ((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)
@@ -97,81 +99,90 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
# Pointer to allocated buffer
*/
-uchar **Filesort_buffer::alloc_sort_buffer(uint num_records,
- uint record_length)
+uchar *Filesort_buffer::alloc_sort_buffer(uint num_records,
+ uint record_length)
{
size_t buff_size;
- uchar **sort_keys, **start_of_data;
DBUG_ENTER("alloc_sort_buffer");
DBUG_EXECUTE_IF("alloc_sort_buffer_fail",
DBUG_SET("+d,simulate_out_of_memory"););
- buff_size= ((size_t)num_records) * (record_length + sizeof(uchar*));
+ buff_size= ALIGN_SIZE(num_records * (record_length + sizeof(uchar*)));
- if (!m_idx_array.is_null())
+ if (m_rawmem)
{
/*
Reuse old buffer if exists and is large enough
Note that we don't make the buffer smaller, as we want to be
prepared for next subquery iteration.
*/
-
- sort_keys= m_idx_array.array();
- if (buff_size > allocated_size)
+ if (buff_size > m_size_in_bytes)
{
/*
Better to free and alloc than realloc as we don't have to remember
the old values
*/
- my_free(sort_keys);
- if (!(sort_keys= (uchar**) my_malloc(buff_size,
- MYF(MY_THREAD_SPECIFIC))))
+ my_free(m_rawmem);
+ if (!(m_rawmem= (uchar*) my_malloc(key_memory_Filesort_buffer_sort_keys,
+ buff_size, MYF(MY_THREAD_SPECIFIC))))
{
- reset();
+ m_size_in_bytes= 0;
DBUG_RETURN(0);
}
- allocated_size= buff_size;
}
}
else
{
- if (!(sort_keys= (uchar**) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))))
+ if (!(m_rawmem= (uchar*) my_malloc(key_memory_Filesort_buffer_sort_keys,
+ buff_size, MYF(MY_THREAD_SPECIFIC))))
+ {
+ m_size_in_bytes= 0;
DBUG_RETURN(0);
- allocated_size= buff_size;
+ }
+
}
- m_idx_array= Idx_array(sort_keys, num_records);
+ m_size_in_bytes= buff_size;
+ m_record_pointers= reinterpret_cast<uchar**>(m_rawmem) +
+ ((m_size_in_bytes / sizeof(uchar*)) - 1);
+ m_num_records= num_records;
m_record_length= record_length;
- start_of_data= m_idx_array.array() + m_idx_array.size();
- m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
-
- DBUG_RETURN(m_idx_array.array());
+ m_idx= 0;
+ DBUG_RETURN(m_rawmem);
}
void Filesort_buffer::free_sort_buffer()
{
- my_free(m_idx_array.array());
- m_idx_array.reset();
- m_start_of_data= NULL;
+ my_free(m_rawmem);
+ *this= Filesort_buffer();
}
void Filesort_buffer::sort_buffer(const Sort_param *param, uint count)
{
size_t size= param->sort_length;
+ m_sort_keys= get_sort_keys();
+
if (count <= 1 || size == 0)
return;
- uchar **keys= get_sort_keys();
+
+ // don't reverse for PQ, it is already done
+ if (!param->using_pq)
+ reverse_record_pointers();
+
uchar **buffer= NULL;
- if (radixsort_is_appliccable(count, param->sort_length) &&
- (buffer= (uchar**) my_malloc(count*sizeof(char*),
+ if (!param->using_packed_sortkeys() &&
+ radixsort_is_appliccable(count, param->sort_length) &&
+ (buffer= (uchar**) my_malloc(PSI_INSTRUMENT_ME, count*sizeof(char*),
MYF(MY_THREAD_SPECIFIC))))
{
- radixsort_for_str_ptr(keys, count, param->sort_length, buffer);
+ radixsort_for_str_ptr(m_sort_keys, count, param->sort_length, buffer);
my_free(buffer);
return;
}
-
- my_qsort2(keys, count, sizeof(uchar*), get_ptr_compare(size), &size);
+
+ my_qsort2(m_sort_keys, count, sizeof(uchar*),
+ param->get_compare_function(),
+ param->get_compare_argument(&size));
}
diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h
index 1ab1ba2daa8..1962f149893 100644
--- a/sql/filesort_utils.h
+++ b/sql/filesort_utils.h
@@ -46,68 +46,194 @@ double get_merge_many_buffs_cost_fast(ha_rows num_rows,
/**
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.
+ The sort buffer is a contiguous chunk of memory,
+ containing both records to be sorted, and pointers to said records:
+
+ <start of buffer | still unused | end of buffer>
+ |rec 0|record 1 |rec 2| ............ |ptr to rec2|ptr to rec1|ptr to rec0|
+
+ Records will be inserted "left-to-right". Records are not necessarily
+ fixed-size, they can be packed and stored without any "gaps".
+
+ Record pointers will be inserted "right-to-left", as a side-effect
+ of inserting the actual records.
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.
+ With this allocation scheme, and lazy initialization of the pointers,
+ we are able to pack variable-sized records in the buffer,
+ and thus possibly have space for more records than we initially estimated.
+
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_start_of_data(NULL), allocated_size(0)
+ Filesort_buffer() :
+ m_next_rec_ptr(NULL), m_rawmem(NULL), m_record_pointers(NULL),
+ m_sort_keys(NULL),
+ m_num_records(0), m_record_length(0),
+ m_sort_length(0),
+ m_size_in_bytes(0), m_idx(0)
{}
-
- ~Filesort_buffer()
+
+ /** Sort me... */
+ void sort_buffer(const Sort_param *param, uint count);
+
+ /**
+ Reverses the record pointer array, to avoid recording new results for
+ non-deterministic mtr tests.
+ */
+ void reverse_record_pointers()
{
- my_free(m_idx_array.array());
+ if (m_idx < 2) // There is nothing to swap.
+ return;
+ uchar **keys= get_sort_keys();
+ const longlong count= m_idx - 1;
+ for (longlong ix= 0; ix <= count/2; ++ix)
+ {
+ uchar *tmp= keys[count - ix];
+ keys[count - ix] = keys[ix];
+ keys[ix]= tmp;
+ }
}
- bool is_allocated()
+ /**
+ Initializes all the record pointers.
+ */
+ void init_record_pointers()
{
- return m_idx_array.array() != 0;
+ init_next_record_pointer();
+ while (m_idx < m_num_records)
+ (void) get_next_record_pointer();
+ reverse_record_pointers();
}
- void reset()
+
+ /**
+ Prepares the buffer for the next batch of records to process.
+ */
+ void init_next_record_pointer()
{
- m_idx_array.reset();
+ m_idx= 0;
+ m_next_rec_ptr= m_rawmem;
+ m_sort_keys= NULL;
}
- /** Sort me... */
- void sort_buffer(const Sort_param *param, uint count);
+ /**
+ @returns the number of bytes currently in use for data.
+ */
+ size_t space_used_for_data() const
+ {
+ return m_next_rec_ptr ? m_next_rec_ptr - m_rawmem : 0;
+ }
- /// Initializes a record pointer.
- uchar *get_record_buffer(uint idx)
+ /**
+ @returns the number of bytes left in the buffer.
+ */
+ size_t spaceleft() const
{
- m_idx_array[idx]= m_start_of_data + (idx * m_record_length);
- return m_idx_array[idx];
+ DBUG_ASSERT(m_next_rec_ptr >= m_rawmem);
+ const size_t spaceused=
+ (m_next_rec_ptr - m_rawmem) +
+ (static_cast<size_t>(m_idx) * sizeof(uchar*));
+ return m_size_in_bytes - spaceused;
}
- /// Initializes all the record pointers.
- void init_record_pointers()
+ /**
+ Is the buffer full?
+ */
+ bool isfull() const
+ {
+ if (m_idx < m_num_records)
+ return false;
+ return spaceleft() < (m_record_length + sizeof(uchar*));
+ }
+
+ /**
+ Where should the next record be stored?
+ */
+ uchar *get_next_record_pointer()
{
- for (uint ix= 0; ix < m_idx_array.size(); ++ix)
- (void) get_record_buffer(ix);
+ uchar *retval= m_next_rec_ptr;
+ // Save the return value in the record pointer array.
+ m_record_pointers[-m_idx]= m_next_rec_ptr;
+ // Prepare for the subsequent request.
+ m_idx++;
+ m_next_rec_ptr+= m_record_length;
+ return retval;
+ }
+
+ /**
+ Adjusts for actual record length. get_next_record_pointer() above was
+ pessimistic, and assumed that the record could not be packed.
+ */
+ void adjust_next_record_pointer(uint val)
+ {
+ m_next_rec_ptr-= (m_record_length - val);
}
/// Returns total size: pointer array + record buffers.
size_t sort_buffer_size() const
{
- return allocated_size;
+ return m_size_in_bytes;
}
- /// Allocates the buffer, but does *not* initialize pointers.
- uchar **alloc_sort_buffer(uint num_records, uint record_length);
+ bool is_allocated() const
+ {
+ return m_rawmem;
+ }
+
+ /**
+ Allocates the buffer, but does *not* initialize pointers.
+ Total size = (num_records * record_length) + (num_records * sizeof(pointer))
+ space for records space for pointer to records
+ Caller is responsible for raising an error if allocation fails.
+
+ @param num_records Number of records.
+ @param record_length (maximum) size of each record.
+ @returns Pointer to allocated area, or NULL in case of out-of-memory.
+ */
+ uchar *alloc_sort_buffer(uint num_records, uint 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(); }
+ void reset()
+ {
+ m_rawmem= NULL;
+ }
+ /**
+ Used to access the "right-to-left" array of record pointers as an ordinary
+ "left-to-right" array, so that we can pass it directly on to std::sort().
+ */
+ uchar **get_sort_keys()
+ {
+ if (m_idx == 0)
+ return NULL;
+ return &m_record_pointers[1 - m_idx];
+ }
+
+ /**
+ Gets sorted record number ix. @see get_sort_keys()
+ Only valid after buffer has been sorted!
+ */
+ uchar *get_sorted_record(uint ix)
+ {
+ return m_sort_keys[ix];
+ }
+
+ /**
+ @returns The entire buffer, as a character array.
+ This is for reusing the memory for merge buffers.
+ */
+ Bounds_checked_array<uchar> get_raw_buf()
+ {
+ return Bounds_checked_array<uchar>(m_rawmem, m_size_in_bytes);
+ }
/**
We need an assignment operator, see filesort().
@@ -117,20 +243,44 @@ public:
*/
Filesort_buffer &operator=(const Filesort_buffer &rhs)
{
- m_idx_array= rhs.m_idx_array;
+ m_next_rec_ptr= rhs.m_next_rec_ptr;
+ m_rawmem= rhs.m_rawmem;
+ m_record_pointers= rhs.m_record_pointers;
+ m_sort_keys= rhs.m_sort_keys;
+ m_num_records= rhs.m_num_records;
m_record_length= rhs.m_record_length;
- m_start_of_data= rhs.m_start_of_data;
- allocated_size= rhs.allocated_size;
+ m_sort_length= rhs.m_sort_length;
+ m_size_in_bytes= rhs.m_size_in_bytes;
+ m_idx= rhs.m_idx;
return *this;
}
+ uint get_sort_length() const { return m_sort_length; }
+ void set_sort_length(uint val) { m_sort_length= val; }
+
private:
- typedef Bounds_checked_array<uchar*> Idx_array;
+ uchar *m_next_rec_ptr; /// The next record will be inserted here.
+ uchar *m_rawmem; /// The raw memory buffer.
+ uchar **m_record_pointers; /// The "right-to-left" array of record pointers.
+ uchar **m_sort_keys; /// Caches the value of get_sort_keys()
+ uint m_num_records; /// Saved value from alloc_sort_buffer()
+ uint m_record_length; /// Saved value from alloc_sort_buffer()
+ uint m_sort_length; /// The length of the sort key.
+ size_t m_size_in_bytes; /// Size of raw buffer, in bytes.
- Idx_array m_idx_array; /* Pointers to key data */
- uint m_record_length;
- uchar *m_start_of_data; /* Start of key data */
- size_t allocated_size;
+ /**
+ This is the index in the "right-to-left" array of the next record to
+ be inserted into the buffer. It is signed, because we use it in signed
+ expressions like:
+ m_record_pointers[-m_idx];
+ It is longlong rather than int, to ensure that it covers UINT_MAX32
+ without any casting/warning.
+ */
+ longlong m_idx;
};
+int compare_packed_sort_keys(void *sort_keys, unsigned char **a,
+ unsigned char **b);
+qsort2_cmp get_packed_keys_compare_ptr();
+
#endif // FILESORT_UTILS_INCLUDED
diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc
index b3752420316..93561f5fe97 100644
--- a/sql/gcalc_slicescan.cc
+++ b/sql/gcalc_slicescan.cc
@@ -204,7 +204,7 @@ void Gcalc_dyn_list::format_blk(void* block)
Gcalc_dyn_list::Item *Gcalc_dyn_list::alloc_new_blk()
{
- void *new_block= my_malloc(m_blk_size, MYF(MY_WME));
+ void *new_block= my_malloc(PSI_INSTRUMENT_ME, m_blk_size, MYF(MY_WME));
if (!new_block)
return NULL;
*m_blk_hook= new_block;
diff --git a/sql/gen_sql_yacc_ora_yy.cmake b/sql/gen_sql_yacc_ora_yy.cmake
new file mode 100644
index 00000000000..3fdd5d43f8d
--- /dev/null
+++ b/sql/gen_sql_yacc_ora_yy.cmake
@@ -0,0 +1,15 @@
+
+file(READ "${IN}" yytmp)
+
+# Comment out sql_mode=DEFAULT rules and directives (e.g. %expect, %type)
+string(REPLACE "/* Start SQL_MODE_DEFAULT_SPECIFIC */"
+ "/* Start SQL_MODE_DEFAULT_SPECIFIC" yytmp "${yytmp}")
+string(REPLACE "/* End SQL_MODE_DEFAULT_SPECIFIC */"
+ "End SQL_MODE_DEFAULT_SPECIFIC */" yytmp "${yytmp}")
+
+# Uncomment sql_mode=ORACLE rules and directives
+string(REPLACE "/* Start SQL_MODE_ORACLE_SPECIFIC"
+ "/* Start SQL_MODE_ORACLE_SPECIFIC */" yytmp "${yytmp}")
+string(REPLACE "End SQL_MODE_ORACLE_SPECIFIC */"
+ "/* End SQL_MODE_ORACLE_SPECIFIC */" yytmp "${yytmp}")
+file(WRITE "${OUT}" "${yytmp}")
diff --git a/sql/grant.cc b/sql/grant.cc
new file mode 100644
index 00000000000..1ba197bc1d9
--- /dev/null
+++ b/sql/grant.cc
@@ -0,0 +1,108 @@
+/*
+ Copyright (c) 2009, 2020, MariaDB Corporation
+
+ 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-1335 USA */
+
+#include "mariadb.h"
+#include "sql_acl.h"
+
+
+bool Grant_privilege::add_column_privilege(THD *thd,
+ const Lex_ident_sys &name,
+ privilege_t which_grant)
+{
+ String *new_str= new (thd->mem_root) String((const char*) name.str,
+ name.length,
+ system_charset_info);
+ if (unlikely(new_str == NULL))
+ return true;
+ List_iterator <LEX_COLUMN> iter(m_columns);
+ class LEX_COLUMN *point;
+ while ((point=iter++))
+ {
+ if (!my_strcasecmp(system_charset_info,
+ point->column.c_ptr(), new_str->c_ptr()))
+ break;
+ }
+ m_column_privilege_total|= which_grant;
+ if (point)
+ {
+ point->rights |= which_grant;
+ return false;
+ }
+
+ LEX_COLUMN *col= new (thd->mem_root) LEX_COLUMN(*new_str, which_grant);
+ if (unlikely(col == NULL))
+ return true;
+ return m_columns.push_back(col, thd->mem_root);
+}
+
+
+bool Grant_privilege::add_column_list_privilege(THD *thd,
+ List<Lex_ident_sys> &list,
+ privilege_t privilege)
+{
+ Lex_ident_sys *col;
+ List_iterator<Lex_ident_sys> it(list);
+ while ((col= it++))
+ {
+ if (add_column_privilege(thd, *col, privilege))
+ return true;
+ }
+ return false;
+}
+
+
+privilege_t Grant_object_name::all_privileges_by_type() const
+{
+ switch (m_type) {
+ case STAR: return DB_ACLS & ~GRANT_ACL;
+ case IDENT_STAR: return DB_ACLS & ~GRANT_ACL;
+ case STAR_STAR: return GLOBAL_ACLS & ~GRANT_ACL;
+ case TABLE_IDENT: return TABLE_ACLS & ~GRANT_ACL;
+ }
+ return NO_ACL;
+}
+
+
+bool Grant_privilege::set_object_name(THD *thd,
+ const Grant_object_name &ident,
+ SELECT_LEX *sel,
+ privilege_t with_grant_option)
+{
+ DBUG_ASSERT(!m_all_privileges || !m_columns.elements);
+
+ m_db= ident.m_db;
+ if (m_all_privileges)
+ m_object_privilege= ident.all_privileges_by_type();
+ m_object_privilege|= with_grant_option;
+ switch (ident.m_type)
+ {
+ case Lex_grant_object_name::STAR:
+ case Lex_grant_object_name::IDENT_STAR:
+ case Lex_grant_object_name::STAR_STAR:
+ if (!m_all_privileges && m_columns.elements)
+ {
+ // e.g. GRANT SELECT (a) ON db.*
+ my_error(ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0));
+ return true;
+ }
+ return false;
+ case Lex_grant_object_name::TABLE_IDENT:
+ m_db= ident.m_table_ident->db;
+ return !sel->add_table_to_list(thd, ident.m_table_ident,
+ NULL, TL_OPTION_UPDATING);
+ }
+ return false; // Make gcc happy
+}
diff --git a/sql/grant.h b/sql/grant.h
new file mode 100644
index 00000000000..5fbec4469d4
--- /dev/null
+++ b/sql/grant.h
@@ -0,0 +1,99 @@
+/*
+ Copyright (c) 2020, MariaDB Corporation.
+
+ 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-1335 USA */
+
+#ifndef SQL_GRANT_INCLUDED
+#define SQL_GRANT_INCLUDED
+
+#include "lex_string.h"
+#include "privilege.h"
+
+class LEX_COLUMN;
+class Lex_ident_sys;
+class Table_ident;
+
+/*
+ Represents the object name in this standard SQL grammar:
+ GRANT <object privileges> ON <object name>
+*/
+class Grant_object_name
+{
+public:
+ enum Type
+ {
+ STAR, // ON *
+ IDENT_STAR, // ON db.*
+ STAR_STAR, // ON *.*
+ TABLE_IDENT // ON db.name
+ };
+ Lex_cstring m_db;
+ Table_ident *m_table_ident;
+ Type m_type;
+public:
+ Grant_object_name(Table_ident *table_ident)
+ :m_table_ident(table_ident),
+ m_type(TABLE_IDENT)
+ { }
+ Grant_object_name(const LEX_CSTRING &db, Type type)
+ :m_db(db),
+ m_table_ident(NULL),
+ m_type(type)
+ { }
+ privilege_t all_privileges_by_type() const;
+};
+
+
+
+/*
+ Represents standard SQL statements described by:
+ - <grant privilege statement>
+ - <revoke privilege statement>
+*/
+class Grant_privilege
+{
+protected:
+ List<LEX_COLUMN> m_columns;
+ Lex_cstring m_db;
+ privilege_t m_object_privilege;
+ privilege_t m_column_privilege_total;
+ bool m_all_privileges;
+public:
+ Grant_privilege()
+ :m_object_privilege(NO_ACL),
+ m_column_privilege_total(NO_ACL),
+ m_all_privileges(false)
+ { }
+ Grant_privilege(privilege_t privilege, bool all_privileges)
+ :m_object_privilege(privilege),
+ m_column_privilege_total(NO_ACL),
+ m_all_privileges(all_privileges)
+ { }
+ void add_object_privilege(privilege_t privilege)
+ {
+ m_object_privilege|= privilege;
+ }
+ bool add_column_privilege(THD *thd, const Lex_ident_sys &col,
+ privilege_t privilege);
+ bool add_column_list_privilege(THD *thd, List<Lex_ident_sys> &list,
+ privilege_t privilege);
+ bool set_object_name(THD *thd,
+ const Grant_object_name &ident,
+ SELECT_LEX *sel,
+ privilege_t with_grant_option);
+ const List<LEX_COLUMN> & columns() const { return m_columns; }
+};
+
+
+#endif // SQL_GRANT_INCLUDED
diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc
index 326aad439ef..71703cf09b6 100644
--- a/sql/group_by_handler.cc
+++ b/sql/group_by_handler.cc
@@ -40,7 +40,7 @@ int Pushdown_query::execute(JOIN *join)
{
int err;
ha_rows max_limit;
- ha_rows *reset_limit= 0;
+ bool reset_limit= FALSE;
Item **reset_item= 0;
THD *thd= handler->thd;
TABLE *table= handler->table;
@@ -52,11 +52,11 @@ int Pushdown_query::execute(JOIN *join)
if (store_data_in_temp_table)
{
max_limit= join->tmp_table_param.end_write_records;
- reset_limit= &join->unit->select_limit_cnt;
+ reset_limit= TRUE;
}
else
{
- max_limit= join->unit->select_limit_cnt;
+ max_limit= join->unit->lim.get_select_limit();
if (join->unit->fake_select_lex)
reset_item= &join->unit->fake_select_lex->select_limit;
}
@@ -97,7 +97,10 @@ int Pushdown_query::execute(JOIN *join)
{
int error;
/* result < 0 if row was not accepted and should not be counted */
- if (unlikely((error= join->result->send_data(*join->fields))))
+ if (unlikely((error=
+ join->result->send_data_with_check(*join->fields,
+ join->unit,
+ join->send_records))))
{
handler->end_scan();
DBUG_RETURN(error < 0 ? 0 : -1);
@@ -112,7 +115,7 @@ int Pushdown_query::execute(JOIN *join)
break; // LIMIT reached
join->do_send_rows= 0; // Calculate FOUND_ROWS()
if (reset_limit)
- *reset_limit= HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
if (reset_item)
*reset_item= 0;
}
diff --git a/sql/group_by_handler.h b/sql/group_by_handler.h
index 108ebc989d9..ff3b204fa56 100644
--- a/sql/group_by_handler.h
+++ b/sql/group_by_handler.h
@@ -14,6 +14,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#ifndef GROUP_BY_HANDLER_INCLUDED
+#define GROUP_BY_HANDLER_INCLUDED
+
+class Select_limit_counters;
/*
This file implements the group_by_handler interface. This interface
can be used by storage handlers that can intercept summary or GROUP
@@ -56,6 +60,7 @@ struct Query
ORDER *order_by;
Item *having;
// LIMIT
+ Select_limit_counters *limit;
};
class group_by_handler
@@ -100,3 +105,4 @@ public:
virtual void print_error(int error, myf errflag);
};
+#endif //GROUP_BY_HANDLER_INCLUDED
diff --git a/sql/gstream.cc b/sql/gstream.cc
index 4678e85019e..f8e84e70560 100644
--- a/sql/gstream.cc
+++ b/sql/gstream.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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 +23,7 @@
#include "sql_priv.h"
#include "gstream.h"
#include "m_string.h" // LEX_STRING
+#include "mysqld.h"
enum Gis_read_stream::enum_tok_types Gis_read_stream::get_next_toc_type()
{
@@ -107,8 +109,7 @@ bool Gis_read_stream::get_next_number(double *d)
return 1;
}
- *d = my_strntod(m_charset, (char *)m_cur,
- (uint) (m_limit-m_cur), &endptr, &err);
+ *d = m_charset->strntod((char *)m_cur, (uint) (m_limit-m_cur), &endptr, &err);
if (err)
return 1;
if (endptr)
@@ -140,6 +141,7 @@ bool Gis_read_stream::check_next_symbol(char symbol)
void Gis_read_stream::set_error_msg(const char *msg)
{
size_t len= strlen(msg); // ok in this context
- m_err_msg= (char *) my_realloc(m_err_msg, (uint) len + 1, MYF(MY_ALLOW_ZERO_PTR));
+ m_err_msg= (char *) my_realloc(key_memory_Gis_read_stream_err_msg,
+ m_err_msg, (uint) len + 1, MYF(MY_ALLOW_ZERO_PTR));
memcpy(m_err_msg, msg, len + 1);
}
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 9f5f9766605..7f00a194698 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -72,14 +72,15 @@
#define PAR_ENGINES_OFFSET 12
#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | \
HA_REC_NOT_IN_SEQ | \
- HA_CAN_REPAIR)
+ HA_CAN_REPAIR | \
+ HA_REUSES_FILE_NAMES)
#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
HA_DUPLICATE_POS | \
HA_CAN_INSERT_DELAYED | \
HA_READ_BEFORE_WRITE_REMOVAL |\
HA_CAN_TABLES_WITHOUT_ROLLBACK)
-static const char *ha_par_ext= ".par";
+static const char *ha_par_ext= PAR_EXT;
/****************************************************************************
MODULE create/delete handler object
@@ -101,22 +102,43 @@ static const char *ha_partition_ext[]=
ha_par_ext, NullS
};
+static PSI_memory_key key_memory_Partition_share;
+static PSI_memory_key key_memory_partition_sort_buffer;
+static PSI_memory_key key_memory_Partition_admin;
+
+static PSI_memory_key key_memory_ha_partition_file;
+//static PSI_memory_key key_memory_ha_partition_engine_array;
+static PSI_memory_key key_memory_ha_partition_part_ids;
#ifdef HAVE_PSI_INTERFACE
PSI_mutex_key key_partition_auto_inc_mutex;
+PSI_file_key key_file_ha_partition_par;
static PSI_mutex_info all_partition_mutexes[]=
{
{ &key_partition_auto_inc_mutex, "Partition_share::auto_inc_mutex", 0}
};
+static PSI_memory_info all_partitioning_memory[]=
+{ { &key_memory_Partition_share, "Partition_share", 0},
+ { &key_memory_partition_sort_buffer, "partition_sort_buffer", 0},
+ { &key_memory_Partition_admin, "Partition_admin", 0},
+ { &key_memory_ha_partition_file, "ha_partition::file", 0},
+// { &key_memory_ha_partition_engine_array, "ha_partition::engine_array", 0},
+ { &key_memory_ha_partition_part_ids, "ha_partition::part_ids", 0} };
+static PSI_file_info all_partition_file[]=
+{ { &key_file_ha_partition_par, "ha_partition::parfile", 0} };
static void init_partition_psi_keys(void)
{
const char* category= "partition";
int count;
+ count= array_elements(all_partitioning_memory);
+ mysql_memory_register(category, all_partitioning_memory, count);
count= array_elements(all_partition_mutexes);
mysql_mutex_register(category, all_partition_mutexes, count);
+ count= array_elements(all_partition_file);
+ mysql_file_register(category, all_partition_file, count);
}
#endif /* HAVE_PSI_INTERFACE */
@@ -125,7 +147,6 @@ static int partition_initialize(void *p)
handlerton *partition_hton;
partition_hton= (handlerton *)p;
- partition_hton->state= SHOW_OPTION_YES;
partition_hton->db_type= DB_TYPE_PARTITION_DB;
partition_hton->create= partition_create_handler;
partition_hton->partition_flags= partition_flags;
@@ -244,7 +265,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share)
void ha_partition::ha_partition_init()
{
- init_alloc_root(&m_mem_root, "ha_partition", 512, 512, MYF(0));
+ init_alloc_root(PSI_INSTRUMENT_ME, &m_mem_root, 512, 512, MYF(0));
init_handler_variables();
}
@@ -298,7 +319,6 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share,
m_clone_mem_root= clone_mem_root_arg;
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;
}
@@ -328,7 +348,6 @@ void ha_partition::init_handler_variables()
m_reorged_parts= 0;
m_added_file= NULL;
m_tot_parts= 0;
- m_pkey_is_clustered= 0;
m_part_spec.start_part= NO_CURRENT_PART_ID;
m_scan_value= 2;
m_ref_length= 0;
@@ -483,8 +502,7 @@ ha_partition::~ha_partition()
The flag HA_READ_ORDER will be reset for the time being to indicate no
ordered output is available from partition handler indexes. Later a merge
sort will be performed using the underlying handlers.
- 5) primary_key_is_clustered and has_transactions are
- calculated here.
+ 5) has_transactions are calculated here.
*/
@@ -519,19 +537,15 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root)
We create all underlying table handlers here. We do it in this special
method to be able to report allocation errors.
- Set up primary_key_is_clustered and
- has_transactions since they are called often in all kinds of places,
+ Set up has_transactions since they are called often in all kinds of places,
other parameters are calculated on demand.
Verify that all partitions have the same table_flags.
*/
check_table_flags= m_file[0]->ha_table_flags();
- m_pkey_is_clustered= TRUE;
file_array= m_file;
do
{
file= *file_array;
- if (!file->primary_key_is_clustered())
- m_pkey_is_clustered= FALSE;
if (check_table_flags != file->ha_table_flags())
{
my_error(ER_MIX_HANDLER_ERROR, MYF(0));
@@ -615,7 +629,8 @@ int ha_partition::rename_table(const char *from, const char *to)
SYNOPSIS
create_partitioning_metadata()
- name Full path of table name
+ path Path to the new frm file (without ext)
+ old_p Path to the old frm file (without ext)
create_info Create info generated for CREATE TABLE
RETURN VALUE
@@ -631,9 +646,10 @@ int ha_partition::rename_table(const char *from, const char *to)
*/
int ha_partition::create_partitioning_metadata(const char *path,
- const char *old_path,
- int action_flag)
+ const char *old_path,
+ chf_create_flags action_flag)
{
+ partition_element *part;
DBUG_ENTER("ha_partition::create_partitioning_metadata");
/*
@@ -649,9 +665,10 @@ int ha_partition::create_partitioning_metadata(const char *path,
strxmov(name, path, ha_par_ext, NullS);
strxmov(old_name, old_path, ha_par_ext, NullS);
if ((action_flag == CHF_DELETE_FLAG &&
- mysql_file_delete(key_file_partition, name, MYF(MY_WME))) ||
+ mysql_file_delete(key_file_ha_partition_par, name, MYF(MY_WME))) ||
(action_flag == CHF_RENAME_FLAG &&
- mysql_file_rename(key_file_partition, old_name, name, MYF(MY_WME))))
+ mysql_file_rename(key_file_ha_partition_par, old_name, name,
+ MYF(MY_WME))))
{
DBUG_RETURN(TRUE);
}
@@ -664,6 +681,21 @@ int ha_partition::create_partitioning_metadata(const char *path,
DBUG_RETURN(1);
}
}
+
+ /* m_part_info is only NULL when we failed to create a partition table */
+ if (m_part_info)
+ {
+ part= m_part_info->partitions.head();
+ /* part->engine_type may be 0 when we failed to create the partition */
+ if (part->engine_type &&
+ (part->engine_type)->create_partitioning_metadata &&
+ ((part->engine_type)->create_partitioning_metadata)(path, old_path,
+ action_flag))
+ {
+ my_error(ER_CANT_CREATE_HANDLER_FILE, MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
DBUG_RETURN(0);
}
@@ -711,7 +743,7 @@ int ha_partition::create(const char *name, TABLE *table_arg,
/* Not allowed to create temporary partitioned tables */
if (create_info && create_info->tmp_table())
{
- my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
+ my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0), "CREATE TEMPORARY TABLE");
DBUG_RETURN(TRUE);
}
@@ -779,7 +811,7 @@ create_error:
{
if (!create_partition_name(name_buff, sizeof(name_buff), path,
name_buffer_ptr, NORMAL_PART_NAME, FALSE))
- (void) (*file)->ha_delete_table((const char*) name_buff);
+ (void) (*file)->delete_table((const char*) name_buff);
name_buffer_ptr= strend(name_buffer_ptr) + 1;
}
handler::delete_table(name);
@@ -848,7 +880,7 @@ int ha_partition::drop_partitions(const char *path)
error= ret_error;
file= m_file[part];
DBUG_PRINT("info", ("Drop subpartition %s", part_name_buff));
- if (unlikely((ret_error= file->ha_delete_table(part_name_buff))))
+ if (unlikely((ret_error= file->delete_table(part_name_buff))))
error= ret_error;
if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
entry_pos)))
@@ -865,7 +897,7 @@ int ha_partition::drop_partitions(const char *path)
{
file= m_file[i];
DBUG_PRINT("info", ("Drop partition %s", part_name_buff));
- if (unlikely((ret_error= file->ha_delete_table(part_name_buff))))
+ if (unlikely((ret_error= file->delete_table(part_name_buff))))
error= ret_error;
if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
entry_pos)))
@@ -957,7 +989,7 @@ int ha_partition::rename_partitions(const char *path)
NORMAL_PART_NAME))))
error= ret_error;
DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff));
- if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
+ if (unlikely((ret_error= file->delete_table(norm_name_buff))))
error= ret_error;
else if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
entry_pos)))
@@ -978,7 +1010,7 @@ int ha_partition::rename_partitions(const char *path)
else
{
DBUG_PRINT("info", ("Delete partition %s", norm_name_buff));
- if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
+ if (unlikely((ret_error= file->delete_table(norm_name_buff))))
error= ret_error;
else if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
entry_pos)))
@@ -1039,7 +1071,7 @@ int ha_partition::rename_partitions(const char *path)
{
file= m_reorged_file[part_count++];
DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff));
- if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
+ if (unlikely((ret_error= file->delete_table(norm_name_buff))))
error= ret_error;
else if (unlikely(deactivate_ddl_log_entry(sub_elem->log_entry->
entry_pos)))
@@ -1086,7 +1118,7 @@ int ha_partition::rename_partitions(const char *path)
{
file= m_reorged_file[part_count++];
DBUG_PRINT("info", ("Delete partition %s", norm_name_buff));
- if (unlikely((ret_error= file->ha_delete_table(norm_name_buff))))
+ if (unlikely((ret_error= file->delete_table(norm_name_buff))))
error= ret_error;
else if (unlikely(deactivate_ddl_log_entry(part_elem->log_entry->
entry_pos)))
@@ -1162,7 +1194,17 @@ int ha_partition::analyze(THD *thd, HA_CHECK_OPT *check_opt)
{
DBUG_ENTER("ha_partition::analyze");
- DBUG_RETURN(handle_opt_partitions(thd, check_opt, ANALYZE_PARTS));
+ int result= handle_opt_partitions(thd, check_opt, ANALYZE_PARTS);
+
+ if ((result == 0) && m_file[0]
+ && (m_file[0]->ha_table_flags() & HA_ONLINE_ANALYZE))
+ {
+ /* If this is ANALYZE TABLE that will not force table definition cache
+ eviction, update statistics for the partition handler. */
+ this->info(HA_STATUS_CONST | HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ }
+
+ DBUG_RETURN(result);
}
@@ -1333,7 +1375,7 @@ bool print_admin_msg(THD* thd, uint len,
char *msgbuf;
bool error= true;
- if (!(msgbuf= (char*) my_malloc(len, MYF(0))))
+ if (!(msgbuf= (char*) my_malloc(key_memory_Partition_admin, len, MYF(0))))
return true;
va_start(args, fmt);
msg_length= my_vsnprintf(msgbuf, len, fmt, args);
@@ -1590,6 +1632,7 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
if (!(file->ht->flags & HTON_CAN_READ_CONNECT_STRING_IN_PARTITION))
tbl->s->connect_string= p_elem->connect_string;
+ create_info->options|= HA_CREATE_TMP_ALTER;
if ((error= file->ha_create(part_name, tbl, create_info)))
{
/*
@@ -1605,7 +1648,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
}
DBUG_PRINT("info", ("partition %s created", part_name));
if (unlikely((error= file->ha_open(tbl, part_name, m_mode,
- m_open_test_lock | HA_OPEN_NO_PSI_CALL))))
+ m_open_test_lock | HA_OPEN_NO_PSI_CALL |
+ HA_OPEN_FOR_CREATE))))
goto error_open;
DBUG_PRINT("info", ("partition %s opened", part_name));
@@ -1626,7 +1670,7 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
error_external_lock:
(void) file->ha_close();
error_open:
- (void) file->ha_delete_table(part_name);
+ (void) file->delete_table(part_name);
error_create:
DBUG_RETURN(error);
}
@@ -1669,10 +1713,10 @@ void ha_partition::cleanup_new_partition(uint part_count)
handler **file= m_added_file;
while ((part_count > 0) && (*file))
{
- (*file)->ha_external_lock(thd, F_UNLCK);
+ (*file)->ha_external_unlock(thd);
(*file)->ha_close();
- /* Leave the (*file)->ha_delete_table(part_name) to the ddl-log */
+ /* Leave the (*file)->delete_table(part_name) to the ddl-log */
file++;
part_count--;
@@ -2112,12 +2156,10 @@ int ha_partition::copy_partitions(ulonglong * const copied,
}
else
{
- THD *thd= ha_thd();
/* Copy record to new handler */
(*copied)++;
- tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ DBUG_ASSERT(!m_new_file[new_part]->row_logging);
result= m_new_file[new_part]->ha_write_row(m_rec0);
- reenable_binlog(thd);
if (result)
goto error;
}
@@ -2271,7 +2313,7 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
@param table_arg TABLE object
@param share New share to use
- @note Is used in error handling in ha_delete_table.
+ @note Is used in error handling in delete_table.
All handlers should exist (lock_partitions should not be used)
*/
@@ -2329,7 +2371,7 @@ char *ha_partition::update_table_comment(const char *comment)
Handle delete and rename table
@param from Full path of old table
- @param to Full path of new table
+ @param to Full path of new table. May be NULL in case of delete
@return Operation status
@retval >0 Error
@@ -2354,14 +2396,20 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
const char *to_path= NULL;
uint i;
handler **file, **abort_file;
+ THD *thd= ha_thd();
DBUG_ENTER("ha_partition::del_ren_table");
- if (get_from_handler_file(from, ha_thd()->mem_root, false))
- DBUG_RETURN(TRUE);
+ if (get_from_handler_file(from, thd->mem_root, false))
+ DBUG_RETURN(my_errno ? my_errno : ENOENT);
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;
+ /* The command should be logged with IF EXISTS if using a shared table */
+ if (m_file[0]->ht->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
+ thd->replication_flags|= OPTION_IF_EXISTS;
+
if (to == NULL)
{
/*
@@ -2371,6 +2419,11 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
if (unlikely((error= handler::delete_table(from))))
DBUG_RETURN(error);
}
+
+ if (ha_check_if_updates_are_ignored(thd, partition_ht(),
+ to ? "RENAME" : "DROP"))
+ DBUG_RETURN(0);
+
/*
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.
@@ -2401,7 +2454,7 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
}
else // delete branch
{
- error= (*file)->ha_delete_table(from_buff);
+ error= (*file)->delete_table(from_buff);
}
name_buffer_ptr= strend(name_buffer_ptr) + 1;
if (unlikely(error))
@@ -2417,7 +2470,33 @@ uint ha_partition::del_ren_table(const char *from, const char *to)
goto rename_error;
}
}
+
+ /* Update .par file in the handlers that supports it */
+ if ((*m_file)->ht->create_partitioning_metadata)
+ {
+ error= (*m_file)->ht->create_partitioning_metadata(to, from,
+ to == NULL ?
+ CHF_DELETE_FLAG :
+ CHF_RENAME_FLAG);
+ DBUG_EXECUTE_IF("failed_create_partitioning_metadata",
+ { my_message_sql(ER_OUT_OF_RESOURCES,"Simulated crash",MYF(0));
+ error= 1;
+ });
+ if (error)
+ {
+ if (to)
+ {
+ (void) handler::rename_table(to, from);
+ (void) (*m_file)->ht->create_partitioning_metadata(from, to,
+ CHF_RENAME_FLAG);
+ goto rename_error;
+ }
+ else
+ save_error=error;
+ }
+ }
DBUG_RETURN(save_error);
+
rename_error:
name_buffer_ptr= m_name_buffer_ptr;
for (abort_file= file, file= m_file; file < abort_file; file++)
@@ -2554,7 +2633,7 @@ register_query_cache_dependant_tables(THD *thd,
/* we store the end \0 as part of the key */
end= strmov(engine_pos, sub_elem->partition_name) + 1;
length= (uint)(end - engine_key);
- /* Copy the suffix also to query cache key */
+ /* Copy the suffix and end 0 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,
@@ -2570,7 +2649,7 @@ register_query_cache_dependant_tables(THD *thd,
{
char *end= engine_pos+1; // copy end \0
uint length= (uint)(end - engine_key);
- /* Copy the suffix also to query cache key */
+ /* Copy the suffix and end 0 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,
@@ -2754,7 +2833,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;
- if (!(file_buffer= (uchar *) my_malloc(tot_len_byte, MYF(MY_ZEROFILL))))
+ if (!(file_buffer= (uchar *) my_malloc(key_memory_ha_partition_file,
+ tot_len_byte, MYF(MY_ZEROFILL))))
DBUG_RETURN(TRUE);
engine_array= (file_buffer + PAR_ENGINES_OFFSET);
name_buffer_ptr= (char*) (engine_array + tot_partition_words * PAR_WORD_SIZE
@@ -2809,7 +2889,7 @@ bool ha_partition::create_handler_file(const char *name)
to be used at open, delete_table and rename_table
*/
fn_format(file_name, name, "", ha_par_ext, MY_APPEND_EXT);
- if ((file= mysql_file_create(key_file_partition,
+ if ((file= mysql_file_create(key_file_ha_partition_par,
file_name, CREATE_MODE, O_RDWR | O_TRUNC,
MYF(MY_WME))) >= 0)
{
@@ -2834,7 +2914,7 @@ bool ha_partition::create_handler_file(const char *name)
}
(void) mysql_file_close(file, MYF(0));
if (result)
- mysql_file_delete(key_file_partition, file_name, MYF(MY_WME));
+ mysql_file_delete(key_file_ha_partition_par, file_name, MYF(MY_WME));
}
else
result= TRUE;
@@ -2998,7 +3078,7 @@ bool ha_partition::read_par_file(const char *name)
fn_format(buff, name, "", ha_par_ext, MY_APPEND_EXT);
/* Following could be done with mysql_file_stat to read in whole file */
- if ((file= mysql_file_open(key_file_partition,
+ if ((file= mysql_file_open(key_file_ha_partition_par,
buff, O_RDONLY | O_SHARE, MYF(0))) < 0)
DBUG_RETURN(TRUE);
if (mysql_file_read(file, (uchar *) &buff[0], PAR_WORD_SIZE, MYF(MY_NABP)))
@@ -3224,7 +3304,7 @@ bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id,
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,
+ if (!my_multi_malloc(key_memory_Partition_share, MY_WME,
&part_def, sizeof(PART_NAME_DEF),
&part_name, part_name_length + 1,
NULL))
@@ -3272,10 +3352,10 @@ bool ha_partition::populate_partition_name_hash()
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))
+ if (my_hash_init(key_memory_Partition_share,
+ &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);
@@ -3511,7 +3591,8 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
if (!m_part_ids_sorted_by_num_of_records)
{
if (!(m_part_ids_sorted_by_num_of_records=
- (uint32*) my_malloc(m_tot_parts * sizeof(uint32), MYF(MY_WME))))
+ (uint32*) my_malloc(key_memory_ha_partition_part_ids,
+ m_tot_parts * sizeof(uint32), MYF(MY_WME))))
DBUG_RETURN(error);
uint32 i;
/* Initialize it with all partition ids. */
@@ -3529,7 +3610,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
/* Allocate memory used with MMR */
if (!(m_range_info= (void **)
- my_multi_malloc(MYF(MY_WME),
+ my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
&m_range_info, sizeof(range_id_t) * m_tot_parts,
&m_stock_range_seq, sizeof(uint) * m_tot_parts,
&m_mrr_buffer, sizeof(HANDLER_BUFFER) * m_tot_parts,
@@ -3540,8 +3621,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
&m_part_mrr_range_current,
sizeof(PARTITION_PART_KEY_MULTI_RANGE *) * m_tot_parts,
&m_partition_part_key_multi_range_hld,
- sizeof(PARTITION_PART_KEY_MULTI_RANGE_HLD) *
- m_tot_parts,
+ sizeof(PARTITION_PART_KEY_MULTI_RANGE_HLD) * m_tot_parts,
NullS)))
goto err_alloc;
@@ -3685,6 +3765,7 @@ err_alloc:
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()
{
@@ -3700,22 +3781,39 @@ void ha_partition::unbind_psi()
DBUG_VOID_RETURN;
}
-void ha_partition::rebind_psi()
+int ha_partition::rebind()
{
uint i;
- DBUG_ENTER("ha_partition::rebind_psi");
- handler::rebind_psi();
+ DBUG_ENTER("ha_partition::rebind");
+ if (int error= handler::rebind())
+ DBUG_RETURN(error);
for (i= 0; i < m_tot_parts; i++)
{
DBUG_ASSERT(m_file[i] != NULL);
- m_file[i]->rebind_psi();
+ if (int error= m_file[i]->rebind())
+ {
+ while (i)
+ m_file[--i]->unbind_psi();
+ handler::unbind_psi();
+ DBUG_RETURN(error);
+ }
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
#endif /* HAVE_M_PSI_PER_PARTITION */
+/*
+ Check if the table definition has changed for the part tables
+ We use the first partition for the check.
+*/
+
+int ha_partition::discover_check_version()
+{
+ return m_file[0]->discover_check_version();
+}
+
/**
Clone the open and locked partitioning handler.
@@ -3977,7 +4075,7 @@ err_handler:
j < i;
j= bitmap_get_next_set(&m_locked_partitions, j))
{
- (void) m_file[j]->ha_external_lock(thd, F_UNLCK);
+ (void) m_file[j]->ha_external_unlock(thd);
}
bitmap_clear_all(&m_locked_partitions);
DBUG_RETURN(error);
@@ -4284,7 +4382,7 @@ int ha_partition::write_row(const uchar * buf)
bool have_auto_increment= table->next_number_field && buf == table->record[0];
my_bitmap_map *old_map;
THD *thd= ha_thd();
- sql_mode_t saved_sql_mode= thd->variables.sql_mode;
+ Sql_mode_save sms(thd);
bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null;
DBUG_ENTER("ha_partition::write_row");
DBUG_PRINT("enter", ("partition this: %p", this));
@@ -4323,7 +4421,6 @@ int ha_partition::write_row(const uchar * buf)
thd->variables.sql_mode|= MODE_NO_AUTO_VALUE_ON_ZERO;
}
}
-
old_map= dbug_tmp_use_all_columns(table, table->read_set);
error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
dbug_tmp_restore_column_map(table->read_set, old_map);
@@ -4341,16 +4438,15 @@ int ha_partition::write_row(const uchar * buf)
}
m_last_part= part_id;
DBUG_PRINT("info", ("Insert in partition %u", part_id));
+
start_part_bulk_insert(thd, part_id);
- tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ DBUG_ASSERT(!m_file[part_id]->row_logging);
error= m_file[part_id]->ha_write_row(buf);
if (have_auto_increment && !table->s->next_number_keypart)
set_auto_increment_if_higher(table->next_number_field);
- reenable_binlog(thd);
exit:
- thd->variables.sql_mode= saved_sql_mode;
table->auto_increment_field_not_null= saved_auto_inc_field_not_null;
DBUG_RETURN(error);
}
@@ -4427,12 +4523,11 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data)
m_last_part= new_part_id;
start_part_bulk_insert(thd, new_part_id);
+ DBUG_ASSERT(!m_file[new_part_id]->row_logging);
if (new_part_id == old_part_id)
{
DBUG_PRINT("info", ("Update in partition %u", (uint) new_part_id));
- tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
error= m_file[new_part_id]->ha_update_row(old_data, new_data);
- reenable_binlog(thd);
goto exit;
}
else
@@ -4451,16 +4546,12 @@ int ha_partition::update_row(const uchar *old_data, const uchar *new_data)
table->next_number_field= NULL;
DBUG_PRINT("info", ("Update from partition %u to partition %u",
(uint) old_part_id, (uint) new_part_id));
- tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
error= m_file[new_part_id]->ha_write_row((uchar*) new_data);
- reenable_binlog(thd);
table->next_number_field= saved_next_number_field;
if (unlikely(error))
goto exit;
- tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
error= m_file[old_part_id]->ha_delete_row(old_data);
- reenable_binlog(thd);
if (unlikely(error))
goto exit;
}
@@ -4520,13 +4611,13 @@ exit:
int ha_partition::delete_row(const uchar *buf)
{
int error;
- THD *thd= ha_thd();
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));
#ifndef DBUG_OFF
+ THD* thd = ha_thd();
/*
The protocol for deleting a row is:
1) position the handler (cursor) on the row to be deleted,
@@ -4562,9 +4653,8 @@ int ha_partition::delete_row(const uchar *buf)
if (!bitmap_is_set(&(m_part_info->lock_partitions), m_last_part))
DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS);
- tmp_disable_binlog(thd);
+ DBUG_ASSERT(!m_file[m_last_part]->row_logging);
error= m_file[m_last_part]->ha_delete_row(buf);
- reenable_binlog(thd);
DBUG_RETURN(error);
}
@@ -5324,7 +5414,8 @@ bool ha_partition::init_record_priority_queue()
/* Allocate a key for temporary use when setting up the scan. */
alloc_len+= table_share->max_key_length;
- if (!(m_ordered_rec_buffer= (uchar*)my_malloc(alloc_len, MYF(MY_WME))))
+ if (!(m_ordered_rec_buffer= (uchar*)my_malloc(key_memory_partition_sort_buffer,
+ alloc_len, MYF(MY_WME))))
DBUG_RETURN(true);
/*
@@ -5412,7 +5503,7 @@ int ha_partition::index_init(uint inx, bool sorted)
m_ordered= sorted;
m_ordered_scan_ongoing= FALSE;
m_curr_key_info[0]= table->key_info+inx;
- if (m_pkey_is_clustered && table->s->primary_key != MAX_KEY)
+ if (pk_is_clustering_key(table->s->primary_key))
{
/*
if PK is clustered, then the key cmp must use the pk to
@@ -5823,6 +5914,7 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index,
{
int error= HA_ERR_KEY_NOT_FOUND;
DBUG_ENTER("ha_partition::index_read_idx_map");
+ decrement_statistics(&SSV::ha_read_key_count);
if (find_flag == HA_READ_KEY_EXACT)
{
@@ -6076,9 +6168,8 @@ int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
if (!m_mrr_range_first)
{
if (!(m_mrr_range_first= (PARTITION_KEY_MULTI_RANGE *)
- my_multi_malloc(MYF(MY_WME),
- &m_mrr_range_current,
- sizeof(PARTITION_KEY_MULTI_RANGE),
+ my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
+ &m_mrr_range_current, sizeof(PARTITION_KEY_MULTI_RANGE),
NullS)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@@ -6095,9 +6186,8 @@ int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
if (!m_part_mrr_range_first[i])
{
if (!(m_part_mrr_range_first[i]= (PARTITION_PART_KEY_MULTI_RANGE *)
- my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &m_part_mrr_range_current[i],
- sizeof(PARTITION_PART_KEY_MULTI_RANGE),
+ my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME | MY_ZEROFILL),
+ &m_part_mrr_range_current[i], sizeof(PARTITION_PART_KEY_MULTI_RANGE),
NullS)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
@@ -6133,7 +6223,7 @@ int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
if (m_mrr_range_current->key[0])
my_free(m_mrr_range_current->key[0]);
if (!(m_mrr_range_current->key[0]=
- (uchar *) my_malloc(length, MYF(MY_WME))))
+ (uchar *) my_malloc(PSI_INSTRUMENT_ME, length, MYF(MY_WME))))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
m_mrr_range_current->length[0]= length;
}
@@ -6157,7 +6247,7 @@ int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
if (m_mrr_range_current->key[1])
my_free(m_mrr_range_current->key[1]);
if (!(m_mrr_range_current->key[1]=
- (uchar *) my_malloc(length, MYF(MY_WME))))
+ (uchar *) my_malloc(PSI_INSTRUMENT_ME, length, MYF(MY_WME))))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
m_mrr_range_current->length[1]= length;
}
@@ -6191,7 +6281,7 @@ int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
{
PARTITION_PART_KEY_MULTI_RANGE *tmp_part_mrr_range;
if (!(tmp_part_mrr_range= (PARTITION_PART_KEY_MULTI_RANGE *)
- my_malloc(sizeof(PARTITION_PART_KEY_MULTI_RANGE),
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(PARTITION_PART_KEY_MULTI_RANGE),
MYF(MY_WME | MY_ZEROFILL))))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@@ -6211,7 +6301,7 @@ int ha_partition::multi_range_key_create_key(RANGE_SEQ_IF *seq,
/* Add end of range sentinel */
PARTITION_KEY_MULTI_RANGE *tmp_mrr_range;
if (!(tmp_mrr_range= (PARTITION_KEY_MULTI_RANGE *)
- my_malloc(sizeof(PARTITION_KEY_MULTI_RANGE), MYF(MY_WME))))
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(PARTITION_KEY_MULTI_RANGE), MYF(MY_WME))))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
tmp_mrr_range->id= m_mrr_range_current->id + 1;
@@ -6484,7 +6574,7 @@ int ha_partition::multi_range_read_init(RANGE_SEQ_IF *seq,
if (m_mrr_full_buffer)
my_free(m_mrr_full_buffer);
if (!(m_mrr_full_buffer=
- (uchar *) my_malloc(m_mrr_new_full_buffer_size, MYF(MY_WME))))
+ (uchar *) my_malloc(PSI_INSTRUMENT_ME, m_mrr_new_full_buffer_size, MYF(MY_WME))))
{
m_mrr_full_buffer_size= 0;
error= HA_ERR_OUT_OF_MEM;
@@ -6882,11 +6972,9 @@ FT_INFO *ha_partition::ft_init_ext(uint flags, uint inx, String *key)
{
FT_INFO **tmp_ft_info;
if (!(ft_target= (st_partition_ft_info *)
- my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &ft_target,
- sizeof(st_partition_ft_info),
- &tmp_ft_info,
- sizeof(FT_INFO *) * m_tot_parts,
+ my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME | MY_ZEROFILL),
+ &ft_target, sizeof(st_partition_ft_info),
+ &tmp_ft_info, sizeof(FT_INFO *) * m_tot_parts,
NullS)))
{
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATAL));
@@ -7170,8 +7258,6 @@ bool ha_partition::check_parallel_search()
if (order_field && order_field->table == table_list->table)
{
Field *part_field= m_part_info->full_part_field_array[0];
- if (set_top_table_fields)
- order_field= top_table_field[order_field->field_index];
DBUG_PRINT("info",("partition order_field: %p", order_field));
DBUG_PRINT("info",("partition part_field: %p", part_field));
if (part_field == order_field)
@@ -7215,8 +7301,6 @@ bool ha_partition::check_parallel_search()
if (group_field && group_field->table == table_list->table)
{
Field *part_field= m_part_info->full_part_field_array[0];
- if (set_top_table_fields)
- group_field= top_table_field[group_field->field_index];
DBUG_PRINT("info",("partition group_field: %p", group_field));
DBUG_PRINT("info",("partition part_field: %p", part_field));
if (part_field == group_field)
@@ -9482,19 +9566,25 @@ double ha_partition::keyread_time(uint inx, uint ranges, ha_rows rows)
if start_key matches any rows.
*/
-ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
+ha_rows ha_partition::records_in_range(uint inx, const key_range *min_key,
+ const key_range *max_key,
+ page_range *pages)
{
ha_rows min_rows_to_check, rows, estimated_rows=0, checked_rows= 0;
uint partition_index= 0, part_id;
+ page_range ignore_pages;
DBUG_ENTER("ha_partition::records_in_range");
+ /* Don't calculate pages of more than one active partition */
+ if (bitmap_bits_set(&m_part_info->read_partitions) != 1)
+ pages= &ignore_pages;
+
min_rows_to_check= min_rows_for_estimate();
while ((part_id= get_biggest_used_partition(&partition_index))
!= NO_CURRENT_PART_ID)
{
- rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
+ rows= m_file[part_id]->records_in_range(inx, min_key, max_key, pages);
DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows,
(ulong) m_file[part_id]->stats.records));
@@ -9709,8 +9799,7 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array)
}
/* 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);
+ my_charset_bin.hash_sort(field->ptr, len, &nr1, &nr2);
/* Done with this field, continue with next one. */
continue;
}
@@ -9733,8 +9822,7 @@ uint32 ha_partition::calculate_key_hash_value(Field **field_array)
}
/* 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);
+ my_charset_latin1.hash_sort(field->ptr, len, &nr1, &nr2);
continue;
}
/* New types in mysql-5.6. */
@@ -9925,8 +10013,13 @@ void ha_partition::print_error(int error, myf errflag)
/* fall through to generic error handling. */
}
- /* In case m_file has not been initialized, like in bug#42438 */
- if (m_file)
+ /*
+ We choose a main handler's print_error if:
+ * m_file has not been initialized, like in bug#42438
+ * lookup_errkey is set, which means that an error has occured in the
+ main handler, not in individual partitions
+ */
+ if (m_file && lookup_errkey == (uint)-1)
{
if (m_last_part >= m_tot_parts)
{
@@ -10306,19 +10399,6 @@ end:
}
-void ha_partition::notify_table_changed()
-{
- handler **file;
-
- DBUG_ENTER("ha_partition::notify_table_changed");
-
- for (file= m_file; *file; file++)
- (*file)->ha_notify_table_changed();
-
- DBUG_VOID_RETURN;
-}
-
-
uint ha_partition::min_of_the_max_uint(
uint (handler::*operator_func)(void) const) const
{
@@ -10952,9 +11032,9 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
/*
If the engine supports transactions, the failure will be
- rollbacked.
+ rolled back
*/
- if (!m_file[correct_part_id]->has_transactions())
+ if (!m_file[correct_part_id]->has_transactions_and_rollback())
{
/* Log this error, so the DBA can notice it and fix it! */
sql_print_error("Table '%-192s' failed to move/insert a row"
@@ -10978,7 +11058,7 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool do_repair)
/* Delete row from wrong partition. */
if ((result= m_file[read_part_id]->ha_delete_row(m_rec0)))
{
- if (m_file[correct_part_id]->has_transactions())
+ if (m_file[correct_part_id]->has_transactions_and_rollback())
break;
/*
We have introduced a duplicate, since we failed to remove it
@@ -11156,26 +11236,6 @@ const COND *ha_partition::cond_push(const COND *cond)
COND *res_cond= NULL;
DBUG_ENTER("ha_partition::cond_push");
- if (set_top_table_fields)
- {
- /*
- We want to do this in a separate loop to not come into a situation
- where we have only done cond_push() to some of the tables
- */
- 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 (bitmap_is_set(&m_opened_partitions, i))
- {
- if ((m_file[i]->set_top_table_and_fields(top_table,
- top_table_field,
- top_table_fields)))
- DBUG_RETURN(cond); // Abort cond push, no error
- }
- }
- }
-
for (i= bitmap_get_first_set(&m_partitions_to_reset);
i < m_tot_parts;
i= bitmap_get_next_set(&m_partitions_to_reset, i))
@@ -11400,6 +11460,12 @@ int ha_partition::end_bulk_delete()
}
+bool ha_partition::check_if_updates_are_ignored(const char *op) const
+{
+ return (handler::check_if_updates_are_ignored(op) ||
+ ha_check_if_updates_are_ignored(table->in_use, partition_ht(), op));
+}
+
/**
Perform initialization for a direct update request.
@@ -11817,30 +11883,6 @@ int ha_partition::info_push(uint info_type, void *info)
}
-void ha_partition::clear_top_table_fields()
-{
- uint i;
- DBUG_ENTER("ha_partition::clear_top_table_fields");
-
- if (set_top_table_fields)
- {
- set_top_table_fields= FALSE;
- top_table= NULL;
- top_table_field= NULL;
- top_table_fields= 0;
- 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 (bitmap_is_set(&m_opened_partitions, i))
- {
- m_file[i]->clear_top_table_fields();
- }
- }
- }
- DBUG_VOID_RETURN;
-}
-
bool
ha_partition::can_convert_string(const Field_string* field,
const Column_definition& new_type) const
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index 7d10dc19683..8d0557f4ae4 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -22,7 +22,7 @@
#include "queues.h" /* QUEUE */
#define PARTITION_BYTES_IN_POS 2
-
+#define PAR_EXT ".par"
/** Struct used for partition_name_hash */
typedef struct st_part_name_def
@@ -363,7 +363,6 @@ private:
uint m_rec_length; // Local copy of record length
bool m_ordered; // Ordered/Unordered index scan
- bool m_pkey_is_clustered; // Is primary key clustered
bool m_create_handler; // Handler used to create table
bool m_is_sub_partitioned; // Is subpartitioned
bool m_ordered_scan_ongoing;
@@ -474,6 +473,10 @@ public:
{
return m_file;
}
+ ha_partition *get_clone_source()
+ {
+ return m_is_clone_of;
+ }
virtual part_id_range *get_part_spec()
{
return &m_part_spec;
@@ -556,8 +559,10 @@ public:
int create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info) override;
int create_partitioning_metadata(const char *name,
- const char *old_name, int action_flag)
+ const char *old_name,
+ chf_create_flags action_flag)
override;
+ bool check_if_updates_are_ignored(const char *op) const override;
void update_create_info(HA_CREATE_INFO *create_info) override;
char *update_table_comment(const char *comment) override;
int change_partitions(HA_CREATE_INFO *create_info, const char *path,
@@ -685,8 +690,9 @@ public:
Bind the table/handler thread to track table i/o.
*/
virtual void unbind_psi();
- virtual void rebind_psi();
+ virtual int rebind();
#endif
+ int discover_check_version() override;
/*
-------------------------------------------------------------------------
MODULE change record
@@ -1049,8 +1055,10 @@ public:
For the given range how many records are estimated to be in this range.
Used by optimiser to calculate cost of using a particular index.
*/
- ha_rows records_in_range(uint inx, key_range * min_key, key_range * max_key)
- override;
+ ha_rows records_in_range(uint inx,
+ const key_range * min_key,
+ const key_range * max_key,
+ page_range *pages) override;
/*
Upper bound of number records returned in scan is sum of all
@@ -1335,12 +1343,6 @@ public:
uint min_record_length(uint options) const override;
/*
- Primary key is clustered can only be true if all underlying handlers have
- this feature.
- */
- bool primary_key_is_clustered() override { return m_pkey_is_clustered; }
-
- /*
-------------------------------------------------------------------------
MODULE compare records
-------------------------------------------------------------------------
@@ -1508,12 +1510,10 @@ public:
Alter_inplace_info *ha_alter_info)
override;
bool inplace_alter_table(TABLE *altered_table,
- Alter_inplace_info *ha_alter_info) override;
+ Alter_inplace_info *ha_alter_info) override;
bool commit_inplace_alter_table(TABLE *altered_table,
Alter_inplace_info *ha_alter_info,
bool commit) override;
- void notify_table_changed() override;
-
/*
-------------------------------------------------------------------------
MODULE tablespace support
@@ -1552,7 +1552,6 @@ public:
*/
const COND *cond_push(const COND *cond) override;
void cond_pop() override;
- void clear_top_table_fields() override;
int info_push(uint info_type, void *info) override;
private:
diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc
index 260f2c202fa..596f8584041 100644
--- a/sql/ha_sequence.cc
+++ b/sql/ha_sequence.cc
@@ -19,13 +19,13 @@
#include "mariadb.h"
#include "sql_list.h"
#include "table.h"
+#include "sql_table.h"
#include "sql_sequence.h"
#include "ha_sequence.h"
#include "sql_plugin.h"
#include "mysql/plugin.h"
#include "sql_priv.h"
#include "sql_parse.h"
-#include "sql_table.h"
#include "sql_update.h"
#include "sql_base.h"
#include "log_event.h"
@@ -101,15 +101,19 @@ int ha_sequence::open(const char *name, int mode, uint flags)
ha_open() sets the following for us. We have to set this for the
underlying handler
*/
- file->cached_table_flags= file->table_flags();
+ file->cached_table_flags= (file->table_flags() | HA_REUSES_FILE_NAMES);
file->reset_statistics();
internal_tmp_table= file->internal_tmp_table=
MY_TEST(flags & HA_OPEN_INTERNAL_TABLE);
reset_statistics();
- /* Don't try to read the initial row the call is part of create code */
- if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR)))
+ /*
+ Don't try to read the initial row if the call is part of CREATE, REPAIR
+ or FLUSH
+ */
+ if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR |
+ HA_OPEN_FOR_FLUSH)))
{
if (unlikely((error= table->s->sequence->read_initial_values(table))))
file->ha_close();
@@ -121,7 +125,8 @@ int ha_sequence::open(const char *name, int mode, uint flags)
The following is needed to fix comparison of rows in
ha_update_first_row() for InnoDB
*/
- memcpy(table->record[1], table->s->default_values, table->s->reclength);
+ if (!error)
+ memcpy(table->record[1], table->s->default_values, table->s->reclength);
}
DBUG_RETURN(error);
}
@@ -202,7 +207,11 @@ int ha_sequence::write_row(const uchar *buf)
DBUG_ENTER("ha_sequence::write_row");
DBUG_ASSERT(table->record[0] == buf);
- row_already_logged= 0;
+ /*
+ Log to binary log even if this function has been called before
+ (The function ends by setting row_logging to 0)
+ */
+ row_logging= row_logging_init;
if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_PREPARE))
{
/* This calls is from ha_open() as part of create table */
@@ -218,6 +227,7 @@ int ha_sequence::write_row(const uchar *buf)
sequence->copy(&tmp_seq);
if (likely(!(error= file->write_row(buf))))
sequence->initialized= SEQUENCE::SEQ_READY_TO_USE;
+ row_logging= 0;
DBUG_RETURN(error);
}
if (unlikely(sequence->initialized != SEQUENCE::SEQ_READY_TO_USE))
@@ -262,10 +272,12 @@ int ha_sequence::write_row(const uchar *buf)
sequence->copy(&tmp_seq);
rows_changed++;
/* We have to do the logging while we hold the sequence mutex */
- error= binlog_log_row(table, 0, buf, log_func);
- row_already_logged= 1;
+ if (row_logging)
+ error= binlog_log_row(table, 0, buf, log_func);
}
+ /* Row is already logged, don't log it again in ha_write_row() */
+ row_logging= 0;
sequence->all_values_used= 0;
if (!sequence_locked)
sequence->write_unlock(table);
@@ -373,6 +385,13 @@ static handler *sequence_create_handler(handlerton *hton,
MEM_ROOT *mem_root)
{
DBUG_ENTER("sequence_create_handler");
+ if (unlikely(!share))
+ {
+ /*
+ This can happen if we call get_new_handler with a non existing share
+ */
+ DBUG_RETURN(0);
+ }
DBUG_RETURN(new (mem_root) ha_sequence(hton, share));
}
@@ -413,7 +432,6 @@ static int sequence_initialize(void *p)
handlerton *local_sequence_hton= (handlerton *)p;
DBUG_ENTER("sequence_initialize");
- local_sequence_hton->state= SHOW_OPTION_YES;
local_sequence_hton->db_type= DB_TYPE_SEQUENCE;
local_sequence_hton->create= sequence_create_handler;
local_sequence_hton->panic= sequence_end;
diff --git a/sql/ha_sequence.h b/sql/ha_sequence.h
index 4360e9faa3d..72e59a40479 100644
--- a/sql/ha_sequence.h
+++ b/sql/ha_sequence.h
@@ -140,9 +140,9 @@ public:
int rename_table(const char *from, const char *to)
{ return file->rename_table(from, to); }
void unbind_psi()
- { return file->unbind_psi(); }
+ { file->unbind_psi(); }
void rebind_psi()
- { return file->rebind_psi(); }
+ { file->rebind_psi(); }
bool auto_repair(int error) const
{ return file->auto_repair(error); }
diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc
index 50fd9090e0a..b61130dd6e9 100644
--- a/sql/handle_connections_win.cc
+++ b/sql/handle_connections_win.cc
@@ -377,7 +377,7 @@ struct Pipe_Listener : public Listener
{
PTP_CALLBACK_ENVIRON m_tp_env;
Pipe_Listener():
- Listener(INVALID_HANDLE_VALUE, CreateEvent(0, FALSE, FALSE, 0)),
+ Listener(create_named_pipe(), CreateEvent(0, FALSE, FALSE, 0)),
m_tp_env(get_threadpool_win_callback_environ())
{
}
@@ -412,7 +412,7 @@ struct Pipe_Listener : public Listener
{
sql_perror("Create named pipe failed");
sql_print_error("Aborting");
- exit(1);
+ unireg_abort(1);
}
first_instance= false;
return pipe_handle;
@@ -420,17 +420,14 @@ struct Pipe_Listener : public Listener
static void create_pipe_connection(HANDLE pipe)
{
- CONNECT *connect;
- if (!(connect= new CONNECT) || !(connect->vio= vio_new_win32pipe(pipe)))
+ if (auto connect= new CONNECT(pipe))
+ create_new_thread(connect);
+ else
{
CloseHandle(pipe);
- delete connect;
statistic_increment(aborted_connects, &LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
- return;
}
- connect->host= my_localhost;
- create_new_thread(connect);
}
/* Threadpool callback.*/
@@ -443,7 +440,6 @@ struct Pipe_Listener : public Listener
void begin_accept()
{
- m_handle= create_named_pipe();
BOOL connected= ConnectNamedPipe(m_handle, &m_overlapped);
if (connected)
{
@@ -488,11 +484,12 @@ struct Pipe_Listener : public Listener
sql_print_warning("ConnectNamedPipe completed with %u", GetLastError());
#endif
CloseHandle(m_handle);
- m_handle= INVALID_HANDLE_VALUE;
+ m_handle= create_named_pipe();
begin_accept();
return;
}
HANDLE pipe= m_handle;
+ m_handle= create_named_pipe();
begin_accept();
// If threadpool is on, create connection in threadpool thread
if (!m_tp_env || !TrySubmitThreadpoolCallback(tp_create_pipe_connection, pipe, m_tp_env))
@@ -537,13 +534,12 @@ struct Pipe_Listener : public Listener
#define SHUTDOWN_IDX 0
#define LISTENER_START_IDX 1
-void handle_connections_win()
-{
- Listener* all_listeners[MAX_WAIT_HANDLES]= {};
- HANDLE wait_events[MAX_WAIT_HANDLES]= {};
- int n_listeners= 0;
- int n_waits= 0;
+static Listener *all_listeners[MAX_WAIT_HANDLES];
+static HANDLE wait_events[MAX_WAIT_HANDLES];
+static int n_listeners;
+void network_init_win()
+{
Socket_Listener::init_winsock_extensions();
/* Listen for TCP connections on "extra-port" (no threadpool).*/
@@ -572,16 +568,22 @@ void handle_connections_win()
sql_print_error("Either TCP connections or named pipe connections must be enabled.");
unireg_abort(1);
}
+}
+
+void handle_connections_win()
+{
+ DBUG_ASSERT(hEventShutdown);
+ int n_waits;
wait_events[SHUTDOWN_IDX]= hEventShutdown;
- n_waits = 1;
+ n_waits= 1;
- for (int i= 0; i < n_listeners; i++)
+ for (int i= 0; i < n_listeners; i++)
{
HANDLE wait_handle= all_listeners[i]->wait_handle();
- if(wait_handle)
+ if (wait_handle)
{
- DBUG_ASSERT((i == 0) || (all_listeners[i-1]->wait_handle() != 0));
+ DBUG_ASSERT((i == 0) || (all_listeners[i - 1]->wait_handle() != 0));
wait_events[n_waits++]= wait_handle;
}
all_listeners[i]->begin_accept();
diff --git a/sql/handle_connections_win.h b/sql/handle_connections_win.h
index a81f4346fb2..bf66c081473 100644
--- a/sql/handle_connections_win.h
+++ b/sql/handle_connections_win.h
@@ -18,3 +18,4 @@
Creates new (THD) connections..
*/
extern void handle_connections_win();
+extern void network_init_win();
diff --git a/sql/handler.cc b/sql/handler.cc
index 2553cd22a59..c33dcb192d8 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -30,7 +30,6 @@
#include "key.h" // key_copy, key_unpack, key_cmp_if_same, key_cmp
#include "sql_table.h" // build_table_filename
#include "sql_parse.h" // check_stack_overrun
-#include "sql_acl.h" // SUPER_ACL
#include "sql_base.h" // TDC_element
#include "discover.h" // extension_based_table_discovery, etc
#include "log_event.h" // *_rows_log_event
@@ -40,10 +39,13 @@
#include "myisam.h"
#include "probes_mysql.h"
#include <mysql/psi/mysql_table.h>
+#include <pfs_transaction_provider.h>
+#include <mysql/psi/mysql_transaction.h>
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_audit.h"
#include "ha_sequence.h"
#include "rowid_filter.h"
+#include "mysys_err.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -60,8 +62,42 @@
#include "wsrep_xid.h"
#include "wsrep_thd.h"
#include "wsrep_trans_observer.h" /* wsrep transaction hooks */
+#include "wsrep_var.h" /* wsrep_hton_check() */
#endif /* WITH_WSREP */
+/**
+ @def MYSQL_TABLE_LOCK_WAIT
+ Instrumentation helper for table io_waits.
+ @param OP the table operation to be performed
+ @param FLAGS per table operation flags.
+ @param PAYLOAD the code to instrument.
+ @sa MYSQL_END_TABLE_WAIT.
+*/
+#ifdef HAVE_PSI_TABLE_INTERFACE
+ #define MYSQL_TABLE_LOCK_WAIT(OP, FLAGS, PAYLOAD) \
+ { \
+ if (m_psi != NULL) \
+ { \
+ PSI_table_locker *locker; \
+ PSI_table_locker_state state; \
+ locker= PSI_TABLE_CALL(start_table_lock_wait) \
+ (& state, m_psi, OP, FLAGS, \
+ __FILE__, __LINE__); \
+ PAYLOAD \
+ if (locker != NULL) \
+ PSI_TABLE_CALL(end_table_lock_wait)(locker); \
+ } \
+ else \
+ { \
+ PAYLOAD \
+ } \
+ }
+#else
+ #define MYSQL_TABLE_LOCK_WAIT(OP, FLAGS, PAYLOAD) \
+ PAYLOAD
+#endif
+
+
/*
While we have legacy_db_type, we have this array to
check for dups and to find handlerton from legacy_db_type.
@@ -113,6 +149,44 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
uint known_extensions_id= 0;
+
+class Table_exists_error_handler : public Internal_error_handler
+{
+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 (non_existing_table_error(sql_errno))
+ {
+ 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;
+};
+
+
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
bool is_real_trans);
@@ -133,23 +207,6 @@ static plugin_ref ha_default_tmp_plugin(THD *thd)
return ha_default_plugin(thd);
}
-#if defined(WITH_ARIA_STORAGE_ENGINE) && MYSQL_VERSION_ID < 100500
-void ha_maria_implicit_commit(THD *thd, bool new_trn)
-{
- if (ha_maria::has_active_transaction(thd))
- {
- int error;
- MDL_request mdl_request;
- mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT);
- error= thd->mdl_context.acquire_lock(&mdl_request,
- thd->variables.lock_wait_timeout);
- ha_maria::implicit_commit(thd, new_trn);
- if (!error)
- thd->mdl_context.release_lock(mdl_request.ticket);
- }
-}
-#endif
-
/** @brief
Return the default storage engine handlerton for thread
@@ -199,8 +256,7 @@ plugin_ref ha_resolve_by_name(THD *thd, const LEX_CSTRING *name,
plugin_ref plugin;
redo:
- /* my_strnncoll is a macro and gcc doesn't do early expansion of macro */
- if (thd && !my_charset_latin1.coll->strnncoll(&my_charset_latin1,
+ if (thd && !my_charset_latin1.strnncoll(
(const uchar *)name->str, name->length,
(const uchar *)STRING_WITH_LEN("DEFAULT"), 0))
return tmp_table ? ha_default_tmp_plugin(thd) : ha_default_plugin(thd);
@@ -222,7 +278,7 @@ redo:
*/
for (table_alias= sys_table_aliases; table_alias->str; table_alias+= 2)
{
- if (!my_strnncoll(&my_charset_latin1,
+ if (!my_charset_latin1.strnncoll(
(const uchar *)name->str, name->length,
(const uchar *)table_alias->str, table_alias->length))
{
@@ -314,7 +370,7 @@ handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc,
DBUG_ENTER("get_new_handler");
DBUG_PRINT("enter", ("alloc: %p", alloc));
- if (db_type && db_type->state == SHOW_OPTION_YES && db_type->create)
+ if (ha_storage_engine_is_enabled(db_type))
{
if ((file= db_type->create(db_type, share, alloc)))
file->init();
@@ -379,7 +435,8 @@ int ha_init_errors(void)
/* Allocate a pointer array for the error message strings. */
/* Zerofill it to avoid uninitialized gaps. */
- if (! (handler_errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*),
+ if (! (handler_errmsgs= (const char**) my_malloc(key_memory_handler_errmsgs,
+ HA_ERR_ERRORS * sizeof(char*),
MYF(MY_WME | MY_ZEROFILL))))
return 1;
@@ -490,6 +547,26 @@ static void update_discovery_counters(handlerton *hton, int val)
engines_with_discover+= val;
}
+int ha_drop_table(THD *thd, handlerton *hton, const char *path)
+{
+ if (ha_check_if_updates_are_ignored(thd, hton, "DROP"))
+ return 0; // Simulate dropped
+ return hton->drop_table(hton, path);
+}
+
+static int hton_drop_table(handlerton *hton, const char *path)
+{
+ char tmp_path[FN_REFLEN];
+ handler *file= get_new_handler(nullptr, current_thd->mem_root, hton);
+ if (!file)
+ return ENOMEM;
+ path= get_canonical_filename(file, path, tmp_path);
+ int error= file->delete_table(path);
+ delete file;
+ return error;
+}
+
+
int ha_finalize_handlerton(st_plugin_int *plugin)
{
handlerton *hton= (handlerton *)plugin->data;
@@ -499,15 +576,8 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
if (!hton)
goto end;
- switch (hton->state) {
- case SHOW_OPTION_NO:
- case SHOW_OPTION_DISABLED:
- break;
- case SHOW_OPTION_YES:
- if (installed_htons[hton->db_type] == hton)
- installed_htons[hton->db_type]= NULL;
- break;
- };
+ if (installed_htons[hton->db_type] == hton)
+ installed_htons[hton->db_type]= NULL;
if (hton->panic)
hton->panic(hton, HA_PANIC_CLOSE);
@@ -556,7 +626,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
DBUG_ENTER("ha_initialize_handlerton");
DBUG_PRINT("plugin", ("initialize plugin: '%s'", plugin->name.str));
- hton= (handlerton *)my_malloc(sizeof(handlerton),
+ hton= (handlerton *)my_malloc(key_memory_handlerton, sizeof(handlerton),
MYF(MY_WME | MY_ZEROFILL));
if (hton == NULL)
{
@@ -567,6 +637,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
hton->tablefile_extensions= no_exts;
hton->discover_table_names= hton_ext_based_table_discovery;
+ hton->drop_table= hton_drop_table;
hton->slot= HA_SLOT_UNDEF;
/* Historical Requirement */
@@ -574,7 +645,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
if (plugin->plugin->init && plugin->plugin->init(hton))
{
sql_print_error("Plugin '%s' init function returned error.",
- plugin->name.str);
+ plugin->name.str);
goto err;
}
@@ -593,90 +664,78 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
hton->discover_table_existence= full_discover_for_existence;
}
- switch (hton->state) {
- case SHOW_OPTION_NO:
- break;
- case SHOW_OPTION_YES:
- {
- uint tmp;
- ulong fslot;
-
- DBUG_EXECUTE_IF("unstable_db_type", {
- static int i= (int) DB_TYPE_FIRST_DYNAMIC;
- hton->db_type= (enum legacy_db_type)++i;
- });
-
- /* now check the db_type for conflict */
- if (hton->db_type <= DB_TYPE_UNKNOWN ||
- hton->db_type >= DB_TYPE_DEFAULT ||
- installed_htons[hton->db_type])
- {
- int idx= (int) DB_TYPE_FIRST_DYNAMIC;
+ uint tmp;
+ ulong fslot;
- while (idx < (int) DB_TYPE_DEFAULT && installed_htons[idx])
- idx++;
+ DBUG_EXECUTE_IF("unstable_db_type", {
+ static int i= (int) DB_TYPE_FIRST_DYNAMIC;
+ hton->db_type= (enum legacy_db_type)++i;
+ });
- if (idx == (int) DB_TYPE_DEFAULT)
- {
- sql_print_warning("Too many storage engines!");
- goto err_deinit;
- }
- if (hton->db_type != DB_TYPE_UNKNOWN)
- sql_print_warning("Storage engine '%s' has conflicting typecode. "
- "Assigning value %d.", plugin->plugin->name, idx);
- hton->db_type= (enum legacy_db_type) idx;
- }
+ /* now check the db_type for conflict */
+ if (hton->db_type <= DB_TYPE_UNKNOWN ||
+ hton->db_type >= DB_TYPE_DEFAULT ||
+ installed_htons[hton->db_type])
+ {
+ int idx= (int) DB_TYPE_FIRST_DYNAMIC;
- /*
- In case a plugin is uninstalled and re-installed later, it should
- reuse an array slot. Otherwise the number of uninstall/install
- cycles would be limited. So look for a free slot.
- */
- DBUG_PRINT("plugin", ("total_ha: %lu", total_ha));
- for (fslot= 0; fslot < total_ha; fslot++)
- {
- if (!hton2plugin[fslot])
- break;
- }
- if (fslot < total_ha)
- hton->slot= fslot;
- else
- {
- if (total_ha >= MAX_HA)
- {
- sql_print_error("Too many plugins loaded. Limit is %lu. "
- "Failed on '%s'", (ulong) MAX_HA, plugin->name.str);
- goto err_deinit;
- }
- hton->slot= total_ha++;
- }
- installed_htons[hton->db_type]= hton;
- tmp= hton->savepoint_offset;
- hton->savepoint_offset= savepoint_alloc_size;
- savepoint_alloc_size+= tmp;
- hton2plugin[hton->slot]=plugin;
- if (hton->prepare)
- {
- total_ha_2pc++;
- if (tc_log && tc_log != get_tc_log_implementation())
- {
- total_ha_2pc--;
- hton->prepare= 0;
- 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",
- plugin->name.str);
- }
- }
+ while (idx < (int) DB_TYPE_DEFAULT && installed_htons[idx])
+ idx++;
+
+ if (idx == (int) DB_TYPE_DEFAULT)
+ {
+ sql_print_warning("Too many storage engines!");
+ goto err_deinit;
+ }
+ if (hton->db_type != DB_TYPE_UNKNOWN)
+ sql_print_warning("Storage engine '%s' has conflicting typecode. "
+ "Assigning value %d.", plugin->plugin->name, idx);
+ hton->db_type= (enum legacy_db_type) idx;
+ }
+
+ /*
+ In case a plugin is uninstalled and re-installed later, it should
+ reuse an array slot. Otherwise the number of uninstall/install
+ cycles would be limited. So look for a free slot.
+ */
+ DBUG_PRINT("plugin", ("total_ha: %lu", total_ha));
+ for (fslot= 0; fslot < total_ha; fslot++)
+ {
+ if (!hton2plugin[fslot])
break;
+ }
+ if (fslot < total_ha)
+ hton->slot= fslot;
+ else
+ {
+ if (total_ha >= MAX_HA)
+ {
+ sql_print_error("Too many plugins loaded. Limit is %lu. "
+ "Failed on '%s'", (ulong) MAX_HA, plugin->name.str);
+ goto err_deinit;
}
- /* fall through */
- default:
- hton->state= SHOW_OPTION_DISABLED;
- break;
+ hton->slot= total_ha++;
}
-
+ installed_htons[hton->db_type]= hton;
+ tmp= hton->savepoint_offset;
+ hton->savepoint_offset= savepoint_alloc_size;
+ savepoint_alloc_size+= tmp;
+ hton2plugin[hton->slot]=plugin;
+ if (hton->prepare)
+ {
+ total_ha_2pc++;
+ if (tc_log && tc_log != get_tc_log_implementation())
+ {
+ total_ha_2pc--;
+ hton->prepare= 0;
+ 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",
+ plugin->name.str);
+ }
+ }
+
/*
This is entirely for legacy. We will create a new "disk based" hton and a
"memory" hton which will be configurable longterm. We should be able to
@@ -711,10 +770,10 @@ err_deinit:
*/
if (plugin->plugin->deinit)
(void) plugin->plugin->deinit(NULL);
-
+
err:
#ifdef DBUG_ASSERT_EXISTS
- if (hton->prepare && hton->state == SHOW_OPTION_YES)
+ if (hton->prepare)
failed_ha_2pc++;
#endif
my_free(hton);
@@ -759,7 +818,7 @@ static my_bool dropdb_handlerton(THD *unused1, plugin_ref plugin,
void *path)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->drop_database)
+ if (hton->drop_database)
hton->drop_database(hton, (char *)path);
return FALSE;
}
@@ -775,7 +834,7 @@ static my_bool checkpoint_state_handlerton(THD *unused1, plugin_ref plugin,
void *disable)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->checkpoint_state)
+ if (hton->checkpoint_state)
hton->checkpoint_state(hton, (int) *(bool*) disable);
return FALSE;
}
@@ -797,7 +856,7 @@ static my_bool commit_checkpoint_request_handlerton(THD *unused1, plugin_ref plu
{
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)
+ if (hton->commit_checkpoint_request)
{
void *cookie= st->cookie;
if (st->pre_hook)
@@ -824,34 +883,29 @@ ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *))
}
-
-static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
- void *unused)
-{
- handlerton *hton= plugin_hton(plugin);
- /*
- there's no need to rollback here as all transactions must
- be rolled back already
- */
- if (hton->state == SHOW_OPTION_YES && thd_get_ha_data(thd, hton))
- {
- if (hton->close_connection)
- hton->close_connection(hton, thd);
- /* make sure ha_data is reset and ha_data_lock is released */
- thd_set_ha_data(thd, hton, NULL);
- }
- return FALSE;
-}
-
/**
@note
don't bother to rollback here, it's done already
+
+ there's no need to rollback here as all transactions must
+ be rolled back already
*/
void ha_close_connection(THD* thd)
{
- plugin_foreach_with_mask(thd, closecon_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN,
- PLUGIN_IS_DELETED|PLUGIN_IS_READY, 0);
+ for (auto i= 0; i < MAX_HA; i++)
+ {
+ if (thd->ha_data[i].lock)
+ {
+ handlerton *hton= plugin_hton(thd->ha_data[i].lock);
+ if (hton->close_connection)
+ hton->close_connection(hton, thd);
+ /* make sure SE didn't reset ha_data in close_connection() */
+ DBUG_ASSERT(thd->ha_data[i].lock);
+ /* make sure ha_data is reset and ha_data_lock is released */
+ thd_set_ha_data(thd, hton, 0);
+ }
+ DBUG_ASSERT(!thd->ha_data[i].ha_ptr);
+ }
}
static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
@@ -859,8 +913,7 @@ static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->kill_query &&
- thd_get_ha_data(thd, hton))
+ if (hton->kill_query && thd_get_ha_data(thd, hton))
hton->kill_query(hton, thd, *(enum thd_kill_levels *) level);
return FALSE;
}
@@ -881,7 +934,7 @@ static my_bool plugin_prepare_for_backup(THD *unused1, plugin_ref plugin,
void *not_used)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->prepare_for_backup)
+ if (hton->prepare_for_backup)
hton->prepare_for_backup();
return FALSE;
}
@@ -897,7 +950,7 @@ static my_bool plugin_end_backup(THD *unused1, plugin_ref plugin,
void *not_used)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->end_backup)
+ if (hton->end_backup)
hton->end_backup();
return FALSE;
}
@@ -910,6 +963,33 @@ void ha_end_backup()
}
+/*
+ Inform plugin of the server shutdown.
+ Called after all connections are down.
+
+ Under some circumstances, storage engine might need to
+ so some work, before deinit() can be safely called.
+ (an example is Innodb purge that might call into server
+ to calculate virtual columns, which might potentially also
+ invoke other plugins, such as audit
+*/
+static my_bool plugin_pre_shutdown(THD *, plugin_ref plugin, void *)
+{
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->pre_shutdown)
+ hton->pre_shutdown();
+ return FALSE;
+}
+
+
+void ha_pre_shutdown()
+{
+ plugin_foreach_with_mask(0, plugin_pre_shutdown,
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ PLUGIN_IS_DELETED | PLUGIN_IS_READY, 0);
+}
+
+
/* ========================================================================
======================= TRANSACTIONS ===================================*/
@@ -1216,7 +1296,7 @@ void ha_end_backup()
times per transaction.
*/
-void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
+void trans_register_ha(THD *thd, bool all, handlerton *ht_arg, ulonglong trxid)
{
THD_TRANS *trans;
Ha_trx_info *ha_info;
@@ -1225,14 +1305,14 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
if (all)
{
- trans= &thd->transaction.all;
+ 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;
+ trans= &thd->transaction->stmt;
ha_info= thd->ha_data[ht_arg->slot].ha_info + (all ? 1 : 0);
@@ -1244,9 +1324,28 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
trans->no_2pc|=(ht_arg->prepare==0);
/* Set implicit xid even if there's explicit XA, it will be ignored anyway. */
- if (thd->transaction.implicit_xid.is_null())
- thd->transaction.implicit_xid.set(thd->query_id);
+ if (thd->transaction->implicit_xid.is_null())
+ thd->transaction->implicit_xid.set(thd->query_id);
+
+/*
+ Register transaction start in performance schema if not done already.
+ By doing this, we handle cases when the transaction is started implicitly in
+ autocommit=0 mode, and cases when we are in normal autocommit=1 mode and the
+ executed statement is a single-statement transaction.
+
+ Explicitly started transactions are handled in trans_begin().
+ Do not register transactions in which binary log is the only participating
+ transactional storage engine.
+*/
+ if (thd->m_transaction_psi == NULL && ht_arg->db_type != DB_TYPE_BINLOG)
+ {
+ thd->m_transaction_psi= MYSQL_START_TRANSACTION(&thd->m_transaction_state,
+ thd->get_xid(), trxid, thd->tx_isolation, thd->tx_read_only,
+ !thd->in_multi_stmt_transaction_mode());
+ DEBUG_SYNC(thd, "after_set_transaction_psi_before_set_transaction_gtid");
+ //gtid_set_performance_schema_values(thd);
+ }
DBUG_VOID_RETURN;
}
@@ -1289,7 +1388,7 @@ static int prepare_or_error(handlerton *ht, THD *thd, bool all)
int ha_prepare(THD *thd)
{
int error=0, all=1;
- THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
+ THD_TRANS *trans=all ? &thd->transaction->all : &thd->transaction->stmt;
Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_prepare");
@@ -1316,11 +1415,42 @@ int ha_prepare(THD *thd)
}
}
+
+ DEBUG_SYNC(thd, "at_unlog_xa_prepare");
+
+ if (tc_log->unlog_xa_prepare(thd, all))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
}
+/*
+ Like ha_check_and_coalesce_trx_read_only to return counted number of
+ read-write transaction participants limited to two, but works in the 'all'
+ context.
+ Also returns the last found rw ha_info through the 2nd argument.
+*/
+uint ha_count_rw_all(THD *thd, Ha_trx_info **ptr_ha_info)
+{
+ unsigned rw_ha_count= 0;
+
+ for (auto ha_info= thd->transaction->all.ha_list; ha_info;
+ ha_info= ha_info->next())
+ {
+ if (ha_info->is_trx_read_write())
+ {
+ *ptr_ha_info= ha_info;
+ if (++rw_ha_count > 1)
+ break;
+ }
+ }
+ return rw_ha_count;
+}
+
/**
Check if we can skip the two-phase commit.
@@ -1403,7 +1533,7 @@ int ha_commit_trans(THD *thd, bool all)
'all' means that this is either an explicit commit issued by
user, or an implicit commit issued by a DDL.
*/
- THD_TRANS *trans= all ? &thd->transaction.all : &thd->transaction.stmt;
+ THD_TRANS *trans= all ? &thd->transaction->all : &thd->transaction->stmt;
/*
"real" is a nick name for a transaction for which a commit will
make persistent changes. E.g. a 'stmt' transaction inside an 'all'
@@ -1411,7 +1541,7 @@ 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;
@@ -1438,8 +1568,8 @@ int ha_commit_trans(THD *thd, bool all)
flags will not get propagated to its normal transaction's
counterpart.
*/
- DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
- trans == &thd->transaction.stmt);
+ DBUG_ASSERT(thd->transaction->stmt.ha_list == NULL ||
+ trans == &thd->transaction->stmt);
if (thd->in_sub_stmt)
{
@@ -1468,16 +1598,17 @@ int ha_commit_trans(THD *thd, bool all)
Free resources and perform other cleanup even for 'empty' transactions.
*/
if (is_real_trans)
- thd->transaction.cleanup();
+ {
+ thd->transaction->cleanup();
+ MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi);
+ thd->m_transaction_psi= NULL;
+ }
#ifdef WITH_WSREP
if (wsrep_is_active(thd) && is_real_trans && !error)
- {
wsrep_commit_empty(thd, all);
- }
#endif /* WITH_WSREP */
- ha_maria_implicit_commit(thd, TRUE);
- DBUG_RETURN(error);
+ DBUG_RETURN(0);
}
DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE(););
@@ -1504,29 +1635,26 @@ int ha_commit_trans(THD *thd, bool all)
We allow the owner of FTWRL to COMMIT; we assume that it knows
what it does.
*/
- mdl_backup.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT);
+ MDL_REQUEST_INIT(&mdl_backup, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_EXPLICIT);
if (!WSREP(thd))
{
if (thd->mdl_context.acquire_lock(&mdl_backup,
thd->variables.lock_wait_timeout))
{
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), 1);
ha_rollback_trans(thd, all);
DBUG_RETURN(1);
}
thd->backup_commit_lock= &mdl_backup;
}
DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
-
- /* Use shortcut as we already have the MDL_BACKUP_COMMIT lock */
- ha_maria::implicit_commit(thd, TRUE);
}
- else
- ha_maria_implicit_commit(thd, TRUE);
if (rw_trans &&
opt_readonly &&
- !(thd->security_ctx->master_access & SUPER_ACL) &&
+ !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) &&
!thd->slave_thread)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
@@ -1563,7 +1691,7 @@ int ha_commit_trans(THD *thd, bool all)
// Here, the call will not commit inside InnoDB. It is only working
// around closing thd->transaction.stmt open by TR_table::open().
if (all)
- commit_one_phase_2(thd, false, &thd->transaction.stmt, false);
+ commit_one_phase_2(thd, false, &thd->transaction->stmt, false);
}
}
#endif
@@ -1585,18 +1713,15 @@ int ha_commit_trans(THD *thd, bool all)
#endif /* WITH_WSREP */
error= ha_commit_one_phase(thd, all);
#ifdef WITH_WSREP
- if (run_wsrep_hooks)
- error= error || wsrep_after_commit(thd, all);
+ // Here in case of error we must return 2 for inconsistency
+ if (run_wsrep_hooks && !error)
+ error= wsrep_after_commit(thd, all) ? 2 : 0;
#endif /* WITH_WSREP */
goto done;
}
need_prepare_ordered= FALSE;
need_commit_ordered= FALSE;
- DBUG_ASSERT(thd->transaction.implicit_xid.get_my_xid() ==
- thd->transaction.implicit_xid.quick_get_my_xid());
- xid= thd->transaction.xid_state.is_explicit_XA() ? 0 :
- thd->transaction.implicit_xid.quick_get_my_xid();
for (Ha_trx_info *hi= ha_info; hi; hi= hi->next())
{
@@ -1621,6 +1746,18 @@ int ha_commit_trans(THD *thd, bool all)
DEBUG_SYNC(thd, "ha_commit_trans_after_prepare");
DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE(););
+ if (!is_real_trans)
+ {
+ error= commit_one_phase_2(thd, all, trans, is_real_trans);
+ goto done;
+ }
+
+ DBUG_ASSERT(thd->transaction->implicit_xid.get_my_xid() ==
+ thd->transaction->implicit_xid.quick_get_my_xid());
+ DBUG_ASSERT(!thd->transaction->xid_state.is_explicit_XA() ||
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->transaction->implicit_xid.quick_get_my_xid();
+
#ifdef WITH_WSREP
if (run_wsrep_hooks && !error)
{
@@ -1631,14 +1768,6 @@ int ha_commit_trans(THD *thd, bool all)
xid= s.get();
}
}
-#endif /* WITH_WSREP */
-
- if (!is_real_trans)
- {
- error= commit_one_phase_2(thd, all, trans, is_real_trans);
- goto done;
- }
-#ifdef WITH_WSREP
if (run_wsrep_hooks && (error = wsrep_before_commit(thd, all)))
goto wsrep_err;
#endif /* WITH_WSREP */
@@ -1655,8 +1784,10 @@ int ha_commit_trans(THD *thd, bool all)
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
#ifdef WITH_WSREP
- if (run_wsrep_hooks && (error || (error = wsrep_after_commit(thd, all))))
+ if (run_wsrep_hooks &&
+ (error || (error = wsrep_after_commit(thd, all))))
{
+ error = 2;
mysql_mutex_lock(&thd->LOCK_thd_data);
if (wsrep_must_abort(thd))
{
@@ -1669,12 +1800,15 @@ int ha_commit_trans(THD *thd, bool all)
#endif /* WITH_WSREP */
DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE(););
if (tc_log->unlog(cookie, xid))
- {
error= 2; /* Error during commit */
- goto end;
- }
done:
+ if (is_real_trans)
+ {
+ MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi);
+ thd->m_transaction_psi= NULL;
+ }
+
DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
mysql_mutex_assert_not_owner(&LOCK_prepare_ordered);
@@ -1712,6 +1846,12 @@ err:
ha_rollback_trans(thd, all);
else
{
+ /*
+ We are not really doing a rollback here, but the code in trans_commit()
+ requres that m_transaction_psi is 0 when we return from this function.
+ */
+ MYSQL_ROLLBACK_TRANSACTION(thd->m_transaction_psi);
+ thd->m_transaction_psi= NULL;
WSREP_DEBUG("rollback skipped %p %d",thd->rgi_slave,
thd->rgi_slave->is_parallel_exec);
}
@@ -1753,7 +1893,7 @@ end:
int ha_commit_one_phase(THD *thd, bool all)
{
- THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
+ THD_TRANS *trans=all ? &thd->transaction->all : &thd->transaction->stmt;
/*
"real" is a nick name for a transaction for which a commit will
make persistent changes. E.g. a 'stmt' transaction inside a 'all'
@@ -1767,7 +1907,7 @@ 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");
@@ -1815,8 +1955,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
if (all)
{
#ifdef HAVE_QUERY_CACHE
- if (thd->transaction.changed_tables)
- query_cache.invalidate(thd, thd->transaction.changed_tables);
+ if (thd->transaction->changed_tables)
+ query_cache.invalidate(thd, thd->transaction->changed_tables);
#endif
}
}
@@ -1825,7 +1965,7 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
if (is_real_trans)
{
thd->has_waiter= false;
- thd->transaction.cleanup();
+ thd->transaction->cleanup();
if (count >= 2)
statistic_increment(transactions_multi_engine, LOCK_status);
}
@@ -1837,7 +1977,7 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
int ha_rollback_trans(THD *thd, bool all)
{
int error=0;
- THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
+ THD_TRANS *trans=all ? &thd->transaction->all : &thd->transaction->stmt;
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
/*
"real" is a nick name for a transaction for which a commit will
@@ -1852,15 +1992,15 @@ int ha_rollback_trans(THD *thd, bool all)
ha_commit_one_phase() is 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;
DBUG_ENTER("ha_rollback_trans");
/*
We must not rollback the normal transaction if a statement
transaction is pending.
*/
- DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
- trans == &thd->transaction.stmt);
+ DBUG_ASSERT(thd->transaction->stmt.ha_list == NULL ||
+ trans == &thd->transaction->stmt);
#ifdef HAVE_REPLICATION
if (is_real_trans)
@@ -1876,7 +2016,8 @@ int ha_rollback_trans(THD *thd, bool all)
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));
+ DBUG_ASSERT(!(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit) ||
+ thd->transaction->xid_state.is_explicit_XA());
if (thd->rgi_slave && thd->rgi_slave->did_mark_start_commit)
thd->rgi_slave->unmark_start_commit();
}
@@ -1910,7 +2051,8 @@ int ha_rollback_trans(THD *thd, bool all)
int err;
handlerton *ht= ha_info->ht();
if ((err= ht->rollback(ht, thd, all)))
- { // cannot happen
+ {
+ // cannot happen
my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err);
error=1;
#ifdef WITH_WSREP
@@ -1936,6 +2078,13 @@ int ha_rollback_trans(THD *thd, bool all)
}
(void) wsrep_after_rollback(thd, all);
#endif /* WITH_WSREP */
+
+ if (all || !thd->in_active_multi_stmt_transaction())
+ {
+ MYSQL_ROLLBACK_TRANSACTION(thd->m_transaction_psi);
+ thd->m_transaction_psi= NULL;
+ }
+
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
{
@@ -1944,10 +2093,10 @@ int ha_rollback_trans(THD *thd, bool all)
transaction hasn't been started in any transactional storage engine.
*/
if (thd->transaction_rollback_request)
- thd->transaction.xid_state.set_error(thd->get_stmt_da()->sql_errno());
+ thd->transaction->xid_state.set_error(thd->get_stmt_da()->sql_errno());
thd->has_waiter= false;
- thd->transaction.cleanup();
+ thd->transaction->cleanup();
}
if (all)
thd->transaction_rollback_request= FALSE;
@@ -1965,7 +2114,7 @@ int ha_rollback_trans(THD *thd, bool all)
it doesn't matter if a warning is pushed to a system thread or not:
No one will see it...
*/
- if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
+ if (is_real_trans && thd->transaction->all.modified_non_trans_table &&
!thd->slave_thread && thd->killed < KILL_CONNECTION)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
@@ -1986,7 +2135,7 @@ static my_bool xacommit_handlerton(THD *unused1, plugin_ref plugin,
void *arg)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->recover)
+ if (hton->recover)
{
hton->commit_by_xid(hton, ((struct xahton_st *)arg)->xid);
((struct xahton_st *)arg)->result= 0;
@@ -1998,7 +2147,7 @@ static my_bool xarollback_handlerton(THD *unused1, plugin_ref plugin,
void *arg)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->recover)
+ if (hton->recover)
{
hton->rollback_by_xid(hton, ((struct xahton_st *)arg)->xid);
((struct xahton_st *)arg)->result= 0;
@@ -2029,7 +2178,7 @@ int ha_commit_or_rollback_by_xid(XID *xid, bool commit)
@return pointer to converted string
@note This does not need to be multi-byte safe or anything */
-char *xid_to_str(char *buf, const XID &xid)
+static char *xid_to_str(char *buf, const XID &xid)
{
int i;
char *s=buf;
@@ -2084,7 +2233,7 @@ static my_xid wsrep_order_and_check_continuity(XID *list, int len)
{
#ifdef WITH_WSREP
wsrep_sort_xid_array(list, len);
- wsrep::gtid cur_position= wsrep_get_SE_checkpoint();
+ wsrep::gtid cur_position= wsrep_get_SE_checkpoint<wsrep::gtid>();
long long cur_seqno= cur_position.seqno().get();
for (int i= 0; i < len; ++i)
{
@@ -2135,7 +2284,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
struct xarecover_st *info= (struct xarecover_st *) arg;
int got;
- if (hton->state == SHOW_OPTION_YES && hton->recover)
+ if (hton->recover)
{
while ((got= hton->recover(hton, info->list, info->len)) > 0 )
{
@@ -2250,7 +2399,7 @@ int ha_recover(HASH *commit_list)
info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2)
{
DBUG_EXECUTE_IF("min_xa_len", info.len = 16;);
- info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0));
+ info.list=(XID *)my_malloc(key_memory_XID, info.len*sizeof(XID), MYF(0));
}
if (!info.list)
{
@@ -2307,8 +2456,8 @@ commit_checkpoint_notify_ha(handlerton *hton, void *cookie)
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);
+ THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction->stmt :
+ &thd->transaction->all);
DBUG_ENTER("ha_rollback_to_savepoint_can_release_mdl");
@@ -2332,8 +2481,8 @@ bool ha_rollback_to_savepoint_can_release_mdl(THD *thd)
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
{
int error=0;
- THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
- &thd->transaction.all);
+ THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction->stmt :
+ &thd->transaction->all);
Ha_trx_info *ha_info, *ha_info_next;
DBUG_ENTER("ha_rollback_to_savepoint");
@@ -2392,6 +2541,10 @@ int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
ha_info->reset(); /* keep it conveniently zero-filled */
}
trans->ha_list= sv->ha_list;
+
+ if (thd->m_transaction_psi != NULL)
+ MYSQL_INC_TRANSACTION_ROLLBACK_TO_SAVEPOINT(thd->m_transaction_psi, 1);
+
DBUG_RETURN(error);
}
@@ -2414,8 +2567,8 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
}
#endif /* WITH_WSREP */
int error=0;
- THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
- &thd->transaction.all);
+ THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction->stmt :
+ &thd->transaction->all);
Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_savepoint");
@@ -2443,6 +2596,9 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
*/
sv->ha_list= trans->ha_list;
+ if (!error && thd->m_transaction_psi != NULL)
+ MYSQL_INC_TRANSACTION_SAVEPOINTS(thd->m_transaction_psi, 1);
+
DBUG_RETURN(error);
}
@@ -2467,6 +2623,10 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
error=1;
}
}
+
+ if (thd->m_transaction_psi != NULL)
+ MYSQL_INC_TRANSACTION_RELEASE_SAVEPOINT(thd->m_transaction_psi, 1);
+
DBUG_RETURN(error);
}
@@ -2475,8 +2635,7 @@ static my_bool snapshot_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES &&
- hton->start_consistent_snapshot)
+ if (hton->start_consistent_snapshot)
{
if (hton->start_consistent_snapshot(hton, thd))
return TRUE;
@@ -2522,28 +2681,14 @@ static my_bool flush_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
- hton->flush_logs(hton))
- return TRUE;
- return FALSE;
+ return hton->flush_logs && hton->flush_logs(hton);
}
-bool ha_flush_logs(handlerton *db_type)
+bool ha_flush_logs()
{
- if (db_type == NULL)
- {
- if (plugin_foreach(NULL, flush_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, 0))
- return TRUE;
- }
- else
- {
- if (db_type->state != SHOW_OPTION_YES ||
- (db_type->flush_logs && db_type->flush_logs(db_type)))
- return TRUE;
- }
- return FALSE;
+ return plugin_foreach(NULL, flush_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, 0);
}
@@ -2595,57 +2740,72 @@ const char *get_canonical_filename(handler *file, const char *path,
}
-/** delete a table in the engine
+/**
+ Delete a table in the engine
+
+ @return 0 Table was deleted
+ @return -1 Table didn't exists, no error given
+ @return # Error from table handler
@note
ENOENT and HA_ERR_NO_SUCH_TABLE are not considered errors.
- The .frm file will be deleted only if we return 0.
+ The .frm file should be deleted by the caller only if we return <= 0.
*/
-int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
- const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning)
+
+int ha_delete_table(THD *thd, handlerton *hton, const char *path,
+ const LEX_CSTRING *db, const LEX_CSTRING *alias,
+ bool generate_warning)
{
- handler *file;
- char tmp_path[FN_REFLEN];
int error;
- TABLE dummy_table;
- TABLE_SHARE dummy_share;
+ bool is_error= thd->is_error();
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)))
+ /* hton is NULL in ALTER TABLE when renaming only .frm files */
+ if (hton == NULL || hton == view_pseudo_hton)
DBUG_RETURN(0);
- bzero((char*) &dummy_table, sizeof(dummy_table));
- bzero((char*) &dummy_share, sizeof(dummy_share));
- dummy_table.s= &dummy_share;
+ if (ha_check_if_updates_are_ignored(thd, hton, "DROP"))
+ DBUG_RETURN(0);
- path= get_canonical_filename(file, path, tmp_path);
- if (unlikely((error= file->ha_delete_table(path))))
+ error= hton->drop_table(hton, path);
+ if (error > 0)
{
/*
- it's not an error if the table doesn't exist in the engine.
+ It's not an error if the table doesn't exist in the engine.
warn the user, but still report DROP being a success
*/
- bool intercept= error == ENOENT || error == HA_ERR_NO_SUCH_TABLE;
+ bool intercept= non_existing_table_error(error);
- if (!intercept || generate_warning)
+ if ((!intercept || generate_warning) && ! thd->is_error())
{
- /* 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= *db;
- dummy_share.table_name= *alias;
- dummy_table.alias.set(alias->str, alias->length, table_alias_charset);
- file->change_table_ptr(&dummy_table, &dummy_share);
- file->print_error(error, MYF(intercept ? ME_WARNING : 0));
+ TABLE dummy_table;
+ TABLE_SHARE dummy_share;
+ handler *file= get_new_handler(nullptr, thd->mem_root, hton);
+ if (file) {
+ bzero((char*) &dummy_table, sizeof(dummy_table));
+ bzero((char*) &dummy_share, sizeof(dummy_share));
+ dummy_share.path.str= (char*) path;
+ dummy_share.path.length= strlen(path);
+ dummy_share.normalized_path= dummy_share.path;
+ dummy_share.db= *db;
+ dummy_share.table_name= *alias;
+ dummy_table.s= &dummy_share;
+ dummy_table.alias.set(alias->str, alias->length, table_alias_charset);
+ file->change_table_ptr(&dummy_table, &dummy_share);
+ file->print_error(error, MYF(intercept ? ME_WARNING : 0));
+ delete file;
+ }
}
if (intercept)
- error= 0;
+ {
+ /* Clear error if we got it in this function */
+ if (!is_error)
+ thd->clear_error();
+ error= -1;
+ }
}
- delete file;
-
+ if (error)
+ DBUG_PRINT("exit", ("error: %d", error));
DBUG_RETURN(error);
}
@@ -2691,6 +2851,24 @@ err:
return NULL;
}
+
+/**
+ clone of current handler.
+
+ Creates a clone of handler used for unique hash key and WITHOUT OVERLAPS.
+ @return error code
+*/
+int handler::create_lookup_handler()
+{
+ handler *tmp;
+ if (lookup_handler != this)
+ return 0;
+ if (!(tmp= clone(table->s->normalized_path.str, table->in_use->mem_root)))
+ return 1;
+ lookup_handler= tmp;
+ return lookup_handler->ha_external_lock(table->in_use, F_RDLCK);
+}
+
LEX_CSTRING *handler::engine_name()
{
return hton_name(ht);
@@ -2709,22 +2887,18 @@ double handler::keyread_time(uint index, uint ranges, ha_rows rows)
{
DBUG_ASSERT(ranges == 0 || ranges == 1);
size_t len= table->key_info[index].key_length + ref_length;
- if (index == table->s->primary_key && table->file->primary_key_is_clustered())
+ if (table->file->is_clustering_key(index))
len= table->s->stored_rec_length;
double cost= (double)rows*len/(stats.block_size+1)*IDX_BLOCK_COPY_COST;
if (ranges)
{
- uint keys_per_block= (uint) (stats.block_size/2.0/len+1);
- ulonglong blocks= !rows ? 0 : (rows-1) / keys_per_block + 1;
+ uint keys_per_block= (uint) (stats.block_size*3/4/len+1);
+ ulonglong blocks= (rows+ keys_per_block- 1)/keys_per_block;
cost+= blocks;
}
return cost;
}
-void **handler::ha_data(THD *thd) const
-{
- return thd_ha_data(thd, ht);
-}
THD *handler::ha_thd(void) const
{
@@ -2751,6 +2925,30 @@ void handler::rebind_psi()
}
+void handler::start_psi_batch_mode()
+{
+#ifdef HAVE_PSI_TABLE_INTERFACE
+ DBUG_ASSERT(m_psi_batch_mode == PSI_BATCH_MODE_NONE);
+ DBUG_ASSERT(m_psi_locker == NULL);
+ m_psi_batch_mode= PSI_BATCH_MODE_STARTING;
+ m_psi_numrows= 0;
+#endif
+}
+
+void handler::end_psi_batch_mode()
+{
+#ifdef HAVE_PSI_TABLE_INTERFACE
+ DBUG_ASSERT(m_psi_batch_mode != PSI_BATCH_MODE_NONE);
+ if (m_psi_locker != NULL)
+ {
+ DBUG_ASSERT(m_psi_batch_mode == PSI_BATCH_MODE_STARTED);
+ PSI_TABLE_CALL(end_table_io_wait)(m_psi_locker, m_psi_numrows);
+ m_psi_locker= NULL;
+ }
+ m_psi_batch_mode= PSI_BATCH_MODE_NONE;
+#endif
+}
+
PSI_table_share *handler::ha_table_share_psi() const
{
return table_share->m_psi;
@@ -2827,7 +3025,6 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
}
reset_statistics();
internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE);
-
DBUG_RETURN(error);
}
@@ -2840,11 +3037,15 @@ int handler::ha_close(void)
*/
if (table->in_use)
status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read);
- PSI_CALL_close_table(m_psi);
+ PSI_CALL_close_table(table_share, m_psi);
m_psi= NULL; /* instrumentation handle, invalid after close_table() */
+ DBUG_ASSERT(m_psi_batch_mode == PSI_BATCH_MODE_NONE);
+ DBUG_ASSERT(m_psi_locker == NULL);
/* Detach from ANALYZE tracker */
tracker= NULL;
+ /* We use ref as way to check that open succeded */
+ ref= 0;
DBUG_ASSERT(m_lock_type == F_UNLCK);
DBUG_ASSERT(inited == NONE);
@@ -2862,7 +3063,7 @@ int handler::ha_rnd_next(uchar *buf)
do
{
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, MAX_KEY, result,
{ result= rnd_next(buf); })
if (result != HA_ERR_RECORD_DELETED)
break;
@@ -2894,7 +3095,7 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited == RND);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, MAX_KEY, result,
{ result= rnd_pos(buf, pos); })
increment_statistics(&SSV::ha_read_rnd_count);
if (result == HA_ERR_RECORD_DELETED)
@@ -2919,7 +3120,7 @@ int handler::ha_index_read_map(uchar *buf, const uchar *key,
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, active_index, result,
{ result= index_read_map(buf, key, keypart_map, find_flag); })
increment_statistics(&SSV::ha_read_key_count);
if (!result)
@@ -2947,7 +3148,7 @@ int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key,
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
m_lock_type != F_UNLCK);
DBUG_ASSERT(end_range == NULL);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, index, result,
{ result= index_read_idx_map(buf, index, key, keypart_map, find_flag); })
increment_statistics(&SSV::ha_read_key_count);
if (!result)
@@ -2969,7 +3170,7 @@ int handler::ha_index_next(uchar * buf)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, active_index, result,
{ result= index_next(buf); })
increment_statistics(&SSV::ha_read_next_count);
if (!result)
@@ -2979,6 +3180,9 @@ int handler::ha_index_next(uchar * buf)
table->update_virtual_fields(this, VCOL_UPDATE_FOR_READ);
}
table->status=result ? STATUS_NOT_FOUND: 0;
+
+ DEBUG_SYNC(ha_thd(), "handler_ha_index_next_end");
+
DBUG_RETURN(result);
}
@@ -2990,7 +3194,7 @@ int handler::ha_index_prev(uchar * buf)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, active_index, result,
{ result= index_prev(buf); })
increment_statistics(&SSV::ha_read_prev_count);
if (!result)
@@ -3010,7 +3214,7 @@ int handler::ha_index_first(uchar * buf)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, active_index, result,
{ result= index_first(buf); })
increment_statistics(&SSV::ha_read_first_count);
if (!result)
@@ -3030,7 +3234,7 @@ int handler::ha_index_last(uchar * buf)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, active_index, result,
{ result= index_last(buf); })
increment_statistics(&SSV::ha_read_last_count);
if (!result)
@@ -3050,7 +3254,7 @@ int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
m_lock_type != F_UNLCK);
DBUG_ASSERT(inited==INDEX);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, active_index, result,
{ result= index_next_same(buf, key, keylen); })
increment_statistics(&SSV::ha_read_next_count);
if (!result)
@@ -3493,8 +3697,9 @@ int handler::update_auto_increment()
variables->auto_increment_increment);
auto_inc_intervals_count++;
/* Row-based replication does not need to store intervals in binlog */
- if (((WSREP(thd) && wsrep_emulate_bin_log ) || mysql_bin_log.is_open())
- && !thd->is_current_stmt_binlog_format_row())
+ if (((WSREP_NNULL(thd) && wsrep_emulate_bin_log) ||
+ mysql_bin_log.is_open()) &&
+ !thd->is_current_stmt_binlog_format_row())
thd->auto_inc_intervals_in_cur_stmt_for_binlog.
append(auto_inc_interval_for_cur_row.minimum(),
auto_inc_interval_for_cur_row.values(),
@@ -4096,7 +4301,7 @@ int handler::check_collation_compatibility()
cs_number == 23 || /* cp1251_ukrainian_ci - bug #29461 */
cs_number == 26)) || /* cp1250_general_ci - bug #29461 */
(mysql_version < 50124 &&
- (cs_number == 33 || /* utf8_general_ci - bug #27877 */
+ (cs_number == 33 || /* utf8mb3_general_ci - bug #27877 */
cs_number == 35))) /* ucs2_general_ci - bug #27877 */
return HA_ADMIN_NEEDS_UPGRADE;
}
@@ -4219,15 +4424,17 @@ 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");
- if (table->s->long_unique_table && table->file->errkey < table->s->keys)
- DBUG_RETURN(table->file->errkey);
- table->file->errkey = (uint) -1;
+
+ if (lookup_errkey != (uint)-1)
+ DBUG_RETURN(errkey= lookup_errkey);
+
+ errkey= (uint)-1;
if (error == HA_ERR_FOUND_DUPP_KEY ||
error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL ||
error == HA_ERR_DROP_INDEX_FK)
- table->file->info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
- DBUG_RETURN(table->file->errkey);
+ info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
+ DBUG_RETURN(errkey);
}
@@ -4238,45 +4445,51 @@ uint handler::get_dup_key(int error)
@note
We assume that the handler may return more extensions than
- was actually used for the file.
+ was actually used for the file. We also assume that the first
+ extension is the most important one (see the comment near
+ handlerton::tablefile_extensions). If this exist and we can't delete
+ that it, we will abort the delete.
+ If the first one doesn't exists, we have to try to delete all other
+ extension as there is chance that the server had crashed between
+ the delete of the first file and the next
@retval
0 If we successfully deleted at least one file from base_ext and
- didn't get any other errors than ENOENT
+ didn't get any other errors than ENOENT
+
@retval
!0 Error
*/
+
int handler::delete_table(const char *name)
{
- int saved_error= 0;
- int error= 0;
- int enoent_or_zero;
-
- 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
+ int saved_error= ENOENT;
+ bool abort_if_first_file_error= 1;
+ bool some_file_deleted= 0;
+ DBUG_ENTER("handler::delete_table");
- for (const char **ext=bas_ext(); *ext ; ext++)
+ for (const char **ext= bas_ext(); *ext ; ext++)
{
- if (mysql_file_delete_with_symlink(key_file_misc, name, *ext, 0))
+ int err= mysql_file_delete_with_symlink(key_file_misc, name, *ext, MYF(0));
+ if (err)
{
if (my_errno != ENOENT)
{
+ saved_error= my_errno;
/*
- If error on the first existing file, return the error.
+ If error other than file not found on the first existing file,
+ return the error.
Otherwise delete as much as possible.
*/
- if (enoent_or_zero)
- return my_errno;
- saved_error= my_errno;
+ if (abort_if_first_file_error)
+ DBUG_RETURN(saved_error);
}
}
else
- enoent_or_zero= 0; // No error for ENOENT
- error= enoent_or_zero;
+ some_file_deleted= 1;
+ abort_if_first_file_error= 0;
}
- return saved_error ? saved_error : error;
+ DBUG_RETURN(some_file_deleted && saved_error == ENOENT ? 0 : saved_error);
}
@@ -4312,6 +4525,23 @@ void handler::drop_table(const char *name)
/**
+ Return true if the error from drop table means that the
+ table didn't exists
+*/
+
+bool non_existing_table_error(int error)
+{
+ return (error == ENOENT ||
+ (error == EE_DELETE && my_errno == ENOENT) ||
+ error == HA_ERR_NO_SUCH_TABLE ||
+ error == HA_ERR_UNSUPPORTED ||
+ error == ER_NO_SUCH_TABLE ||
+ error == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ error == ER_WRONG_OBJECT);
+}
+
+
+/**
Performs checks upon the table.
@param thd thread doing CHECK TABLE operation
@@ -4326,6 +4556,7 @@ void handler::drop_table(const char *name)
@retval
HA_ADMIN_NOT_IMPLEMENTED
*/
+
int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
{
int error;
@@ -4372,10 +4603,9 @@ void handler::mark_trx_read_write_internal()
*/
if (ha_info->is_started())
{
- DBUG_ASSERT(has_transaction_manager());
/*
- table_share can be NULL in ha_delete_table(). See implementation
- of standalone function ha_delete_table() in sql_base.cc.
+ table_share can be NULL, for example, in ha_delete_table() or
+ ha_rename_table().
*/
if (table_share == NULL || table_share->tmp_table == NO_TMP_TABLE)
ha_info->set_trx_read_write();
@@ -4647,7 +4877,8 @@ handler::check_if_supported_inplace_alter(TABLE *altered_table,
ALTER_DROP_CHECK_CONSTRAINT |
ALTER_PARTITIONED |
ALTER_VIRTUAL_GCOL_EXPR |
- ALTER_RENAME;
+ ALTER_RENAME |
+ ALTER_RENAME_INDEX;
/* Is there at least one operation that requires copy algorithm? */
if (ha_alter_info->handler_flags & ~inplace_offline_operations)
@@ -4735,35 +4966,106 @@ handler::ha_rename_table(const char *from, const char *to)
/**
- Delete table: public interface.
+ Drop table in the engine: public interface.
- @sa handler::delete_table()
+ @sa handler::drop_table()
+
+ The difference between this and delete_table() is that the table is open in
+ drop_table().
*/
-int
-handler::ha_delete_table(const char *name)
+void
+handler::ha_drop_table(const char *name)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
+ if (check_if_updates_are_ignored("DROP"))
+ return;
+
mark_trx_read_write();
- return delete_table(name);
+ drop_table(name);
}
/**
- Drop table in the engine: public interface.
+ Structure used during force drop table.
+*/
- @sa handler::drop_table()
+struct st_force_drop_table_params
+{
+ const char *path;
+ const LEX_CSTRING *db;
+ const LEX_CSTRING *alias;
+ int error;
+ bool discovering;
+};
- The difference between this and delete_table() is that the table is open in
- drop_table().
+
+/**
+ Try to delete table from a given plugin
+ Table types with discovery is ignored as these .frm files would have
+ been created during discovery and thus doesn't need to be found
+ for drop table force
*/
-void
-handler::ha_drop_table(const char *name)
+static my_bool delete_table_force(THD *thd, plugin_ref plugin, void *arg)
{
- DBUG_ASSERT(m_lock_type == F_UNLCK);
- mark_trx_read_write();
+ handlerton *hton = plugin_hton(plugin);
+ st_force_drop_table_params *param = (st_force_drop_table_params *)arg;
+
+ if (param->discovering == (hton->discover_table != NULL) &&
+ !(thd->slave_thread && (hton->flags & HTON_IGNORE_UPDATES)))
+ {
+ int error;
+ error= ha_delete_table(thd, hton, param->path, param->db, param->alias, 0);
+ if (error > 0 && !non_existing_table_error(error))
+ param->error= error;
+ if (error == 0)
+ {
+ param->error= 0;
+ return TRUE; // Table was deleted
+ }
+ }
+ return FALSE;
+}
+
+/**
+ @brief
+ Traverse all plugins to delete table when .frm file is missing.
+
+ @return -1 Table was not found in any engine
+ @return 0 Table was found in some engine and delete succeded
+ @return # Error from first engine that had a table but didn't succeed to
+ delete the table
+ @return HA_ERR_ROW_IS_REFERENCED if foreign key reference is encountered,
+
+*/
+
+int ha_delete_table_force(THD *thd, const char *path, const LEX_CSTRING *db,
+ const LEX_CSTRING *alias)
+{
+ st_force_drop_table_params param;
+ Table_exists_error_handler no_such_table_handler;
+ DBUG_ENTER("ha_delete_table_force");
+
+ param.path= path;
+ param.db= db;
+ param.alias= alias;
+ param.error= -1; // Table not found
+ param.discovering= true;
- return drop_table(name);
+ thd->push_internal_handler(&no_such_table_handler);
+ if (plugin_foreach(thd, delete_table_force, MYSQL_STORAGE_ENGINE_PLUGIN,
+ &param))
+ param.error= 0; // Delete succeded
+ else
+ {
+ param.discovering= false;
+ if (plugin_foreach(thd, delete_table_force, MYSQL_STORAGE_ENGINE_PLUGIN,
+ &param))
+ param.error= 0; // Delete succeded
+ }
+ thd->pop_internal_handler();
+ DBUG_RETURN(param.error);
}
@@ -4795,7 +5097,7 @@ handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info_arg)
int
handler::ha_create_partitioning_metadata(const char *name,
const char *old_name,
- int action_flag)
+ chf_create_flags action_flag)
{
/*
Normally this is done when unlocked, but in fast_alter_partition_table,
@@ -4887,7 +5189,7 @@ int ha_enable_transaction(THD *thd, bool on)
DBUG_ENTER("ha_enable_transaction");
DBUG_PRINT("enter", ("on: %d", (int) on));
- if ((thd->transaction.on= on))
+ if ((thd->transaction->on= on))
{
/*
Now all storage engines should have transaction handling enabled.
@@ -5005,7 +5307,7 @@ void handler::update_global_table_stats()
table->s->table_cache_key.length)))
{
if (!(table_stats = ((TABLE_STATS*)
- my_malloc(sizeof(TABLE_STATS),
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(TABLE_STATS),
MYF(MY_WME | MY_ZEROFILL)))))
{
/* Out of memory error already given */
@@ -5070,7 +5372,7 @@ void handler::update_global_index_stats()
key_length)))
{
if (!(index_stats = ((INDEX_STATS*)
- my_malloc(sizeof(INDEX_STATS),
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(INDEX_STATS),
MYF(MY_WME | MY_ZEROFILL)))))
goto end; // Error is already given
@@ -5393,7 +5695,7 @@ static my_bool discover_handlerton(THD *thd, plugin_ref plugin,
{
TABLE_SHARE *share= (TABLE_SHARE *)arg;
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->discover_table)
+ if (hton->discover_table)
{
share->db_plugin= plugin;
int error= hton->discover_table(hton, thd, share);
@@ -5469,7 +5771,7 @@ static my_bool discover_existence(THD *thd, plugin_ref plugin,
{
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)
+ if (!ht->discover_table_existence)
return args->frm_exists;
args->hton= ht;
@@ -5481,43 +5783,6 @@ static my_bool discover_existence(THD *thd, plugin_ref plugin,
return ht->discover_table_existence(ht, args->db, args->table_name);
}
-class Table_exists_error_handler : public Internal_error_handler
-{
-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
@@ -5546,7 +5811,8 @@ private:
*hton will be NULL.
*/
-bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name,
+bool ha_table_exists(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name,
handlerton **hton, bool *is_sequence)
{
handlerton *dummy;
@@ -5583,24 +5849,30 @@ bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_n
{
char engine_buf[NAME_CHAR_LEN + 1];
LEX_CSTRING engine= { engine_buf, 0 };
- Table_type type;
+ Table_type type= dd_frm_type(thd, path, &engine);
- if ((type= dd_frm_type(thd, path, &engine, is_sequence)) ==
- TABLE_TYPE_UNKNOWN)
- DBUG_RETURN(0);
-
- if (type != TABLE_TYPE_VIEW)
- {
- plugin_ref p= plugin_lock_by_name(thd, &engine,
- MYSQL_STORAGE_ENGINE_PLUGIN);
- *hton= p ? plugin_hton(p) : NULL;
- if (*hton)
- // verify that the table really exists
- exists= discover_existence(thd, p, &args);
- }
- else
+ switch (type) {
+ case TABLE_TYPE_UNKNOWN:
+ DBUG_PRINT("exit", ("Exist, cannot be opened"));
+ DBUG_RETURN(true); // Frm exists
+ case TABLE_TYPE_VIEW:
*hton= view_pseudo_hton;
+ DBUG_PRINT("exit", ("Exist, view"));
+ DBUG_RETURN(true); // Frm exists
+ case TABLE_TYPE_SEQUENCE:
+ *is_sequence= true;
+ /* fall through */
+ case TABLE_TYPE_NORMAL:
+ {
+ plugin_ref p= plugin_lock_by_name(thd, &engine,
+ MYSQL_STORAGE_ENGINE_PLUGIN);
+ *hton= p ? plugin_hton(p) : NULL;
+ if (*hton) // verify that the table really exists
+ exists= discover_existence(thd, p, &args);
+ }
+ }
}
+ DBUG_PRINT("exit", (exists ? "Exists" : "Does not exist"));
DBUG_RETURN(exists);
}
@@ -5610,13 +5882,16 @@ bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_n
{
if (hton)
*hton= args.hton;
+ DBUG_PRINT("exit", ("discovery found file"));
DBUG_RETURN(TRUE);
}
if (need_full_discover_for_existence)
{
TABLE_LIST table;
+ bool exists;
uint flags = GTS_TABLE | GTS_VIEW;
+
if (!hton)
flags|= GTS_NOLOCK;
@@ -5633,12 +5908,43 @@ bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_n
}
// 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());
+ exists= !no_such_table_handler.safely_trapped_errors();
+ DBUG_PRINT("exit", (exists ? "Exists" : "Does not exist"));
+ DBUG_RETURN(exists);
}
+ DBUG_PRINT("exit", ("Does not exist"));
DBUG_RETURN(FALSE);
}
+
+/*
+ Check if the CREATE/ALTER table should be ignored
+ This could happen for slaves where the table is shared between master
+ and slave
+
+ If statement is ignored, write a note
+*/
+
+bool handler::check_if_updates_are_ignored(const char *op) const
+{
+ return ha_check_if_updates_are_ignored(table->in_use, ht, op);
+}
+
+
+bool ha_check_if_updates_are_ignored(THD *thd, handlerton *hton,
+ const char *op)
+{
+ DBUG_ENTER("ha_check_if_updates_are_ignored");
+ if (!thd->slave_thread || !(hton= ha_checktype(thd, hton, 1)))
+ DBUG_RETURN(0); // Not slave or no engine
+ if (!(hton->flags & HTON_IGNORE_UPDATES))
+ DBUG_RETURN(0); // Not shared table
+ my_error(ER_SLAVE_IGNORED_SHARED_TABLE, MYF(ME_NOTE), op);
+ DBUG_RETURN(1);
+}
+
+
/**
Discover all table names in a given database
*/
@@ -5649,13 +5955,13 @@ static int cmp_file_names(const void *a, const void *b)
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));
+ return cs->strnncoll(aa, strlen(aa), bb, strlen(bb));
}
static int cmp_table_names(LEX_CSTRING * const *a, LEX_CSTRING * const *b)
{
- return my_strnncoll(&my_charset_bin, (uchar*)((*a)->str), (*a)->length,
- (uchar*)((*b)->str), (*b)->length);
+ return my_charset_bin.strnncoll((*a)->str, (*a)->length,
+ (*b)->str, (*b)->length);
}
#ifndef DBUG_OFF
@@ -5689,8 +5995,8 @@ bool Discovered_table_list::add_table(const char *tname, size_t tlen)
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))
+ if (wild && table_alias_charset->wildcmp(tname, tname + tlen, wild, wend,
+ wild_prefix, wild_one, wild_many))
return 0;
LEX_CSTRING *name= thd->make_clex_string(tname, tlen);
@@ -5759,7 +6065,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin,
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)
+ if (ht->discover_table_names)
{
size_t old_elements= args->result->tables->elements();
if (ht->discover_table_names(ht, args->db, args->dirp, args->result))
@@ -5804,6 +6110,8 @@ int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp,
error= ext_table_discovery_simple(dirp, result) ||
plugin_foreach(thd, discover_names,
MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+ if (args.possible_duplicates > 0)
+ result->remove_duplicates();
}
else
{
@@ -6016,7 +6324,7 @@ extern "C" check_result_t handler_index_cond_check(void* h_arg)
check_result_t res;
DEBUG_SYNC(thd, "handler_index_cond_check");
- enum thd_kill_levels abort_at= h->has_transactions() ?
+ enum thd_kill_levels abort_at= h->has_rollback() ?
THD_ABORT_SOFTLY : THD_ABORT_ASAP;
if (thd_kill_level(thd) > abort_at)
return CHECK_ABORTED_BY_USER;
@@ -6178,7 +6486,7 @@ static my_bool showstat_handlerton(THD *thd, plugin_ref plugin,
{
enum ha_stat_type stat= *(enum ha_stat_type *) arg;
handlerton *hton= plugin_hton(plugin);
- if (hton->state == SHOW_OPTION_YES && hton->show_status &&
+ if (hton->show_status &&
hton->show_status(hton, thd, stat_print, stat))
return TRUE;
return FALSE;
@@ -6210,17 +6518,8 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
}
else
{
- if (db_type->state != SHOW_OPTION_YES)
- {
- const LEX_CSTRING *name= hton_name(db_type);
- result= stat_print(thd, name->str, name->length,
- "", 0, "DISABLED", 8) ? 1 : 0;
- }
- else
- {
- result= db_type->show_status &&
- db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0;
- }
+ result= db_type->show_status &&
+ db_type->show_status(db_type, thd, stat_print, stat) ? 1 : 0;
}
/*
@@ -6252,32 +6551,35 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
1 Row needs to be logged
*/
-bool handler::check_table_binlog_row_based(bool binlog_row)
+bool handler::check_table_binlog_row_based()
{
- if (table->versioned(VERS_TRX_ID))
- return false;
- if (unlikely((table->in_use->variables.sql_log_bin_off)))
- return 0; /* Called by partitioning engine */
-#ifdef WITH_WSREP
- if (!table->in_use->variables.sql_log_bin &&
- wsrep_thd_is_applying(table->in_use))
- return 0; /* wsrep patch sets sql_log_bin to silence binlogging
- from high priority threads */
-#endif /* WITH_WSREP */
if (unlikely((!check_table_binlog_row_based_done)))
{
check_table_binlog_row_based_done= 1;
check_table_binlog_row_based_result=
- check_table_binlog_row_based_internal(binlog_row);
+ check_table_binlog_row_based_internal();
}
return check_table_binlog_row_based_result;
}
-bool handler::check_table_binlog_row_based_internal(bool binlog_row)
+bool handler::check_table_binlog_row_based_internal()
{
THD *thd= table->in_use;
+#ifdef WITH_WSREP
+ if (!thd->variables.sql_log_bin &&
+ wsrep_thd_is_applying(table->in_use))
+ {
+ /*
+ wsrep patch sets sql_log_bin to silence binlogging from high
+ priority threads
+ */
+ return 0;
+ }
+#endif
return (table->s->can_do_row_logging &&
+ !table->versioned(VERS_TRX_ID) &&
+ !(thd->variables.option_bits & OPTION_BIN_TMP_LOG_OFF) &&
thd->is_current_stmt_binlog_format_row() &&
/*
Wsrep partially enables binary logging if it have not been
@@ -6293,9 +6595,9 @@ bool handler::check_table_binlog_row_based_internal(bool binlog_row)
Otherwise, return 'true' if binary logging is on.
*/
- IF_WSREP(((WSREP_EMULATE_BINLOG(thd) &&
+ IF_WSREP(((WSREP_EMULATE_BINLOG_NNULL(thd) &&
wsrep_thd_is_local(thd)) ||
- ((WSREP(thd) ||
+ ((WSREP_NNULL(thd) ||
(thd->variables.option_bits & OPTION_BIN_LOG)) &&
mysql_bin_log.is_open())),
(thd->variables.option_bits & OPTION_BIN_LOG) &&
@@ -6303,137 +6605,22 @@ bool handler::check_table_binlog_row_based_internal(bool binlog_row)
}
-/** @brief
- Write table maps for all (manually or automatically) locked tables
- to the binary log. Also, if binlog_annotate_row_events is ON,
- write Annotate_rows event before the first table map.
-
- SYNOPSIS
- write_locked_table_maps()
- thd Pointer to THD structure
-
- DESCRIPTION
- This function will generate and write table maps for all tables
- that are locked by the thread 'thd'.
-
- RETURN VALUE
- 0 All OK
- 1 Failed to write all table maps
-
- SEE ALSO
- THD::lock
-*/
-
-static int write_locked_table_maps(THD *thd)
+int handler::binlog_log_row(TABLE *table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func)
{
- DBUG_ENTER("write_locked_table_maps");
- DBUG_PRINT("enter", ("thd:%p thd->lock:%p "
- "thd->extra_lock: %p",
- thd, thd->lock, thd->extra_lock));
-
- DBUG_PRINT("debug", ("get_binlog_table_maps(): %d", thd->get_binlog_table_maps()));
-
- MYSQL_LOCK *locks[2];
- locks[0]= thd->extra_lock;
- locks[1]= thd->lock;
- my_bool with_annotate= IF_WSREP(!wsrep_fragments_certified_for_stmt(thd),
- true) &&
- thd->variables.binlog_annotate_row_events &&
- thd->query() && thd->query_length();
-
- for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
- {
- MYSQL_LOCK const *const lock= locks[i];
- if (lock == NULL)
- continue;
-
- TABLE **const end_ptr= lock->table + lock->table_count;
- for (TABLE **table_ptr= lock->table ;
- table_ptr != end_ptr ;
- ++table_ptr)
- {
- TABLE *const table= *table_ptr;
- DBUG_PRINT("info", ("Checking table %s", table->s->table_name.str));
- if (table->current_lock == F_WRLCK &&
- table->file->check_table_binlog_row_based(0))
- {
- /*
- We need to have a transactional behavior for SQLCOM_CREATE_TABLE
- (e.g. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
- compatible behavior with the STMT based replication even when
- the table is not transactional. In other words, if the operation
- fails while executing the insert phase nothing is written to the
- binlog.
-
- Note that at this point, we check the type of a set of tables to
- create the table map events. In the function binlog_log_row(),
- which calls the current function, we check the type of the table
- of the current row.
- */
- bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
- table->file->has_transactions();
- int const error= thd->binlog_write_table_map(table, has_trans,
- &with_annotate);
- /*
- If an error occurs, it is the responsibility of the caller to
- roll back the transaction.
- */
- if (unlikely(error))
- DBUG_RETURN(1);
- }
- }
- }
- DBUG_RETURN(0);
-}
-
-
-static int binlog_log_row_internal(TABLE* table,
- const uchar *before_record,
- const uchar *after_record,
- Log_func *log_func)
-{
- bool error= 0;
- THD *const thd= table->in_use;
-
- /*
- If there are no table maps written to the binary log, this is
- the first row handled in this statement. In that case, we need
- to write table maps for all locked tables to the binary log.
- */
- if (likely(!(error= ((thd->get_binlog_table_maps() == 0 &&
- write_locked_table_maps(thd))))))
- {
- /*
- We need to have a transactional behavior for SQLCOM_CREATE_TABLE
- (i.e. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
- compatible behavior with the STMT based replication even when
- the table is not transactional. In other words, if the operation
- fails while executing the insert phase nothing is written to the
- binlog.
- */
- bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE ||
- table->file->has_transactions();
- error= (*log_func)(thd, table, has_trans, before_record, after_record);
- }
- return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
-}
-
-int binlog_log_row(TABLE* table, const uchar *before_record,
- const uchar *after_record, Log_func *log_func)
-{
-#ifdef WITH_WSREP
- THD *const thd= table->in_use;
+ bool error;
+ THD *thd= table->in_use;
+ DBUG_ENTER("binlog_log_row");
- /* only InnoDB tables will be replicated through binlog emulation */
- if ((WSREP_EMULATE_BINLOG(thd) &&
- !(table->file->partition_ht()->flags & HTON_WSREP_REPLICATION)) ||
- thd->wsrep_ignore_table == true)
- return 0;
-#endif
+ if (!thd->binlog_table_maps &&
+ thd->binlog_write_table_maps())
+ DBUG_RETURN(HA_ERR_RBR_LOGGING_FAILED);
- if (!table->file->check_table_binlog_row_based(1))
- return 0;
- return binlog_log_row_internal(table, before_record, after_record, log_func);
+ error= (*log_func)(thd, table, row_logging_has_trans,
+ before_record, after_record);
+ DBUG_RETURN(error ? HA_ERR_RBR_LOGGING_FAILED : 0);
}
@@ -6479,7 +6666,7 @@ int handler::ha_external_lock(THD *thd, int lock_type)
We cache the table flags if the locking succeeded. Otherwise, we
keep them as they were when they were fetched in ha_open().
*/
- MYSQL_TABLE_LOCK_WAIT(m_psi, PSI_TABLE_EXTERNAL_LOCK, lock_type,
+ MYSQL_TABLE_LOCK_WAIT(PSI_TABLE_EXTERNAL_LOCK, lock_type,
{ error= external_lock(thd, lock_type); })
DBUG_EXECUTE_IF("external_lock_failure", error= HA_ERR_GENERIC;);
@@ -6519,6 +6706,7 @@ int handler::ha_external_lock(THD *thd, int lock_type)
int handler::ha_reset()
{
DBUG_ENTER("ha_reset");
+
/* Check that we have called all proper deallocation functions */
DBUG_ASSERT((uchar*) table->def_read_set.bitmap +
table->s->column_bitmap_size ==
@@ -6532,12 +6720,22 @@ int handler::ha_reset()
pushed_cond= NULL;
tracker= NULL;
mark_trx_read_write_done= 0;
+ /*
+ Disable row logging.
+ */
+ row_logging= row_logging_init= 0;
clear_cached_table_binlog_row_based_flag();
/* Reset information about pushed engine conditions */
cancel_pushed_idx_cond();
/* Reset information about pushed index conditions */
cancel_pushed_rowid_filter();
- clear_top_table_fields();
+ if (lookup_handler != this)
+ {
+ lookup_handler->ha_external_unlock(table->in_use);
+ lookup_handler->close();
+ delete lookup_handler;
+ lookup_handler= this;
+ }
DBUG_RETURN(reset());
}
@@ -6545,17 +6743,20 @@ int handler::ha_reset()
static int wsrep_after_row(THD *thd)
{
DBUG_ENTER("wsrep_after_row");
+ if (thd->internal_transaction())
+ DBUG_RETURN(0);
+
/* enforce wsrep_max_ws_rows */
thd->wsrep_affected_rows++;
if (wsrep_max_ws_rows &&
- wsrep_thd_is_local(thd) &&
- thd->wsrep_affected_rows > wsrep_max_ws_rows)
+ thd->wsrep_affected_rows > wsrep_max_ws_rows &&
+ wsrep_thd_is_local(thd))
{
trans_rollback_stmt(thd) || trans_rollback(thd);
my_message(ER_ERROR_DURING_COMMIT, "wsrep_max_ws_rows exceeded", MYF(0));
DBUG_RETURN(ER_ERROR_DURING_COMMIT);
}
- else if (wsrep_after_row(thd, false))
+ else if (wsrep_after_row_internal(thd))
{
DBUG_RETURN(ER_LOCK_DEADLOCK);
}
@@ -6563,33 +6764,33 @@ static int wsrep_after_row(THD *thd)
}
#endif /* WITH_WSREP */
-static int check_duplicate_long_entry_key(TABLE *table, handler *h,
- const uchar *new_rec, uint key_no)
+
+/**
+ Check if there is a conflicting unique hash key
+*/
+
+int handler::check_duplicate_long_entry_key(const uchar *new_rec, uint key_no)
{
- Field *hash_field;
int result, error= 0;
KEY *key_info= table->key_info + key_no;
- hash_field= key_info->key_part->field;
+ Field *hash_field= key_info->key_part->field;
uchar ptr[HA_HASH_KEY_LENGTH_WITH_NULL];
+ DBUG_ENTER("handler::check_duplicate_long_entry_key");
DBUG_ASSERT((key_info->flags & HA_NULL_PART_KEY &&
- key_info->key_length == HA_HASH_KEY_LENGTH_WITH_NULL)
- || key_info->key_length == HA_HASH_KEY_LENGTH_WITHOUT_NULL);
+ key_info->key_length == HA_HASH_KEY_LENGTH_WITH_NULL) ||
+ key_info->key_length == HA_HASH_KEY_LENGTH_WITHOUT_NULL);
if (hash_field->is_real_null())
- return 0;
+ DBUG_RETURN(0);
key_copy(ptr, new_rec, key_info, key_info->key_length, false);
- if (!table->check_unique_buf)
- table->check_unique_buf= (uchar *)alloc_root(&table->mem_root,
- table->s->reclength);
-
- result= h->ha_index_init(key_no, 0);
+ result= lookup_handler->ha_index_init(key_no, 0);
if (result)
- return result;
- store_record(table, check_unique_buf);
- result= h->ha_index_read_map(table->record[0],
+ DBUG_RETURN(result);
+ store_record(table, file->lookup_buffer);
+ result= lookup_handler->ha_index_read_map(table->record[0],
ptr, HA_WHOLE_KEY, HA_READ_KEY_EXACT);
if (!result)
{
@@ -6600,7 +6801,7 @@ static int check_duplicate_long_entry_key(TABLE *table, handler *h,
uint arg_count= temp->argument_count();
do
{
- my_ptrdiff_t diff= table->check_unique_buf - new_rec;
+ my_ptrdiff_t diff= table->file->lookup_buffer - new_rec;
is_same= true;
for (uint j=0; is_same && j < arg_count; j++)
{
@@ -6625,8 +6826,9 @@ static int check_duplicate_long_entry_key(TABLE *table, handler *h,
}
}
}
- while (!is_same && !(result= h->ha_index_next_same(table->record[0],
- ptr, key_info->key_length)));
+ while (!is_same &&
+ !(result= lookup_handler->ha_index_next_same(table->record[0],
+ ptr, key_info->key_length)));
if (is_same)
error= HA_ERR_FOUND_DUPP_KEY;
goto exit;
@@ -6636,16 +6838,25 @@ static int check_duplicate_long_entry_key(TABLE *table, handler *h,
exit:
if (error == HA_ERR_FOUND_DUPP_KEY)
{
- table->file->errkey= key_no;
- if (h->ha_table_flags() & HA_DUPLICATE_POS)
+ table->file->lookup_errkey= key_no;
+ if (ha_table_flags() & HA_DUPLICATE_POS)
{
- h->position(table->record[0]);
- memcpy(table->file->dup_ref, h->ref, h->ref_length);
+ lookup_handler->position(table->record[0]);
+ memcpy(table->file->dup_ref, lookup_handler->ref, ref_length);
}
}
- restore_record(table, check_unique_buf);
- h->ha_index_end();
- return error;
+ restore_record(table, file->lookup_buffer);
+ lookup_handler->ha_index_end();
+ DBUG_RETURN(error);
+}
+
+void handler::alloc_lookup_buffer()
+{
+ if (!lookup_buffer)
+ lookup_buffer= (uchar*)alloc_root(&table->mem_root,
+ table_share->max_unique_length
+ + table_share->null_fields
+ + table_share->reclength);
}
/** @brief
@@ -6653,20 +6864,20 @@ exit:
unique constraint on long columns.
@returns 0 if no duplicate else returns error
*/
-static int check_duplicate_long_entries(TABLE *table, handler *h,
- const uchar *new_rec)
+int handler::check_duplicate_long_entries(const uchar *new_rec)
{
- table->file->errkey= -1;
- int result;
+ lookup_errkey= (uint)-1;
for (uint i= 0; i < table->s->keys; i++)
{
+ int result;
if (table->key_info[i].algorithm == HA_KEY_ALG_LONG_HASH &&
- (result= check_duplicate_long_entry_key(table, h, new_rec, i)))
+ (result= check_duplicate_long_entry_key(new_rec, i)))
return result;
}
return 0;
}
+
/** @brief
check whether updated records breaks the
unique constraint on long columns.
@@ -6681,11 +6892,10 @@ static int check_duplicate_long_entries(TABLE *table, handler *h,
key as a parameter in normal insert key should be -1
@returns 0 if no duplicate else returns error
*/
-static int check_duplicate_long_entries_update(TABLE *table, handler *h, uchar *new_rec)
+int handler::check_duplicate_long_entries_update(const uchar *new_rec)
{
Field *field;
uint key_parts;
- int error= 0;
KEY *keyinfo;
KEY_PART_INFO *keypart;
/*
@@ -6693,7 +6903,7 @@ static int check_duplicate_long_entries_update(TABLE *table, handler *h, uchar *
with respect to fields in hash_str
*/
uint reclength= (uint) (table->record[1] - table->record[0]);
- table->clone_handler_for_update();
+
for (uint i= 0; i < table->s->keys; i++)
{
keyinfo= table->key_info + i;
@@ -6703,13 +6913,13 @@ static int check_duplicate_long_entries_update(TABLE *table, handler *h, uchar *
keypart= keyinfo->key_part - key_parts;
for (uint j= 0; j < key_parts; j++, keypart++)
{
+ int error;
field= keypart->field;
- /* Compare fields if they are different then check for duplicates*/
- if(field->cmp_binary_offset(reclength))
+ /* Compare fields if they are different then check for duplicates */
+ if (field->cmp_binary_offset(reclength))
{
- if((error= check_duplicate_long_entry_key(table, table->update_handler,
- new_rec, i)))
- goto exit;
+ if((error= check_duplicate_long_entry_key(new_rec, i)))
+ return error;
/*
break because check_duplicate_long_entries_key will
take care of remaining fields
@@ -6719,42 +6929,236 @@ static int check_duplicate_long_entries_update(TABLE *table, handler *h, uchar *
}
}
}
- exit:
+ return 0;
+}
+
+
+int handler::ha_check_overlaps(const uchar *old_data, const uchar* new_data)
+{
+ DBUG_ASSERT(new_data);
+ if (this != table->file)
+ return 0;
+ if (!table_share->period.unique_keys)
+ return 0;
+ if (table->versioned() && !table->vers_end_field()->is_max())
+ return 0;
+
+ const bool is_update= old_data != NULL;
+ uchar *record_buffer= lookup_buffer + table_share->max_unique_length
+ + table_share->null_fields;
+
+ // Needs to compare record refs later is old_row_found()
+ if (is_update)
+ position(old_data);
+
+ DBUG_ASSERT(!keyread_enabled());
+
+ int error= 0;
+ lookup_errkey= (uint)-1;
+
+ for (uint key_nr= 0; key_nr < table_share->keys && !error; key_nr++)
+ {
+ const KEY &key_info= table->key_info[key_nr];
+ const uint key_parts= key_info.user_defined_key_parts;
+ if (!key_info.without_overlaps)
+ continue;
+
+ if (is_update)
+ {
+ bool key_used= false;
+ for (uint k= 0; k < key_parts && !key_used; k++)
+ key_used= bitmap_is_set(table->write_set,
+ key_info.key_part[k].fieldnr - 1);
+ if (!key_used)
+ continue;
+ }
+
+ error= lookup_handler->ha_index_init(key_nr, 0);
+ if (error)
+ return error;
+
+ error= lookup_handler->ha_start_keyread(key_nr);
+ DBUG_ASSERT(!error);
+
+ const uint period_field_length= key_info.key_part[key_parts - 1].length;
+ const uint key_base_length= key_info.key_length - 2 * period_field_length;
+
+ key_copy(lookup_buffer, new_data, &key_info, 0);
+
+ /* Copy period_start to period_end.
+ the value in period_start field is not significant, but anyway let's leave
+ it defined to avoid uninitialized memory access
+ */
+ memcpy(lookup_buffer + key_base_length,
+ lookup_buffer + key_base_length + period_field_length,
+ period_field_length);
+
+ /* Find row with period_end > (period_start of new_data) */
+ error = lookup_handler->ha_index_read_map(record_buffer, lookup_buffer,
+ key_part_map((1 << (key_parts - 1)) - 1),
+ HA_READ_AFTER_KEY);
+
+ if (!error && is_update)
+ {
+ /* In case of update it could happen that the nearest neighbour is
+ a record we are updating. It means, that there are no overlaps
+ from this side.
+
+ An assumption is made that during update we always have the last
+ fetched row in old_data. Therefore, comparing ref's is enough
+ */
+ DBUG_ASSERT(lookup_handler != this);
+ DBUG_ASSERT(inited != NONE);
+ DBUG_ASSERT(ref_length == lookup_handler->ref_length);
+
+ lookup_handler->position(record_buffer);
+ if (memcmp(ref, lookup_handler->ref, ref_length) == 0)
+ error= lookup_handler->ha_index_next(record_buffer);
+ }
+
+ if (!error && table->check_period_overlaps(key_info, new_data, record_buffer))
+ error= HA_ERR_FOUND_DUPP_KEY;
+
+ if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE)
+ error= 0;
+
+ if (error == HA_ERR_FOUND_DUPP_KEY)
+ lookup_errkey= key_nr;
+
+ int end_error= lookup_handler->ha_end_keyread();
+ DBUG_ASSERT(!end_error);
+
+ end_error= lookup_handler->ha_index_end();
+ if (!error && end_error)
+ error= end_error;
+ }
+
return error;
}
+
+/**
+ Check if galera disables binary logging for this table
+
+ @return 0 Binary logging disabled
+ @return 1 Binary logging can be enabled
+*/
+
+
+static inline bool wsrep_check_if_binlog_row(TABLE *table)
+{
+#ifdef WITH_WSREP
+ THD *const thd= table->in_use;
+
+ /* only InnoDB tables will be replicated through binlog emulation */
+ if ((WSREP_EMULATE_BINLOG(thd) &&
+ !(table->file->partition_ht()->flags & HTON_WSREP_REPLICATION)) ||
+ thd->wsrep_ignore_table == true)
+ return 0;
+#endif
+ return 1;
+}
+
+
+/**
+ Prepare handler for row logging
+
+ @return 0 if handler will not participate in row logging
+ @return 1 handler will participate in row logging
+
+ This function is always safe to call on an opened table.
+*/
+
+bool handler::prepare_for_row_logging()
+{
+ DBUG_ENTER("handler::prepare_for_row_logging");
+
+ /* Check if we should have row logging */
+ if (wsrep_check_if_binlog_row(table) &&
+ check_table_binlog_row_based())
+ {
+ /*
+ Row logging enabled. Intialize all variables and write
+ annotated and table maps
+ */
+ row_logging= row_logging_init= 1;
+
+ /*
+ We need to have a transactional behavior for SQLCOM_CREATE_TABLE
+ (e.g. CREATE TABLE... SELECT * FROM TABLE) in order to keep a
+ compatible behavior with the STMT based replication even when
+ the table is not transactional. In other words, if the operation
+ fails while executing the insert phase nothing is written to the
+ binlog.
+ */
+ row_logging_has_trans=
+ ((sql_command_flags[table->in_use->lex->sql_command] &
+ (CF_SCHEMA_CHANGE | CF_ADMIN_COMMAND)) ||
+ table->file->has_transactions_and_rollback());
+ }
+ else
+ {
+ /* Check row_logging has not been properly cleared from previous command */
+ DBUG_ASSERT(row_logging == 0);
+ }
+ DBUG_RETURN(row_logging);
+}
+
+
+/*
+ Do all initialization needed for insert
+*/
+
+int handler::prepare_for_insert(bool do_create)
+{
+ /* Preparation for unique of blob's */
+ if (table->s->long_unique_table || table->s->period.unique_keys)
+ {
+ if (do_create && create_lookup_handler())
+ return 1;
+ alloc_lookup_buffer();
+ }
+ return 0;
+}
+
+
int handler::ha_write_row(const 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");
+ if ((error= ha_check_overlaps(NULL, buf)))
+ DBUG_RETURN(error);
+
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_write_count);
- if (table->s->long_unique_table)
+ if (table->s->long_unique_table && this == table->file)
{
- if (this->inited == RND)
- table->clone_handler_for_update();
- handler *h= table->update_handler ? table->update_handler : table->file;
- if ((error= check_duplicate_long_entries(table, h, buf)))
+ DBUG_ASSERT(inited == NONE || lookup_handler != this);
+ if ((error= check_duplicate_long_entries(buf)))
DBUG_RETURN(error);
}
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_WRITE_ROW, MAX_KEY, error,
{ error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
- if (likely(!error) && !row_already_logged)
+ if (likely(!error))
{
rows_changed++;
- error= binlog_log_row(table, 0, buf, log_func);
+ if (row_logging)
+ {
+ Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
+ error= binlog_log_row(table, 0, buf, log_func);
+ }
#ifdef WITH_WSREP
- if (table_share->tmp_table == NO_TMP_TABLE &&
- WSREP(ha_thd()) && (error= wsrep_after_row(ha_thd())))
+ if (WSREP_NNULL(ha_thd()) && table_share->tmp_table == NO_TMP_TABLE &&
+ ht->flags & HTON_WSREP_REPLICATION &&
+ !error && (error= wsrep_after_row(ha_thd())))
{
DBUG_RETURN(error);
}
@@ -6769,10 +7173,8 @@ int handler::ha_write_row(const uchar *buf)
int handler::ha_update_row(const uchar *old_data, const 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]
(and the old record is in record[1]).
@@ -6780,29 +7182,35 @@ int handler::ha_update_row(const uchar *old_data, const uchar *new_data)
DBUG_ASSERT(new_data == table->record[0]);
DBUG_ASSERT(old_data == table->record[1]);
+ if ((error= ha_check_overlaps(old_data, new_data)))
+ return error;
+
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_update_count);
- if (table->s->long_unique_table &&
- (error= check_duplicate_long_entries_update(table, table->file, (uchar *)new_data)))
+ if (table->s->long_unique_table && this == table->file &&
+ (error= check_duplicate_long_entries_update(new_data)))
{
return error;
}
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_UPDATE_ROW, active_index, 0,
{ error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
- if (likely(!error) && !row_already_logged)
+ if (likely(!error))
{
rows_changed++;
- error= binlog_log_row(table, old_data, new_data, log_func);
-#ifdef WITH_WSREP
- if (table_share->tmp_table == NO_TMP_TABLE &&
- WSREP(ha_thd()) && (error= wsrep_after_row(ha_thd())))
+ if (row_logging)
{
- return error;
+ Log_func *log_func= Update_rows_log_event::binlog_row_logging_function;
+ error= binlog_log_row(table, old_data, new_data, log_func);
}
+#ifdef WITH_WSREP
+ if (WSREP_NNULL(ha_thd()) && table_share->tmp_table == NO_TMP_TABLE &&
+ ht->flags & HTON_WSREP_REPLICATION &&
+ !error && (error= wsrep_after_row(ha_thd())))
+ return error;
#endif /* WITH_WSREP */
}
return error;
@@ -6839,7 +7247,6 @@ int handler::update_first_row(const uchar *new_data)
int handler::ha_delete_row(const uchar *buf)
{
int error;
- Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
m_lock_type == F_WRLCK);
/*
@@ -6852,16 +7259,21 @@ int handler::ha_delete_row(const uchar *buf)
mark_trx_read_write();
increment_statistics(&SSV::ha_delete_count);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_DELETE_ROW, active_index, error,
{ error= delete_row(buf);})
MYSQL_DELETE_ROW_DONE(error);
if (likely(!error))
{
rows_changed++;
- error= binlog_log_row(table, buf, 0, log_func);
+ if (row_logging)
+ {
+ Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+ error= binlog_log_row(table, buf, 0, log_func);
+ }
#ifdef WITH_WSREP
- if (table_share->tmp_table == NO_TMP_TABLE &&
- WSREP(ha_thd()) && (error= wsrep_after_row(ha_thd())))
+ if (WSREP_NNULL(ha_thd()) && table_share->tmp_table == NO_TMP_TABLE &&
+ ht->flags & HTON_WSREP_REPLICATION &&
+ !error && (error= wsrep_after_row(ha_thd())))
{
return error;
}
@@ -6888,11 +7300,10 @@ int handler::ha_delete_row(const uchar *buf)
int handler::ha_direct_update_rows(ha_rows *update_rows, ha_rows *found_rows)
{
int error;
-
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
- error = direct_update_rows(update_rows, found_rows);
+ error= direct_update_rows(update_rows, found_rows);
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
@@ -7326,7 +7737,7 @@ int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *ta
cache_key_length= db->length + 1 + table->length + 1;
- if(!(cache_key= (uchar *)my_malloc(cache_key_length,
+ if(!(cache_key= (uchar *)my_malloc(PSI_INSTRUMENT_ME, cache_key_length,
MYF(MY_WME | MY_ZEROFILL))))
{
/* Out of memory error already given */
@@ -7550,11 +7961,7 @@ bool Table_scope_and_contents_source_st::vers_check_system_fields(
if (!(alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING))
return false;
- bool can_native= ha_check_storage_engine_flag(db_type,
- HTON_NATIVE_SYS_VERSIONING)
- || db_type->db_type == DB_TYPE_PARTITION_DB;
-
- return vers_info.check_sys_fields(table_name, db, alter_info, can_native);
+ return vers_info.check_sys_fields(table_name, db, alter_info);
}
@@ -7660,7 +8067,16 @@ bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info,
return false;
}
- return fix_implicit(thd, alter_info);
+ if (fix_implicit(thd, alter_info))
+ return true;
+
+ if (alter_info->flags & ALTER_ADD_SYSTEM_VERSIONING)
+ {
+ if (check_sys_fields(table_name, share->db, alter_info))
+ return true;
+ }
+
+ return false;
}
bool
@@ -7780,83 +8196,121 @@ bool Vers_parse_info::check_conditions(const Lex_table_name &table_name,
return false;
}
-static bool is_versioning_timestamp(const Create_field *f)
+static bool is_versioning_timestamp(const Column_definition *f)
{
return f->type_handler() == &type_handler_timestamp2 &&
f->length == MAX_DATETIME_FULL_WIDTH;
}
-static bool is_some_bigint(const Create_field *f)
+static bool is_some_bigint(const Column_definition *f)
{
- return f->type_handler() == &type_handler_longlong ||
+ return f->type_handler() == &type_handler_slonglong ||
+ f->type_handler() == &type_handler_ulonglong ||
f->type_handler() == &type_handler_vers_trx_id;
}
-static bool is_versioning_bigint(const Create_field *f)
+static bool is_versioning_bigint(const Column_definition *f)
{
return is_some_bigint(f) && f->flags & UNSIGNED_FLAG &&
f->length == MY_INT64_NUM_DECIMAL_DIGITS - 1;
}
-static bool require_timestamp(const Create_field *f, Lex_table_name table_name)
+static void require_timestamp_error(const char *field, const char *table)
+{
+ my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), field, "TIMESTAMP(6)", table);
+}
+
+static void require_trx_id_error(const char *field, const char *table)
+{
+ my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), field, "BIGINT(20) UNSIGNED",
+ table);
+}
+
+
+bool Vers_type_timestamp::check_sys_fields(const LEX_CSTRING &table_name,
+ const Column_definition *row_start,
+ const Column_definition *row_end) const
{
- my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str, "TIMESTAMP(6)",
- table_name.str);
- return true;
+ if (!is_versioning_timestamp(row_start))
+ {
+ require_timestamp_error(row_start->field_name.str, table_name.str);
+ return true;
+ }
+
+ if (row_end->type_handler()->vers() != this ||
+ !is_versioning_timestamp(row_end))
+ {
+ require_timestamp_error(row_end->field_name.str, table_name.str);
+ return true;
+ }
+
+ return false;
}
-static bool require_bigint(const Create_field *f, Lex_table_name table_name)
+
+
+bool Vers_type_trx::check_sys_fields(const LEX_CSTRING &table_name,
+ const Column_definition *row_start,
+ const Column_definition *row_end) const
{
- my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str,
- "BIGINT(20) UNSIGNED", table_name.str);
- return true;
+ if (!is_versioning_bigint(row_start))
+ {
+ require_trx_id_error(row_start->field_name.str, table_name.str);
+ return true;
+ }
+
+ if (row_end->type_handler()->vers() != this ||
+ !is_versioning_bigint(row_end))
+ {
+ require_trx_id_error(row_end->field_name.str, table_name.str);
+ return true;
+ }
+
+ if (!is_some_bigint(row_start))
+ {
+ require_timestamp_error(row_start->field_name.str, table_name.str);
+ return true;
+ }
+
+ if (!TR_table::use_transaction_registry)
+ {
+ my_error(ER_VERS_TRT_IS_DISABLED, MYF(0));
+ return true;
+ }
+
+ return false;
}
bool Vers_parse_info::check_sys_fields(const Lex_table_name &table_name,
const Lex_table_name &db,
- Alter_info *alter_info,
- bool can_native) const
+ Alter_info *alter_info) const
{
if (check_conditions(table_name, db))
return true;
+ List_iterator<Create_field> it(alter_info->create_list);
const Create_field *row_start= NULL;
const Create_field *row_end= NULL;
-
- List_iterator<Create_field> it(alter_info->create_list);
- while (Create_field *f= it++)
+ while (const Create_field *f= it++)
{
- if (!row_start && f->flags & VERS_SYS_START_FLAG)
+ if (f->flags & VERS_SYS_START_FLAG && !row_start)
row_start= f;
- else if (!row_end && f->flags & VERS_SYS_END_FLAG)
+ if (f->flags & VERS_SYS_END_FLAG && !row_end)
row_end= f;
}
- const bool expect_timestamp=
- !can_native || !is_some_bigint(row_start) || !is_some_bigint(row_end);
+ DBUG_ASSERT(row_start);
+ DBUG_ASSERT(row_end);
- if (expect_timestamp)
- {
- if (!is_versioning_timestamp(row_start))
- return require_timestamp(row_start, table_name);
+ const Vers_type_handler *row_start_vers= row_start->type_handler()->vers();
- if (!is_versioning_timestamp(row_end))
- return require_timestamp(row_end, table_name);
- }
- else
+ if (!row_start_vers)
{
- if (!is_versioning_bigint(row_start))
- return require_bigint(row_start, table_name);
-
- if (!is_versioning_bigint(row_end))
- return require_bigint(row_end, table_name);
+ require_timestamp_error(row_start->field_name.str, table_name);
+ return true;
}
- if (is_versioning_bigint(row_start) && is_versioning_bigint(row_end) &&
- !TR_table::use_transaction_registry)
- {
- my_error(ER_VERS_TRT_IS_DISABLED, MYF(0));
+ if (row_start_vers->check_sys_fields(table_name, row_start, row_end))
return true;
- }
return false;
}
diff --git a/sql/handler.h b/sql/handler.h
index 3b0cedfcd96..e06b6a7aa43 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -52,7 +52,6 @@ class Rowid_filter;
class Field_string;
class Field_varstring;
class Field_blob;
-class Field_geom;
class Column_definition;
// the following is for checking tables
@@ -71,6 +70,7 @@ class Column_definition;
#define HA_ADMIN_NEEDS_UPGRADE -10
#define HA_ADMIN_NEEDS_ALTER -11
#define HA_ADMIN_NEEDS_CHECK -12
+#define HA_ADMIN_COMMIT_ERROR -13
/**
Return values for check_if_supported_inplace_alter().
@@ -91,6 +91,15 @@ enum enum_alter_inplace_result {
HA_ALTER_INPLACE_NO_LOCK
};
+/* Flags for create_partitioning_metadata() */
+
+enum chf_create_flags {
+ CHF_CREATE_FLAG,
+ CHF_DELETE_FLAG,
+ CHF_RENAME_FLAG,
+ CHF_INDEX_FLAG
+};
+
/* Bits in table_flags() to show what database can do */
#define HA_NO_TRANSACTIONS (1ULL << 0) /* Doesn't support transactions */
@@ -298,6 +307,9 @@ enum enum_alter_inplace_result {
#define HA_PERSISTENT_TABLE (1ULL << 48)
+/* If storage engine uses another engine as a base */
+#define HA_REUSES_FILE_NAMES (1ULL << 49)
+
/*
Set of all binlog flags. Currently only contain the capabilities
flags.
@@ -328,8 +340,17 @@ enum enum_alter_inplace_result {
#define HA_CAN_ONLINE_BACKUPS (1ULL << 56)
/* Support native hash index */
-#define HA_CAN_HASH_KEYS (1ULL << 58)
-#define HA_LAST_TABLE_FLAG HA_CAN_HASH_KEYS
+#define HA_CAN_HASH_KEYS (1ULL << 57)
+#define HA_CRASH_SAFE (1ULL << 58)
+
+/*
+ There is no need to evict the table from the table definition cache having
+ run ANALYZE TABLE on it
+ */
+#define HA_ONLINE_ANALYZE (1ULL << 59)
+
+#define HA_LAST_TABLE_FLAG HA_ONLINE_ANALYZE
+
/* bits in index_flags(index_number) for what you can do with index */
#define HA_READ_NEXT 1 /* TODO really use this flag */
@@ -512,6 +533,7 @@ enum legacy_db_type
DB_TYPE_BINLOG=21,
DB_TYPE_PBXT=23,
DB_TYPE_PERFORMANCE_SCHEMA=28,
+ DB_TYPE_S3=41,
DB_TYPE_ARIA=42,
DB_TYPE_TOKUDB=43,
DB_TYPE_SEQUENCE=44,
@@ -605,8 +627,11 @@ given at all. */
/* Create a sequence */
#define HA_CREATE_USED_SEQUENCE (1UL << 25)
+/* Tell binlog_show_create_table to print all engine options */
+#define HA_CREATE_PRINT_ALL_OPTIONS (1UL << 26)
typedef ulonglong alter_table_operations;
+typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*);
/*
These flags are set by the parser and describes the type of
@@ -1030,11 +1055,7 @@ enum enum_schema_tables
SCH_TABLE_PRIVILEGES,
SCH_TRIGGERS,
SCH_USER_PRIVILEGES,
- SCH_VIEWS,
-#ifdef HAVE_SPATIAL
- SCH_GEOMETRY_COLUMNS,
- SCH_SPATIAL_REF_SYS,
-#endif /*HAVE_SPATIAL*/
+ SCH_VIEWS
};
struct TABLE_SHARE;
@@ -1248,11 +1269,6 @@ typedef struct st_order ORDER;
struct handlerton
{
/*
- Historical marker for if the engine is available of not
- */
- SHOW_COMP_OPTION state;
-
- /*
Historical number used for frm file to determine the correct
storage engine. This is going away and new engines will just use
"name" for this.
@@ -1468,6 +1484,12 @@ struct handlerton
void (*close_cursor_read_view)(handlerton *hton, THD *thd, void *read_view);
handler *(*create)(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root);
void (*drop_database)(handlerton *hton, char* path);
+ /*
+ return 0 if dropped successfully,
+ -1 if nothing was done by design (as in e.g. blackhole)
+ an error code (e.g. HA_ERR_NO_SUCH_TABLE) otherwise
+ */
+ int (*drop_table)(handlerton *hton, const char* path);
int (*panic)(handlerton *hton, enum ha_panic_function flag);
int (*start_consistent_snapshot)(handlerton *hton, THD *thd);
bool (*flush_logs)(handlerton *hton);
@@ -1651,6 +1673,14 @@ struct handlerton
int (*discover_table_structure)(handlerton *hton, THD* thd,
TABLE_SHARE *share, HA_CREATE_INFO *info);
+ /*
+ Notify the storage engine that the definition of the table (and the .frm
+ file) has changed. Returns 0 if ok.
+ */
+ int (*notify_tabledef_changed)(handlerton *hton, LEX_CSTRING *db,
+ LEX_CSTRING *table_name, LEX_CUSTRING *frm,
+ LEX_CUSTRING *org_tabledef_version);
+
/*
System Versioning
*/
@@ -1664,6 +1694,17 @@ struct handlerton
/* backup */
void (*prepare_for_backup)(void);
void (*end_backup)(void);
+
+ /* Server shutdown early notification.*/
+ void (*pre_shutdown)(void);
+
+ /*
+ Inform handler that partitioning engine has changed the .frm and the .par
+ files
+ */
+ int (*create_partitioning_metadata)(const char *path,
+ const char *old_path,
+ chf_create_flags action_flag);
};
@@ -1723,6 +1764,38 @@ handlerton *ha_default_tmp_handlerton(THD *thd);
/* can be replicated by wsrep replication provider plugin */
#define HTON_WSREP_REPLICATION (1 << 13)
+/*
+ Set this on the *slave* that's connected to a shared with a master storage.
+ The slave will ignore any CREATE TABLE, DROP or updates for this engine.
+*/
+#define HTON_IGNORE_UPDATES (1 << 14)
+
+/*
+ Set this on the *master* that's connected to a shared with a slave storage.
+ The table may not exists on the slave. The effects of having this flag are:
+ - ALTER TABLE that changes engine from this table to another engine will
+ be replicated as CREATE + INSERT
+ - CREATE ... LIKE shared_table will be replicated as a full CREATE TABLE
+ - ALTER TABLE for this engine will have "IF EXISTS" added.
+ - RENAME TABLE for this engine will have "IF EXISTS" added.
+ - DROP TABLE for this engine will have "IF EXISTS" added.
+*/
+#define HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE (1 << 15)
+
+/*
+ True if handler cannot rollback transactions. If not true, the transaction
+ will be put in the transactional binlog cache.
+ For some engines, like Aria, the rollback can happen in case of crash, but
+ not trough a handler rollback call.
+*/
+#define HTON_NO_ROLLBACK (1 << 16)
+
+/*
+ This storage engine can support both transactional and non transactional
+ tables
+*/
+#define HTON_TRANSACTIONAL_AND_NON_TRANSACTIONAL (1 << 17)
+
class Ha_trx_info;
struct THD_TRANS
@@ -1955,11 +2028,13 @@ enum enum_stats_auto_recalc { HA_STATS_AUTO_RECALC_DEFAULT= 0,
It stores the "schema_specification" part of the CREATE/ALTER statements and
is passed to mysql_create_db() and mysql_alter_db().
- Currently consists only of the schema default character set and collation.
+ Currently consists of the schema default character set, collation
+ and schema_comment.
*/
struct Schema_specification_st
{
CHARSET_INFO *default_table_charset;
+ LEX_CSTRING *schema_comment;
void init()
{
bzero(this, sizeof(*this));
@@ -1968,22 +2043,17 @@ struct Schema_specification_st
class Create_field;
-enum vers_sys_type_t
-{
- VERS_UNDEFINED= 0,
- VERS_TIMESTAMP,
- VERS_TRX_ID
-};
-
struct Table_period_info: Sql_alloc
{
Table_period_info() :
create_if_not_exists(false),
- constr(NULL) {}
+ constr(NULL),
+ unique_keys(0) {}
Table_period_info(const char *name_arg, size_t size) :
name(name_arg, size),
create_if_not_exists(false),
- constr(NULL) {}
+ constr(NULL),
+ unique_keys(0){}
Lex_ident name;
@@ -1999,6 +2069,7 @@ struct Table_period_info: Sql_alloc
start_end_t period;
bool create_if_not_exists;
Virtual_column_info *constr;
+ uint unique_keys;
bool is_set() const
{
@@ -2057,8 +2128,7 @@ public:
bool fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_info,
TABLE_LIST &src_table, TABLE_LIST &table);
bool check_sys_fields(const Lex_table_name &table_name,
- const Lex_table_name &db, Alter_info *alter_info,
- bool can_native) const;
+ const Lex_table_name &db, Alter_info *alter_info) const;
/**
At least one field was specified 'WITH/WITHOUT SYSTEM VERSIONING'.
@@ -2201,10 +2271,14 @@ struct Table_scope_and_contents_source_st:
struct HA_CREATE_INFO: public Table_scope_and_contents_source_st,
public Schema_specification_st
{
+ /* TODO: remove after MDEV-20865 */
+ Alter_info *alter_info;
+
void init()
{
Table_scope_and_contents_source_st::init();
Schema_specification_st::init();
+ alter_info= NULL;
}
bool check_conflicting_charset_declarations(CHARSET_INFO *cs);
bool add_table_option_default_charset(CHARSET_INFO *cs)
@@ -2667,22 +2741,22 @@ public:
double idx_cpu_cost; /* cost of operations in CPU for index */
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 };
+
+ static constexpr double IO_COEFF= 1;
+ static constexpr double CPU_COEFF= 1;
+ static constexpr double MEM_COEFF= 1;
+ static constexpr double IMPORT_COEFF= 1;
Cost_estimate()
{
reset();
}
- double total_cost()
+ double total_cost() const
{
return IO_COEFF*io_count*avg_io_cost +
IO_COEFF*idx_io_count*idx_avg_io_cost +
- CPU_COEFF*cpu_cost +
+ CPU_COEFF*(cpu_cost + idx_cpu_cost) +
MEM_COEFF*mem_cost + IMPORT_COEFF*import_cost;
}
@@ -2699,7 +2773,7 @@ public:
*/
bool is_zero() const
{
- return io_count == 0.0 && idx_io_count && cpu_cost == 0.0 &&
+ return io_count == 0.0 && idx_io_count == 0.0 && cpu_cost == 0.0 &&
import_cost == 0.0 && mem_cost == 0.0;
}
@@ -2722,7 +2796,7 @@ public:
void add(const Cost_estimate* cost)
{
- if (cost->io_count)
+ if (cost->io_count != 0.0)
{
double io_count_sum= io_count + cost->io_count;
avg_io_cost= (io_count * avg_io_cost +
@@ -2730,7 +2804,7 @@ public:
/io_count_sum;
io_count= io_count_sum;
}
- if (cost->idx_io_count)
+ if (cost->idx_io_count != 0.0)
{
double idx_io_count_sum= idx_io_count + cost->idx_io_count;
idx_avg_io_cost= (idx_io_count * idx_avg_io_cost +
@@ -2945,7 +3019,7 @@ public:
enum class Compare_keys : uint32_t
{
- Equal,
+ Equal= 0,
EqualButKeyPartLength,
EqualButComment,
NotEqual
@@ -3009,10 +3083,12 @@ protected:
Table_flags cached_table_flags; /* Set on init() and open() */
ha_rows estimation_rows_to_insert;
+ handler *lookup_handler;
public:
handlerton *ht; /* storage engine of this handler */
uchar *ref; /* Pointer to current row */
uchar *dup_ref; /* Pointer to duplicate row */
+ uchar *lookup_buffer;
ha_statistics stats;
@@ -3039,14 +3115,13 @@ public:
bool mark_trx_read_write_done; /* mark_trx_read_write was called */
bool check_table_binlog_row_based_done; /* check_table_binlog.. was called */
bool check_table_binlog_row_based_result; /* cached check_table_binlog... */
- /* Set to 1 if handler logged last insert/update/delete operation */
- bool row_already_logged;
/*
TRUE <=> the engine guarantees that returned records are within the range
being scanned.
*/
bool in_range_check_pushed_down;
+ uint lookup_errkey;
uint errkey; /* Last dup key */
uint key_used_on_scan;
uint active_index, keyread;
@@ -3125,13 +3200,67 @@ public:
*/
PSI_table *m_psi;
+private:
+ /** Internal state of the batch instrumentation. */
+ enum batch_mode_t
+ {
+ /** Batch mode not used. */
+ PSI_BATCH_MODE_NONE,
+ /** Batch mode used, before first table io. */
+ PSI_BATCH_MODE_STARTING,
+ /** Batch mode used, after first table io. */
+ PSI_BATCH_MODE_STARTED
+ };
+ /**
+ Batch mode state.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ batch_mode_t m_psi_batch_mode;
+ /**
+ The number of rows in the batch.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ ulonglong m_psi_numrows;
+ /**
+ The current event in a batch.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ PSI_table_locker *m_psi_locker;
+ /**
+ Storage for the event in a batch.
+ @sa start_psi_batch_mode.
+ @sa end_psi_batch_mode.
+ */
+ PSI_table_locker_state m_psi_locker_state;
+
+public:
virtual void unbind_psi();
virtual void rebind_psi();
+ /* Return error if definition doesn't match for already opened table */
+ virtual int discover_check_version() { return 0; }
- bool set_top_table_fields;
- struct TABLE *top_table;
- Field **top_table_field;
- uint top_table_fields;
+ /**
+ Put the handler in 'batch' mode when collecting
+ table io instrumented events.
+ When operating in batch mode:
+ - a single start event is generated in the performance schema.
+ - all table io performed between @c start_psi_batch_mode
+ and @c end_psi_batch_mode is not instrumented:
+ the number of rows affected is counted instead in @c m_psi_numrows.
+ - a single end event is generated in the performance schema
+ when the batch mode ends with @c end_psi_batch_mode.
+ */
+ void start_psi_batch_mode();
+ /** End a batch started with @c start_psi_batch_mode. */
+ void end_psi_batch_mode();
+
+ /* If we have row logging enabled for this table */
+ bool row_logging, row_logging_init;
+ /* If the row logging should be done in transaction cache */
+ bool row_logging_has_trans;
private:
/**
@@ -3150,18 +3279,17 @@ private:
/** Stores next_insert_id for handling duplicate key errors. */
ulonglong m_prev_insert_id;
-
public:
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
- estimation_rows_to_insert(0), ht(ht_arg),
- ref(0), end_range(NULL),
+ estimation_rows_to_insert(0),
+ lookup_handler(this),
+ ht(ht_arg), ref(0), lookup_buffer(NULL), end_range(NULL),
implicit_emptied(0),
mark_trx_read_write_done(0),
check_table_binlog_row_based_done(0),
check_table_binlog_row_based_result(0),
- row_already_logged(0),
- in_range_check_pushed_down(FALSE), errkey(-1),
+ in_range_check_pushed_down(FALSE), lookup_errkey(-1), errkey(-1),
key_used_on_scan(MAX_KEY),
active_index(MAX_KEY), keyread(MAX_KEY),
ref_length(sizeof(my_off_t)),
@@ -3173,8 +3301,11 @@ public:
pushed_rowid_filter(NULL),
rowid_filter_is_active(0),
auto_inc_intervals_count(0),
- m_psi(NULL), set_top_table_fields(FALSE), top_table(0),
- top_table_field(0), top_table_fields(0),
+ m_psi(NULL),
+ m_psi_batch_mode(PSI_BATCH_MODE_NONE),
+ m_psi_numrows(0),
+ m_psi_locker(NULL),
+ row_logging(0), row_logging_init(0),
m_lock_type(F_UNLCK), ha_share(NULL), m_prev_insert_id(0)
{
DBUG_PRINT("info",
@@ -3187,6 +3318,11 @@ public:
DBUG_ASSERT(m_lock_type == F_UNLCK);
DBUG_ASSERT(inited == NONE);
}
+ /* To check if table has been properely opened */
+ bool is_open()
+ {
+ return ref != 0;
+ }
virtual handler *clone(const char *name, MEM_ROOT *mem_root);
/** This is called after create to allow us to set up cached variables */
void init()
@@ -3272,6 +3408,7 @@ public:
and delete_row() below.
*/
int ha_external_lock(THD *thd, int lock_type);
+ int ha_external_unlock(THD *thd) { return ha_external_lock(thd, F_UNLCK); }
int ha_write_row(const uchar * buf);
int ha_update_row(const uchar * old_data, const uchar * new_data);
int ha_delete_row(const uchar * buf);
@@ -3318,13 +3455,12 @@ public:
int ha_enable_indexes(uint mode);
int ha_discard_or_import_tablespace(my_bool discard);
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_partitioning_metadata(const char *name, const char *old_name,
- int action_flag);
+ chf_create_flags action_flag);
int ha_change_partitions(HA_CREATE_INFO *create_info,
const char *path,
@@ -3382,19 +3518,28 @@ public:
reset_statistics();
}
virtual double scan_time()
- { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
+ {
+ return ((ulonglong2double(stats.data_file_length) / stats.block_size + 2) *
+ avg_io_cost());
+ }
virtual double key_scan_time(uint index)
{
return keyread_time(index, 1, records());
}
+ virtual double avg_io_cost()
+ {
+ return 1.0;
+ }
+
/**
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 ranges The number of ranges to be read. If 0, it means that
+ we calculate separately the cost of reading the key.
@param rows Total number of rows to be read.
This method can be used to calculate the total cost of scanning a table
@@ -3415,9 +3560,12 @@ public:
virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; }
/*
- True if changes to the table is persistent (no rollback)
- This is mainly used to decide how to log changes to the table in
- the binary log.
+ True if changes to the table is persistent (if there are no rollback)
+ This is used to decide:
+ - If the table is stored in the transaction or non transactional binary
+ log
+ - How things are tracked in trx and in add_changed_table().
+ - If we can combine several statements under one commit in the binary log.
*/
bool has_transactions()
{
@@ -3425,11 +3573,31 @@ public:
== 0);
}
/*
- True if the underlaying table doesn't support transactions
+ True if table has both transactions and rollback. This is used to decide
+ if we should write the changes to the binary log. If this is true,
+ we don't have to write failed statements to the log as they can be
+ rolled back.
+ */
+ bool has_transactions_and_rollback()
+ {
+ return has_transactions() && has_rollback();
+ }
+ /*
+ True if the underlaying table support transactions and rollback
*/
bool has_transaction_manager()
{
- return ((ha_table_flags() & HA_NO_TRANSACTIONS) == 0);
+ return ((ha_table_flags() & HA_NO_TRANSACTIONS) == 0 && has_rollback());
+ }
+
+ /*
+ True if table has rollback. Used to check if an update on the table
+ can be killed fast.
+ */
+
+ bool has_rollback()
+ {
+ return ((ht->flags & HTON_NO_ROLLBACK) == 0);
}
/**
@@ -3785,8 +3953,9 @@ public:
virtual int rnd_same(uchar *buf, uint inx)
{ return HA_ERR_WRONG_COMMAND; }
- virtual ha_rows records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
+ virtual ha_rows records_in_range(uint inx, const key_range *min_key,
+ const key_range *max_key,
+ page_range *res)
{ return (ha_rows) 10; }
/*
If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, then it sets ref
@@ -3799,6 +3968,7 @@ public:
virtual void get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id);
virtual void set_partitions_to_open(List<String> *partition_names) {}
+ virtual bool check_if_updates_are_ignored(const char *op) const;
virtual int change_partitions_to_open(List<String> *partition_names)
{ return 0; }
virtual int extra(enum ha_extra_function operation)
@@ -3950,6 +4120,7 @@ public:
virtual void set_part_info(partition_info *part_info) {return;}
virtual void return_record_by_parent() { return; }
+ /* Information about index. Both index and part starts from 0 */
virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
uint max_record_length() const
@@ -3978,11 +4149,6 @@ public:
void update_global_table_stats();
void update_global_index_stats();
-#define CHF_CREATE_FLAG 0
-#define CHF_DELETE_FLAG 1
-#define CHF_RENAME_FLAG 2
-#define CHF_INDEX_FLAG 3
-
/**
@note lock_count() can return > 1 if the table is MERGE or partitioned.
*/
@@ -4040,11 +4206,10 @@ public:
virtual my_bool register_query_cache_table(THD *thd, const char *table_key,
uint key_length,
- qc_engine_callback
- *engine_callback,
+ qc_engine_callback *callback,
ulonglong *engine_data)
{
- *engine_callback= 0;
+ *callback= 0;
return TRUE;
}
@@ -4090,30 +4255,52 @@ public:
}
/*
- Check if the primary key (if there is one) is a clustered and a
- reference key. This means:
+ Check if the key is a clustering key
- Data is stored together with the primary key (no secondary lookup
needed to find the row data). The optimizer uses this to find out
the cost of fetching data.
- - The primary key is part of each secondary key and is used
+
+ Note that in many cases a clustered key is also a reference key.
+ This means that:
+
+ - The key is part of each secondary key and is used
to find the row data in the primary index when reading trough
secondary indexes.
- When doing a HA_KEYREAD_ONLY we get also all the primary key parts
into the row. This is critical property used by index_merge.
All the above is usually true for engines that store the row
- data in the primary key index (e.g. in a b-tree), and use the primary
+ data in the primary key index (e.g. in a b-tree), and use the key
key value as a position(). InnoDB is an example of such an engine.
- For such a clustered primary key, the following should also hold:
+ For a clustered (primary) key, the following should also hold:
index_flags() should contain HA_CLUSTERED_INDEX
table_flags() should contain HA_TABLE_SCAN_ON_INDEX
+ For a reference key the following should also hold:
+ table_flags() should contain HA_PRIMARY_KEY_IS_READ_INDEX.
+
@retval TRUE yes
@retval FALSE No.
*/
- virtual bool primary_key_is_clustered() { return FALSE; }
+
+ /* The following code is for primary keys */
+ bool pk_is_clustering_key(uint index) const
+ {
+ /*
+ We have to check for MAX_INDEX as table->s->primary_key can be
+ MAX_KEY in the case where there is no primary key.
+ */
+ return index != MAX_KEY && is_clustering_key(index);
+ }
+ /* Same as before but for other keys, in which case we can skip the check */
+ bool is_clustering_key(uint index) const
+ {
+ DBUG_ASSERT(index != MAX_KEY);
+ return (index_flags(index, 0, 1) & HA_CLUSTERED_INDEX);
+ }
+
virtual int cmp_ref(const uchar *ref1, const uchar *ref2)
{
return memcmp(ref1, ref2, ref_length);
@@ -4159,36 +4346,6 @@ public:
virtual int info_push(uint info_type, void *info) { return 0; };
/**
- This function is used to get correlating of a parent (table/column)
- and children (table/column). When conditions are pushed down to child
- table (like child of myisam_merge), child table needs to know about
- which table/column is my parent for understanding conditions.
- */
- virtual int set_top_table_and_fields(TABLE *top_table,
- Field **top_table_field,
- uint top_table_fields)
- {
- if (!set_top_table_fields)
- {
- set_top_table_fields= TRUE;
- this->top_table= top_table;
- this->top_table_field= top_table_field;
- this->top_table_fields= top_table_fields;
- }
- return 0;
- }
- virtual void clear_top_table_fields()
- {
- if (set_top_table_fields)
- {
- set_top_table_fields= FALSE;
- top_table= NULL;
- top_table_field= NULL;
- top_table_fields= 0;
- }
- }
-
- /**
Push down an index condition to the handler.
The server will use this method to push down a condition it wants
@@ -4313,7 +4470,7 @@ public:
*) 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.
+ hton::notify_table_changed()
*) Destroy the Alter_inplace_info and handler_ctx objects.
*/
@@ -4380,16 +4537,6 @@ public:
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
@@ -4488,14 +4635,6 @@ protected:
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. */
@@ -4519,7 +4658,6 @@ public:
TABLE_SHARE* get_table_share() { return table_share; }
protected:
/* Service methods for use by storage engines. */
- void **ha_data(THD *) const;
THD *ha_thd(void) const;
/**
@@ -4536,19 +4674,25 @@ protected:
provide useful functionality.
*/
virtual int rename_table(const char *from, const char *to);
+
+
+public:
/**
Delete a table in the engine. Called for base as well as temporary
tables.
*/
virtual int delete_table(const char *name);
-
-public:
- bool check_table_binlog_row_based(bool binlog_row);
+ bool check_table_binlog_row_based();
+ bool prepare_for_row_logging();
+ int prepare_for_insert(bool do_create);
+ int binlog_log_row(TABLE *table,
+ const uchar *before_record,
+ const uchar *after_record,
+ Log_func *log_func);
inline void clear_cached_table_binlog_row_based_flag()
{
check_table_binlog_row_based_done= 0;
- check_table_binlog_row_based_result= 0;
}
private:
/* Cache result to avoid extra calls */
@@ -4563,7 +4707,15 @@ private:
private:
void mark_trx_read_write_internal();
- bool check_table_binlog_row_based_internal(bool binlog_row);
+ bool check_table_binlog_row_based_internal();
+
+ int create_lookup_handler();
+ void alloc_lookup_buffer();
+ int check_duplicate_long_entries(const uchar *new_rec);
+ int check_duplicate_long_entries_update(const uchar *new_rec);
+ int check_duplicate_long_entry_key(const uchar *new_rec, uint key_no);
+ /** PRIMARY KEY/UNIQUE WITHOUT OVERLAPS check */
+ int ha_check_overlaps(const uchar *old_data, const uchar* new_data);
protected:
/*
@@ -4718,9 +4870,9 @@ private:
DBUG_ASSERT(!(ha_table_flags() & HA_CAN_REPAIR));
return HA_ADMIN_NOT_IMPLEMENTED;
}
+protected:
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; }
@@ -4800,8 +4952,9 @@ public:
virtual void drop_table(const char *name);
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
- virtual int create_partitioning_metadata(const char *name, const char *old_name,
- int action_flag)
+ virtual int create_partitioning_metadata(const char *name,
+ const char *old_name,
+ chf_create_flags action_flag)
{ return FALSE; }
virtual int change_partitions(HA_CREATE_INFO *create_info,
@@ -4824,6 +4977,7 @@ public:
ha_share= arg_ha_share;
return false;
}
+ void set_table(TABLE* table_arg) { table= table_arg; }
int get_lock_type() const { return m_lock_type; }
public:
/* XXX to be removed, see ha_partition::partition_ht() */
@@ -4834,7 +4988,6 @@ public:
inline int ha_update_tmp_row(const uchar * old_data, uchar * new_data);
virtual void set_lock_type(enum thr_lock_type lock);
-
friend check_result_t handler_index_cond_check(void* h_arg);
friend check_result_t handler_rowid_filter_check(void *h_arg);
@@ -4858,8 +5011,6 @@ public:
virtual void update_partition(uint part_id)
{}
- virtual bool is_clustering_key(uint index) { return false; }
-
/**
Some engines can perform column type conversion with ALGORITHM=INPLACE.
These functions check for such possibility.
@@ -4880,11 +5031,8 @@ public:
{
return false;
}
- virtual bool can_convert_geom(const Field_geom *field,
- const Column_definition &new_type) const
- {
- return false;
- }
+ /* If the table is using sql level unique constraints on some column */
+ inline bool has_long_unique();
/* Used for ALTER TABLE.
Some engines can handle some differences in indexes by themself. */
@@ -4945,8 +5093,7 @@ static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint3
static inline bool ha_storage_engine_is_enabled(const handlerton *db_type)
{
- return (db_type && db_type->create) ?
- (db_type->state == SHOW_OPTION_YES) : FALSE;
+ return db_type && db_type->create;
}
#define view_pseudo_hton ((handlerton *)1)
@@ -4962,7 +5109,7 @@ TYPELIB *ha_known_exts(void);
int ha_panic(enum ha_panic_function flag);
void ha_close_connection(THD* thd);
void ha_kill_query(THD* thd, enum thd_kill_levels level);
-bool ha_flush_logs(handlerton *db_type);
+bool ha_flush_logs();
void ha_drop_database(char* path);
void ha_checkpoint_state(bool disable);
void ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *));
@@ -4970,9 +5117,14 @@ int ha_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
HA_CREATE_INFO *create_info, LEX_CUSTRING *frm);
int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
- const LEX_CSTRING *db, const LEX_CSTRING *alias, bool generate_warning);
+ const LEX_CSTRING *db, const LEX_CSTRING *alias,
+ bool generate_warning);
+int ha_delete_table_force(THD *thd, const char *path, const LEX_CSTRING *db,
+ const LEX_CSTRING *alias);
+
void ha_prepare_for_backup();
void ha_end_backup();
+void ha_pre_shutdown();
/* statistics and info */
bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
@@ -5004,15 +5156,18 @@ public:
INFORMATION_SCHEMA.TABLES without ORDER BY.
*/
void sort_desc();
-#endif
+#endif /* DBUG_OFF */
};
int ha_discover_table(THD *thd, TABLE_SHARE *share);
int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp,
Discovered_table_list *result, bool reusable);
-bool ha_table_exists(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name,
+bool ha_table_exists(THD *thd, const LEX_CSTRING *db,
+ const LEX_CSTRING *table_name,
handlerton **hton= 0, bool *is_sequence= 0);
-#endif
+bool ha_check_if_updates_are_ignored(THD *thd, handlerton *hton,
+ const char *op);
+#endif /* MYSQL_SERVER */
/* key cache */
extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *);
@@ -5043,7 +5198,8 @@ int ha_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal);
#endif
/* these are called by storage engines */
-void trans_register_ha(THD *thd, bool all, handlerton *ht);
+void trans_register_ha(THD *thd, bool all, handlerton *ht,
+ ulonglong trxid);
/*
Storage engine has to assume the transaction will end up with 2pc if
@@ -5068,38 +5224,91 @@ int binlog_log_row(TABLE* table,
const uchar *after_record,
Log_func *log_func);
-#define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \
+/**
+ @def MYSQL_TABLE_IO_WAIT
+ Instrumentation helper for table io_waits.
+ Note that this helper is intended to be used from
+ within the handler class only, as it uses members
+ from @c handler
+ Performance schema events are instrumented as follows:
+ - in non batch mode, one event is generated per call
+ - in batch mode, the number of rows affected is saved
+ in @c m_psi_numrows, so that @c end_psi_batch_mode()
+ generates a single event for the batch.
+ @param OP the table operation to be performed
+ @param INDEX the table index used if any, or MAX_KEY.
+ @param PAYLOAD instrumented code to execute
+ @sa handler::end_psi_batch_mode.
+*/
+#ifdef HAVE_PSI_TABLE_INTERFACE
+ #define MYSQL_TABLE_IO_WAIT(OP, INDEX, RESULT, PAYLOAD) \
+ { \
+ if (m_psi != NULL) \
+ { \
+ switch (m_psi_batch_mode) \
+ { \
+ case PSI_BATCH_MODE_NONE: \
+ { \
+ PSI_table_locker *sub_locker= NULL; \
+ PSI_table_locker_state reentrant_safe_state; \
+ sub_locker= PSI_TABLE_CALL(start_table_io_wait) \
+ (& reentrant_safe_state, m_psi, OP, INDEX, \
+ __FILE__, __LINE__); \
+ PAYLOAD \
+ if (sub_locker != NULL) \
+ PSI_TABLE_CALL(end_table_io_wait) \
+ (sub_locker, 1); \
+ break; \
+ } \
+ case PSI_BATCH_MODE_STARTING: \
+ { \
+ m_psi_locker= PSI_TABLE_CALL(start_table_io_wait) \
+ (& m_psi_locker_state, m_psi, OP, INDEX, \
+ __FILE__, __LINE__); \
+ PAYLOAD \
+ if (!RESULT) \
+ m_psi_numrows++; \
+ m_psi_batch_mode= PSI_BATCH_MODE_STARTED; \
+ break; \
+ } \
+ case PSI_BATCH_MODE_STARTED: \
+ default: \
+ { \
+ DBUG_ASSERT(m_psi_batch_mode \
+ == PSI_BATCH_MODE_STARTED); \
+ PAYLOAD \
+ if (!RESULT) \
+ m_psi_numrows++; \
+ break; \
+ } \
+ } \
+ } \
+ else \
+ { \
+ PAYLOAD \
+ } \
+ }
+#else
+ #define MYSQL_TABLE_IO_WAIT(OP, INDEX, RESULT, PAYLOAD) \
+ PAYLOAD
+#endif
+
+#define TABLE_IO_WAIT(TRACKER, OP, INDEX, RESULT, PAYLOAD) \
{ \
Exec_time_tracker *this_tracker; \
if (unlikely((this_tracker= tracker))) \
- tracker->start_tracking(); \
+ tracker->start_tracking(table->in_use); \
\
- MYSQL_TABLE_IO_WAIT(PSI, OP, INDEX, FLAGS, PAYLOAD); \
+ MYSQL_TABLE_IO_WAIT(OP, INDEX, RESULT, PAYLOAD); \
\
if (unlikely(this_tracker)) \
- tracker->stop_tracking(); \
+ tracker->stop_tracking(table->in_use); \
}
-
void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag);
void print_keydup_error(TABLE *table, KEY *key, myf errflag);
int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info);
int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table);
-#ifndef DBUG_OFF
-/** Converts XID to string.
-
-@param[out] buf output buffer
-@param[in] xid XID to convert
-
-@return pointer to converted string
-
-@note This does not need to be multi-byte safe or anything */
-char *xid_to_str(char *buf, const XID &xid);
-#endif // !DBUG_OFF
-
-#if defined(WITH_ARIA_STORAGE_ENGINE) && MYSQL_VERSION_ID < 100500
-extern void ha_maria_implicit_commit(THD *thd, bool new_trans);
-#else
-#define ha_maria_implicit_commit(A, B) while(0)
-#endif
+uint ha_count_rw_all(THD *thd, Ha_trx_info **ptr_ha_info);
+bool non_existing_table_error(int error);
#endif /* HANDLER_INCLUDED */
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
index d815c428ac6..ac84e5ccb7b 100644
--- a/sql/hash_filo.h
+++ b/sql/hash_filo.h
@@ -48,6 +48,7 @@ private:
class hash_filo
{
private:
+ PSI_memory_key m_psi_key;
const uint key_offset, key_length;
const my_hash_get_key get_key;
/** Size of this hash table. */
@@ -61,15 +62,13 @@ public:
mysql_mutex_t lock;
HASH cache;
- 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)
- :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)
+ hash_filo(PSI_memory_key psi_key, 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)
+ : m_psi_key(psi_key), 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));
}
@@ -95,8 +94,8 @@ public:
first_link= NULL;
last_link= NULL;
(void) my_hash_free(&cache);
- (void) my_hash_init(&cache,hash_charset,m_size,key_offset,
- key_length, get_key, free_element,0);
+ (void) my_hash_init(m_psi_key, &cache,hash_charset,m_size,key_offset,
+ key_length, get_key, free_element, 0);
if (!locked)
mysql_mutex_unlock(&lock);
}
@@ -202,10 +201,10 @@ public:
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,
+ Hash_filo(PSI_memory_key psi_key, 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(psi_key, 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(); }
diff --git a/sql/hostname.cc b/sql/hostname.cc
index 968914fd56e..edf31c11081 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -150,10 +150,9 @@ bool hostname_cache_init()
Host_entry tmp;
uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
- 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)))
+ if (!(hostname_cache= new Hash_filo<Host_entry>(key_memory_host_cache_hostname,
+ host_cache_size, key_offset, HOST_ENTRY_KEY_SIZE,
+ NULL, (my_hash_free_key) free, &my_charset_bin)))
return 1;
hostname_cache->clear();
@@ -476,7 +475,8 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
if (entry->m_host_validated)
{
if (entry->m_hostname_length)
- *hostname= my_strdup(entry->m_hostname, MYF(0));
+ *hostname= my_strdup(key_memory_host_cache_hostname,
+ entry->m_hostname, MYF(0));
DBUG_PRINT("info",("IP (%s) has been found in the cache. "
"Hostname: '%s'",
@@ -926,7 +926,8 @@ int ip_to_hostname(struct sockaddr_storage *ip_storage,
{
/* Copy host name string to be stored in the cache. */
- *hostname= my_strdup(hostname_buffer, MYF(0));
+ *hostname= my_strdup(key_memory_host_cache_hostname,
+ hostname_buffer, MYF(0));
if (!*hostname)
{
diff --git a/sql/item.cc b/sql/item.cc
index 26e99fe752e..52274380cd1 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -102,9 +102,7 @@ void item_init(void)
void Item::raise_error_not_evaluable()
{
Item::Print tmp(this, QT_ORDINARY);
- // TODO-10.5: add an error message to errmsg-utf8.txt
- my_printf_error(ER_UNKNOWN_ERROR,
- "'%s' is not allowed in this context", MYF(0), tmp.ptr());
+ my_error(ER_NOT_ALLOWED_IN_THIS_CONTEXT, MYF(0), tmp.ptr());
}
@@ -338,6 +336,7 @@ my_decimal *Item::val_decimal_from_real(my_decimal *decimal_value)
my_decimal *Item::val_decimal_from_int(my_decimal *decimal_value)
{
+ DBUG_ASSERT(is_fixed());
longlong nr= val_int();
if (null_value)
return 0;
@@ -411,7 +410,7 @@ int Item::save_str_value_in_field(Field *field, String *result)
Item::Item(THD *thd):
is_expensive_cache(-1), rsize(0), name(null_clex_str), orig_name(0),
- is_autogenerated_name(TRUE)
+ common_flags(IS_AUTO_GENERATED_NAME)
{
DBUG_ASSERT(thd);
marker= 0;
@@ -457,7 +456,7 @@ const TABLE_SHARE *Item::field_table_or_null()
tables.
*/
Item::Item(THD *thd, Item *item):
- Type_all_attributes(item),
+ Type_all_attributes(*item),
join_tab_idx(item->join_tab_idx),
is_expensive_cache(-1),
rsize(0),
@@ -471,7 +470,7 @@ Item::Item(THD *thd, Item *item):
with_param(item->with_param),
with_window_func(item->with_window_func),
with_field(item->with_field),
- is_autogenerated_name(item->is_autogenerated_name)
+ common_flags(item->common_flags)
{
next= thd->free_list; // Put in free list
thd->free_list= this;
@@ -632,33 +631,34 @@ Item* Item::set_expr_cache(THD *thd)
Item_ident::Item_ident(THD *thd, Name_resolution_context *context_arg,
- const char *db_name_arg,const char *table_name_arg,
- const LEX_CSTRING *field_name_arg)
+ const LEX_CSTRING &db_name_arg,
+ const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg)
:Item_result_field(thd), orig_db_name(db_name_arg),
orig_table_name(table_name_arg),
- orig_field_name(*field_name_arg), context(context_arg),
+ orig_field_name(field_name_arg), context(context_arg),
db_name(db_name_arg), table_name(table_name_arg),
- field_name(*field_name_arg),
+ field_name(field_name_arg),
alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX),
cached_table(0), depended_from(0), can_be_depended(TRUE)
{
- name= *field_name_arg;
+ name= field_name_arg;
}
Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg,
- const LEX_CSTRING *field_name_arg)
- :Item_result_field(thd), orig_db_name(NullS),
- orig_table_name(view_arg->table_name.str),
- orig_field_name(*field_name_arg),
+ const LEX_CSTRING &field_name_arg)
+ :Item_result_field(thd), orig_db_name(null_clex_str),
+ orig_table_name(view_arg->table_name),
+ orig_field_name(field_name_arg),
/* TODO: suspicious use of first_select_lex */
context(&view_arg->view->first_select_lex()->context),
- db_name(NullS), table_name(view_arg->alias.str),
- field_name(*field_name_arg),
+ db_name(null_clex_str), table_name(view_arg->alias),
+ field_name(field_name_arg),
alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX),
cached_table(NULL), depended_from(NULL), can_be_depended(TRUE)
{
- name= *field_name_arg;
+ name= field_name_arg;
}
@@ -764,6 +764,29 @@ bool Item_field::collect_item_field_processor(void *arg)
}
+void Item_ident::undeclared_spvar_error() const
+{
+ /*
+ We assume this is an unknown SP variable, possibly a ROW variable.
+ Print the leftmost name in the error:
+ SET var=a; -> a
+ SET var=a.b; -> a
+ SET var=a.b.c; -> a
+ */
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), db_name.str ? db_name.str :
+ table_name.str ? table_name.str :
+ field_name.str);
+}
+
+bool Item_field::unknown_splocal_processor(void *arg)
+{
+ DBUG_ENTER("Item_field::unknown_splocal_processor");
+ DBUG_ASSERT(type() == FIELD_ITEM);
+ undeclared_spvar_error();
+ DBUG_RETURN(true);
+}
+
+
bool Item_field::add_field_to_set_processor(void *arg)
{
DBUG_ENTER("Item_field::add_field_to_set_processor");
@@ -789,10 +812,10 @@ bool Item_field::rename_fields_processor(void *arg)
while ((def=def_it++))
{
if (def->change.str &&
- (!db_name || !db_name[0] ||
- !my_strcasecmp(table_alias_charset, db_name, rename->db_name.str)) &&
- (!table_name || !table_name[0] ||
- !my_strcasecmp(table_alias_charset, table_name, rename->table_name.str)) &&
+ (!db_name.str || !db_name.str[0] ||
+ !my_strcasecmp(table_alias_charset, db_name.str, rename->db_name.str)) &&
+ (!table_name.str || !table_name.str[0] ||
+ !my_strcasecmp(table_alias_charset, table_name.str, rename->table_name.str)) &&
!my_strcasecmp(system_charset_info, field_name.str, def->change.str))
{
field_name= def->field_name;
@@ -986,7 +1009,7 @@ bool Item::check_type_general_purpose_string(const char *opname) const
bool Item::check_type_traditional_scalar(const char *opname) const
{
const Type_handler *handler= type_handler();
- if (handler->is_traditional_type() && handler->is_scalar_type())
+ if (handler->is_traditional_scalar_type())
return false;
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
handler->name().ptr(), opname);
@@ -1107,9 +1130,9 @@ void Item::set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs)
}
const char *str_start= str;
- if (!cs->ctype || cs->mbminlen > 1)
+ if (!cs->m_ctype || cs->mbminlen > 1)
{
- str+= cs->cset->scan(cs, str, str + length, MY_SEQ_SPACES);
+ str+= cs->scan(str, str + length, MY_SEQ_SPACES);
length-= (uint)(str - str_start);
}
else
@@ -1124,7 +1147,7 @@ void Item::set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs)
str++;
}
}
- if (str != str_start && !is_autogenerated_name)
+ if (str != str_start && !is_autogenerated_name())
{
char buff[SAFE_NAME_LEN];
@@ -1300,7 +1323,7 @@ Item *Item::const_charset_converter(THD *thd, CHARSET_INFO *tocs,
uint conv_errors;
Item_string *conv= (func_name ?
new (mem_root)
- Item_static_string_func(thd, func_name,
+ Item_static_string_func(thd, Lex_cstring_strlen(func_name),
s, tocs, &conv_errors,
collation.derivation,
collation.repertoire) :
@@ -1371,7 +1394,7 @@ bool Item::get_date_from_real(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate
bool Item::get_date_from_string(THD *thd, MYSQL_TIME *to, date_mode_t mode)
{
- StringBuffer<40> tmp;
+ StringBuffer<MAX_DATETIME_FULL_WIDTH+1> tmp;
const TABLE_SHARE *s = field_table_or_null();
Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
s ? s->table_name.str : nullptr,
@@ -1421,7 +1444,7 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
THD *thd= table->in_use;
enum_check_fields tmp= thd->count_cuted_fields;
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
- sql_mode_t sql_mode= thd->variables.sql_mode;
+ Sql_mode_save sms(thd);
thd->variables.sql_mode&= ~(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
thd->variables.sql_mode|= MODE_INVALID_DATES;
thd->count_cuted_fields= CHECK_FIELD_IGNORE;
@@ -1430,7 +1453,6 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions)
thd->count_cuted_fields= tmp;
dbug_tmp_restore_column_map(table->write_set, old_map);
- thd->variables.sql_mode= sql_mode;
return res;
}
@@ -1473,6 +1495,37 @@ bool mark_unsupported_function(const char *w1, const char *w2,
}
+bool Item_field::check_vcol_func_processor(void *arg)
+{
+ context= 0;
+ vcol_func_processor_result *res= (vcol_func_processor_result *) arg;
+ if (res && res->alter_info)
+ {
+ for (Key &k: res->alter_info->key_list)
+ {
+ if (k.type != Key::FOREIGN_KEY)
+ continue;
+ Foreign_key *fk= (Foreign_key*) &k;
+ if (fk->update_opt != FK_OPTION_CASCADE)
+ continue;
+ for (Key_part_spec& kp: fk->columns)
+ {
+ if (!lex_string_cmp(system_charset_info, &kp.field_name, &field_name))
+ {
+ return mark_unsupported_function(field_name.str, arg, VCOL_IMPOSSIBLE);
+ }
+ }
+ }
+ }
+ if (field && (field->unireg_check == Field::NEXT_NUMBER))
+ {
+ // Auto increment fields are unsupported
+ return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
+ }
+ return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF);
+}
+
+
Query_fragment::Query_fragment(THD *thd, sp_head *sphead,
const char *start, const char *end)
{
@@ -2030,7 +2083,7 @@ Item_name_const::Item_name_const(THD *thd, Item *name_arg, Item *val):
Item::maybe_null= TRUE;
if (name_item->basic_const_item() &&
(name_str= name_item->val_str(&name_buffer))) // Can't have a NULL name
- set_name(thd, name_str->ptr(), name_str->length(), name_str->charset());
+ set_name(thd, name_str);
}
@@ -2075,7 +2128,7 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref)
return TRUE;
}
if (value_item->collation.derivation == DERIVATION_NUMERIC)
- collation.set_numeric();
+ collation= DTCollation_numeric();
else
collation.set(value_item->collation.collation, DERIVATION_IMPLICIT);
max_length= value_item->max_length;
@@ -2105,18 +2158,18 @@ class Item_aggregate_ref : public Item_ref
{
public:
Item_aggregate_ref(THD *thd, Name_resolution_context *context_arg,
- Item **item, const char *table_name_arg,
- const LEX_CSTRING *field_name_arg):
+ Item **item, const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg):
Item_ref(thd, context_arg, item, table_name_arg, field_name_arg) {}
- virtual inline void print (String *str, enum_query_type query_type)
+ void print (String *str, enum_query_type query_type) override
{
if (ref)
(*ref)->print(str, query_type);
else
Item_ident::print(str, query_type);
}
- virtual Ref_Type ref_type() { return AGGREGATE_REF; }
+ Ref_Type ref_type() override final { return AGGREGATE_REF; }
};
@@ -2228,8 +2281,8 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
if (!(item_ref= (new (thd->mem_root)
Item_direct_ref(thd,
&thd->lex->current_select->context,
- &ref_pointer_array[el], 0,
- &name))))
+ &ref_pointer_array[el],
+ null_clex_str, name))))
return; // fatal_error is set
}
else
@@ -2237,8 +2290,8 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array,
if (!(item_ref= (new (thd->mem_root)
Item_aggregate_ref(thd,
&thd->lex->current_select->context,
- &ref_pointer_array[el], 0,
- &name))))
+ &ref_pointer_array[el],
+ null_clex_str, name))))
return; // fatal_error is set
}
if (type() == SUM_FUNC_ITEM)
@@ -2384,7 +2437,7 @@ bool DTCollation::aggregate(const DTCollation &dt, uint flags)
{
if (derivation == DERIVATION_EXPLICIT)
{
- set(0, DERIVATION_NONE, 0);
+ set(0, DERIVATION_NONE, MY_REPERTOIRE_NONE);
return 1;
}
if (collation->state & MY_CS_BINSORT &&
@@ -2767,7 +2820,8 @@ Item_sp::execute_impl(THD *thd, Item **args, uint arg_count)
(m_sp->agg_type() == NOT_AGGREGATE && !func_ctx));
if (!func_ctx)
{
- init_sql_alloc(&sp_mem_root, "Item_sp", MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(key_memory_sp_head_call_root, &sp_mem_root,
+ MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
*sp_query_arena= Query_arena(&sp_mem_root,
Query_arena::STMT_INITIALIZED_FOR_SP);
}
@@ -2885,9 +2939,10 @@ Item* Item_ref::build_clone(THD *thd)
/**********************************************/
Item_field::Item_field(THD *thd, Field *f)
- :Item_ident(thd, 0, NullS, *f->table_name, &f->field_name),
+ :Item_ident(thd, 0, null_clex_str,
+ Lex_cstring_strlen(*f->table_name), f->field_name),
item_equal(0),
- have_privileges(0), any_privileges(0)
+ have_privileges(NO_ACL), any_privileges(0)
{
set_field(f);
/*
@@ -2909,10 +2964,10 @@ Item_field::Item_field(THD *thd, Field *f)
Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
Field *f)
- :Item_ident(thd, context_arg, f->table->s->db.str, *f->table_name,
- &f->field_name),
+ :Item_ident(thd, context_arg, f->table->s->db,
+ Lex_cstring_strlen(*f->table_name), f->field_name),
item_equal(0),
- have_privileges(0), any_privileges(0)
+ have_privileges(NO_ACL), any_privileges(0)
{
/*
We always need to provide Item_field with a fully qualified field
@@ -2932,13 +2987,12 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
procedures).
*/
{
- if (db_name)
- orig_db_name= thd->strdup(db_name);
- if (table_name)
- orig_table_name= thd->strdup(table_name);
+ if (db_name.str)
+ orig_db_name= thd->strmake_lex_cstring(db_name);
+ if (table_name.str)
+ orig_table_name= thd->strmake_lex_cstring(table_name);
if (field_name.str)
- thd->make_lex_string(&orig_field_name, field_name.str,
- field_name.length);
+ orig_field_name= thd->strmake_lex_cstring(field_name);
/*
We don't restore 'name' in cleanup because it's not changed
during execution. Still we need it to point to persistent
@@ -2952,11 +3006,12 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
Item_field::Item_field(THD *thd, Name_resolution_context *context_arg,
- const char *db_arg,const char *table_name_arg,
- const LEX_CSTRING *field_name_arg)
+ const LEX_CSTRING &db_arg,
+ const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg)
:Item_ident(thd, context_arg, db_arg, table_name_arg, field_name_arg),
field(0), item_equal(0),
- have_privileges(0), any_privileges(0)
+ have_privileges(NO_ACL), any_privileges(0)
{
SELECT_LEX *select= thd->lex->current_select;
collation.set(DERIVATION_IMPLICIT);
@@ -2981,14 +3036,25 @@ Item_field::Item_field(THD *thd, Item_field *item)
}
+bool Item_field::is_json_type()
+{
+ if (!field->check_constraint ||
+ field->check_constraint->expr->type() != FUNC_ITEM)
+ return FALSE;
+
+ Item_func *f= (Item_func *) field->check_constraint->expr;
+ return f->functype() == Item_func::JSON_VALID_FUNC;
+}
+
+
void Item_field::set_field(Field *field_par)
{
field=result_field=field_par; // for easy coding with fields
maybe_null=field->maybe_null();
Type_std_attributes::set(field_par->type_std_attributes());
- table_name= *field_par->table_name;
+ table_name= Lex_cstring_strlen(*field_par->table_name);
field_name= field_par->field_name;
- db_name= field_par->table->s->db.str;
+ db_name= field_par->table->s->db;
alias_name_used= field_par->table->alias_name_used;
fixed= 1;
@@ -3071,24 +3137,24 @@ bool Item_field::switch_to_nullable_fields_processor(void *arg)
const char *Item_ident::full_name() const
{
char *tmp;
- if (!table_name || !field_name.str)
+ if (!table_name.str || !field_name.str)
return field_name.str ? field_name.str : name.str ? name.str : "tmp_field";
- if (db_name && db_name[0])
+ if (db_name.str && db_name.str[0])
{
THD *thd= current_thd;
- tmp=(char*) thd->alloc((uint) strlen(db_name)+(uint) strlen(table_name)+
+ tmp=(char*) thd->alloc((uint) db_name.length+ (uint) table_name.length +
(uint) field_name.length+3);
- strxmov(tmp,db_name,".",table_name,".",field_name.str,NullS);
+ strxmov(tmp,db_name.str,".",table_name.str,".",field_name.str,NullS);
}
else
{
- if (table_name[0])
+ if (table_name.str[0])
{
THD *thd= current_thd;
- tmp= (char*) thd->alloc((uint) strlen(table_name) +
+ tmp= (char*) thd->alloc((uint) table_name.length +
field_name.length + 2);
- strxmov(tmp, table_name, ".", field_name.str, NullS);
+ strxmov(tmp, table_name.str, ".", field_name.str, NullS);
}
else
return field_name.str;
@@ -3100,12 +3166,14 @@ void Item_ident::print(String *str, enum_query_type query_type)
{
THD *thd= current_thd;
char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
- const char *d_name= db_name, *t_name= table_name;
- bool use_table_name= table_name && table_name[0];
- bool use_db_name= use_table_name && db_name && db_name[0] && !alias_name_used;
+ LEX_CSTRING d_name= db_name;
+ LEX_CSTRING t_name= table_name;
+ bool use_table_name= table_name.str && table_name.str[0];
+ bool use_db_name= use_table_name && db_name.str && db_name.str[0] &&
+ !alias_name_used;
if (use_db_name && (query_type & QT_ITEM_IDENT_SKIP_DB_NAMES))
- use_db_name= !thd->db.str || strcmp(thd->db.str, db_name);
+ use_db_name= !thd->db.str || strcmp(thd->db.str, db_name.str);
if (use_db_name)
use_db_name= !(cached_table && cached_table->belong_to_view &&
@@ -3128,6 +3196,12 @@ void Item_ident::print(String *str, enum_query_type query_type)
use_db_name= use_table_name= false;
}
+ if ((query_type & QT_ITEM_IDENT_DISABLE_DB_TABLE_NAMES))
+ {
+ // Don't print db or table name irrespective of any other settings.
+ use_db_name= use_table_name= false;
+ }
+
if (!field_name.str || !field_name.str[0])
{
append_identifier(thd, str, STRING_WITH_LEN("tmp_field"));
@@ -3139,27 +3213,27 @@ void Item_ident::print(String *str, enum_query_type query_type)
{
if (use_table_name)
{
- strmov(t_name_buff, table_name);
+ strmov(t_name_buff, table_name.str);
my_casedn_str(files_charset_info, t_name_buff);
- t_name= t_name_buff;
+ t_name= Lex_cstring_strlen(t_name_buff);
}
if (use_db_name)
{
- strmov(d_name_buff, db_name);
+ strmov(d_name_buff, db_name.str);
my_casedn_str(files_charset_info, d_name_buff);
- d_name= d_name_buff;
+ d_name= Lex_cstring_strlen(d_name_buff);
}
}
if (use_db_name)
{
- append_identifier(thd, str, d_name, (uint)strlen(d_name));
+ append_identifier(thd, str, d_name.str, (uint) d_name.length);
str->append('.');
DBUG_ASSERT(use_table_name);
}
if (use_table_name)
{
- append_identifier(thd, str, t_name, (uint) strlen(t_name));
+ append_identifier(thd, str, t_name.str, (uint) t_name.length);
str->append('.');
}
append_identifier(thd, str, &field_name);
@@ -3326,12 +3400,12 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const
*/
return (!lex_string_cmp(system_charset_info, &item_field->name,
&field_name) &&
- (!item_field->table_name || !table_name ||
- (!my_strcasecmp(table_alias_charset, item_field->table_name,
- table_name) &&
- (!item_field->db_name || !db_name ||
- (item_field->db_name && !strcmp(item_field->db_name,
- db_name))))));
+ (!item_field->table_name.str || !table_name.str ||
+ (!my_strcasecmp(table_alias_charset, item_field->table_name.str,
+ table_name.str) &&
+ (!item_field->db_name.str || !db_name.str ||
+ (item_field->db_name.str && !strcmp(item_field->db_name.str,
+ db_name.str))))));
}
@@ -3348,6 +3422,16 @@ table_map Item_field::all_used_tables() const
}
+bool Item_field::find_not_null_fields(table_map allowed)
+{
+ if (field->table->const_table)
+ return false;
+ if (!get_depended_from() && field->real_maybe_null())
+ bitmap_set_bit(&field->table->tmp_set, field->field_index);
+ return false;
+}
+
+
/*
@Note thd->fatal_error can be set in case of OOM
*/
@@ -3550,9 +3634,10 @@ String *Item_int::val_str(String *str)
void Item_int::print(String *str, enum_query_type query_type)
{
+ StringBuffer<LONGLONG_BUFFER_SIZE> buf;
// my_charset_bin is good enough for numbers
- str_value.set_int(value, unsigned_flag, &my_charset_bin);
- str->append(str_value);
+ buf.set_int(value, unsigned_flag, &my_charset_bin);
+ str->append(buf);
}
@@ -3578,21 +3663,6 @@ Item_uint::Item_uint(THD *thd, const char *str_arg, longlong i, uint length):
}
-String *Item_uint::val_str(String *str)
-{
- str->set((ulonglong) value, collation.collation);
- return str;
-}
-
-
-void Item_uint::print(String *str, enum_query_type query_type)
-{
- // latin1 is good enough for numbers
- str_value.set((ulonglong) value, default_charset());
- str->append(str_value);
-}
-
-
Item_decimal::Item_decimal(THD *thd, const char *str_arg, size_t length,
CHARSET_INFO *charset):
Item_num(thd)
@@ -3835,7 +3905,7 @@ Item_null::make_string_literal_concat(THD *thd, const LEX_CSTRING *str)
if (str->length)
{
CHARSET_INFO *cs= thd->variables.collation_connection;
- uint repertoire= my_string_repertoire(cs, str->str, str->length);
+ my_repertoire_t repertoire= my_string_repertoire(cs, str->str, str->length);
return new (thd->mem_root) Item_string(thd,
str->str, (uint) str->length, cs,
DERIVATION_COERCIBLE, repertoire);
@@ -3916,7 +3986,6 @@ void Item_param::sync_clones()
c->null_value= null_value;
c->Type_std_attributes::operator=(*this);
c->Type_handler_hybrid_field_type::operator=(*this);
- c->Type_geometry_attributes::operator=(*this);
c->state= state;
c->m_empty_string_is_null= m_empty_string_is_null;
@@ -3962,7 +4031,7 @@ void Item_param::set_int(longlong i, uint32 max_length_arg)
DBUG_ASSERT(value.type_handler()->cmp_type() == INT_RESULT);
value.integer= (longlong) i;
state= SHORT_DATA_VALUE;
- collation.set_numeric();
+ collation= DTCollation_numeric();
max_length= max_length_arg;
decimals= 0;
maybe_null= 0;
@@ -3976,7 +4045,7 @@ void Item_param::set_double(double d)
DBUG_ASSERT(value.type_handler()->cmp_type() == REAL_RESULT);
value.real= d;
state= SHORT_DATA_VALUE;
- collation.set_numeric();
+ collation= DTCollation_numeric();
max_length= DBL_DIG + 8;
decimals= NOT_FIXED_DEC;
maybe_null= 0;
@@ -4007,7 +4076,7 @@ void Item_param::set_decimal(const char *str, ulong length)
str2my_decimal(E_DEC_FATAL_ERROR, str, &value.m_decimal, &end);
state= SHORT_DATA_VALUE;
decimals= value.m_decimal.frac;
- collation.set_numeric();
+ collation= DTCollation_numeric();
max_length=
my_decimal_precision_to_length_no_truncation(value.m_decimal.precision(),
decimals, unsigned_flag);
@@ -4024,7 +4093,7 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg)
my_decimal2decimal(dv, &value.m_decimal);
decimals= (uint8) value.m_decimal.frac;
- collation.set_numeric();
+ collation= DTCollation_numeric();
unsigned_flag= unsigned_arg;
max_length= my_decimal_precision_to_length(value.m_decimal.intg + decimals,
decimals, unsigned_flag);
@@ -4036,7 +4105,7 @@ void Item_param::set_decimal(const my_decimal *dv, bool unsigned_arg)
void Item_param::fix_temporal(uint32 max_length_arg, uint decimals_arg)
{
state= SHORT_DATA_VALUE;
- collation.set_numeric();
+ collation= DTCollation_numeric();
max_length= max_length_arg;
decimals= decimals_arg;
maybe_null= 0;
@@ -4143,12 +4212,12 @@ bool Item_param::set_longdata(const char *str, ulong length)
(here), and first have to concatenate all pieces together,
write query to the binary log and only then perform conversion.
*/
- if (value.m_string.length() + length > max_long_data_size)
+ if (value.m_string.length() + length > current_thd->variables.max_allowed_packet)
{
my_message(ER_UNKNOWN_ERROR,
"Parameter of prepared statement which is set through "
"mysql_send_long_data() is longer than "
- "'max_long_data_size' bytes",
+ "'max_allowed_packet' bytes",
MYF(0));
DBUG_RETURN(true);
}
@@ -4619,9 +4688,9 @@ Item *Item_param::value_clone_item(THD *thd)
case DECIMAL_RESULT:
return 0; // Should create Item_decimal. See MDEV-11361.
case STRING_RESULT:
- return new (mem_root) Item_string(thd, name.str,
- value.m_string.c_ptr_quick(),
- value.m_string.length(),
+ return new (mem_root) Item_string(thd, name,
+ Lex_cstring(value.m_string.c_ptr_quick(),
+ value.m_string.length()),
value.m_string.charset(),
collation.derivation,
collation.repertoire);
@@ -4857,16 +4926,16 @@ double Item_copy_string::val_real()
int err_not_used;
char *end_not_used;
return (null_value ? 0.0 :
- my_strntod(str_value.charset(), (char*) str_value.ptr(),
- str_value.length(), &end_not_used, &err_not_used));
+ str_value.charset()->strntod((char*) str_value.ptr(), str_value.length(),
+ &end_not_used, &err_not_used));
}
longlong Item_copy_string::val_int()
{
int err;
- return null_value ? 0 : my_strntoll(str_value.charset(),str_value.ptr(),
- str_value.length(), 10, (char**) 0,
- &err);
+ return null_value ? 0 : str_value.charset()->strntoll(str_value.ptr(),
+ str_value.length(), 10, (char**) 0,
+ &err);
}
@@ -5002,10 +5071,10 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
DBUG_RETURN(TRUE);
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
- const char *db_name= (resolved_item->db_name ?
- resolved_item->db_name : "");
- const char *table_name= (resolved_item->table_name ?
- resolved_item->table_name : "");
+ const char *db_name= (resolved_item->db_name.str ?
+ resolved_item->db_name.str : "");
+ const char *table_name= (resolved_item->table_name.str ?
+ resolved_item->table_name.str : "");
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_FIELD_RESOLVED,
ER_THD(thd,ER_WARN_FIELD_RESOLVED),
@@ -5038,8 +5107,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
resolved identifier.
*/
-void mark_select_range_as_dependent(THD *thd,
- SELECT_LEX *last_select,
+void mark_select_range_as_dependent(THD *thd, SELECT_LEX *last_select,
SELECT_LEX *current_sel,
Field *found_field, Item *found_item,
Item_ident *resolved_item)
@@ -5051,34 +5119,33 @@ void mark_select_range_as_dependent(THD *thd,
resolving)
*/
SELECT_LEX *previous_select= current_sel;
- for (; previous_select->outer_select() != last_select;
- previous_select= previous_select->outer_select())
+ for (; previous_select->context.outer_select() != last_select;
+ previous_select= previous_select->context.outer_select())
{
Item_subselect *prev_subselect_item=
previous_select->master_unit()->item;
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
prev_subselect_item->const_item_cache= 0;
}
+
+ Item_subselect *prev_subselect_item=
+ previous_select->master_unit()->item;
+ Item_ident *dependent= resolved_item;
+ if (found_field == view_ref_found)
{
- Item_subselect *prev_subselect_item=
- previous_select->master_unit()->item;
- Item_ident *dependent= resolved_item;
- if (found_field == view_ref_found)
- {
- Item::Type type= found_item->type();
- prev_subselect_item->used_tables_cache|=
- found_item->used_tables();
- dependent= ((type == Item::REF_ITEM || type == Item::FIELD_ITEM) ?
- (Item_ident*) found_item :
- 0);
- }
- else
- prev_subselect_item->used_tables_cache|=
- found_field->table->map;
- prev_subselect_item->const_item_cache= 0;
- mark_as_dependent(thd, last_select, current_sel, resolved_item,
- dependent);
+ Item::Type type= found_item->type();
+ prev_subselect_item->used_tables_cache|=
+ found_item->used_tables();
+ dependent= ((type == Item::REF_ITEM || type == Item::FIELD_ITEM) ?
+ (Item_ident*) found_item :
+ 0);
}
+ else
+ prev_subselect_item->used_tables_cache|=
+ found_field->table->map;
+ prev_subselect_item->const_item_cache= 0;
+ mark_as_dependent(thd, last_select, current_sel, resolved_item,
+ dependent);
}
@@ -5099,9 +5166,9 @@ void mark_select_range_as_dependent(THD *thd,
static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
{
- const char *db_name;
- const char *table_name;
- LEX_CSTRING *field_name;
+ LEX_CSTRING db_name;
+ LEX_CSTRING table_name;
+ LEX_CSTRING field_name;
ORDER *found_group= NULL;
int found_match_degree= 0;
char name_buff[SAFE_NAME_LEN+1];
@@ -5111,30 +5178,30 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
{
db_name= ((Item_ident*) find_item)->db_name;
table_name= ((Item_ident*) find_item)->table_name;
- field_name= &((Item_ident*) find_item)->field_name;
+ field_name= ((Item_ident*) find_item)->field_name;
}
else
return NULL;
- if (db_name && lower_case_table_names)
+ if (db_name.str && lower_case_table_names)
{
/* Convert database to lower case for comparison */
- strmake_buf(name_buff, db_name);
+ strmake_buf(name_buff, db_name.str);
my_casedn_str(files_charset_info, name_buff);
- db_name= name_buff;
+ db_name= Lex_cstring_strlen(name_buff);
}
- DBUG_ASSERT(field_name->str != 0);
+ DBUG_ASSERT(field_name.str != 0);
for (ORDER *cur_group= group_list ; cur_group ; cur_group= cur_group->next)
{
int cur_match_degree= 0;
/* SELECT list element with explicit alias */
- if ((*(cur_group->item))->name.str && !table_name &&
- !(*(cur_group->item))->is_autogenerated_name &&
+ if ((*(cur_group->item))->name.str && !table_name.str &&
+ !(*(cur_group->item))->is_autogenerated_name() &&
!lex_string_cmp(system_charset_info,
- &(*(cur_group->item))->name, field_name))
+ &(*(cur_group->item))->name, &field_name))
{
++cur_match_degree;
}
@@ -5143,30 +5210,30 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
(*(cur_group->item))->type() == Item::REF_ITEM )
{
Item_ident *cur_field= (Item_ident*) *cur_group->item;
- const char *l_db_name= cur_field->db_name;
- const char *l_table_name= cur_field->table_name;
+ const char *l_db_name= cur_field->db_name.str;
+ const char *l_table_name= cur_field->table_name.str;
LEX_CSTRING *l_field_name= &cur_field->field_name;
DBUG_ASSERT(l_field_name->str != 0);
if (!lex_string_cmp(system_charset_info,
- l_field_name, field_name))
+ l_field_name, &field_name))
++cur_match_degree;
else
continue;
- if (l_table_name && table_name)
+ if (l_table_name && table_name.str)
{
/* If field_name is qualified by a table name. */
- if (my_strcasecmp(table_alias_charset, l_table_name, table_name))
+ if (my_strcasecmp(table_alias_charset, l_table_name, table_name.str))
/* Same field names, different tables. */
return NULL;
++cur_match_degree;
- if (l_db_name && db_name)
+ if (l_db_name && db_name.str)
{
/* If field_name is also qualified by a database name. */
- if (strcmp(l_db_name, db_name))
+ if (strcmp(l_db_name, db_name.str))
/* Same field names, different databases. */
return NULL;
++cur_match_degree;
@@ -5635,14 +5702,14 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
rf= (place == IN_HAVING ?
new (thd->mem_root)
Item_ref(thd, context, ref, table_name,
- &field_name, alias_name_used) :
+ field_name, alias_name_used) :
(!select->group_list.elements ?
new (thd->mem_root)
Item_direct_ref(thd, context, ref, table_name,
- &field_name, alias_name_used) :
+ field_name, alias_name_used) :
new (thd->mem_root)
Item_outer_ref(thd, context, ref, table_name,
- &field_name, alias_name_used)));
+ field_name, alias_name_used)));
*ref= save;
if (!rf)
return -1;
@@ -5687,9 +5754,9 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
{
Item_ref *rf;
rf= new (thd->mem_root) Item_ref(thd, context,
- (*from_field)->table->s->db.str,
- (*from_field)->table->alias.c_ptr(),
- &field_name);
+ (*from_field)->table->s->db,
+ Lex_cstring_strlen((*from_field)->table->alias.c_ptr()),
+ field_name);
if (!rf)
return -1;
thd->change_item_tree(reference, rf);
@@ -5837,7 +5904,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
Item_field created by the parser with the new Item_ref.
*/
Item_ref *rf= new (thd->mem_root)
- Item_ref(thd, context, db_name, table_name, &field_name);
+ Item_ref(thd, context, db_name, table_name, field_name);
if (!rf)
return 1;
bool err= rf->fix_fields(thd, (Item **) &rf) || rf->check_cols(1);
@@ -6020,7 +6087,7 @@ bool Item_field::post_fix_fields_part_expr_processor(void *int_arg)
/*
Update table_name to be real table name, not the alias. Because alias is
reallocated for every statement, and this item has a long life time */
- table_name= field->table->s->table_name.str;
+ table_name= field->table->s->table_name;
return FALSE;
}
@@ -6217,10 +6284,10 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg)
void Item::init_make_send_field(Send_field *tmp_field,
const Type_handler *h)
{
- tmp_field->db_name= "";
- tmp_field->org_table_name= "";
+ tmp_field->db_name= empty_clex_str;
+ tmp_field->org_table_name= empty_clex_str;
tmp_field->org_col_name= empty_clex_str;
- tmp_field->table_name= "";
+ tmp_field->table_name= empty_clex_str;
tmp_field->col_name= name;
tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) |
(my_binary_compare(charset_for_protocol()) ?
@@ -6230,6 +6297,9 @@ void Item::init_make_send_field(Send_field *tmp_field,
tmp_field->decimals=decimals;
if (unsigned_flag)
tmp_field->flags |= UNSIGNED_FLAG;
+ static_cast<Send_field_extended_metadata>(*tmp_field)=
+ Send_field_extended_metadata();
+ h->Item_append_extended_type_info(tmp_field, this);
}
void Item::make_send_field(THD *thd, Send_field *tmp_field)
@@ -6318,7 +6388,7 @@ String_copier_for_item::copy_with_warn(CHARSET_INFO *dstcs, String *dst,
if (unlikely(pos= cannot_convert_error_pos()))
{
char buf[16];
- int mblen= my_charlen(srccs, pos, src + src_length);
+ int mblen= srccs->charlen(pos, src + src_length);
DBUG_ASSERT(mblen > 0 && mblen * 2 + 1 <= (int) sizeof(buf));
octet2hex(buf, pos, mblen);
push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN,
@@ -6379,15 +6449,15 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs)
void Item_field::make_send_field(THD *thd, Send_field *tmp_field)
{
field->make_send_field(tmp_field);
- DBUG_ASSERT(tmp_field->table_name != 0);
+ DBUG_ASSERT(tmp_field->table_name.str != 0);
if (name.str)
{
DBUG_ASSERT(name.length == strlen(name.str));
tmp_field->col_name= name; // Use user supplied name
}
- if (table_name)
+ if (table_name.str)
tmp_field->table_name= table_name;
- if (db_name)
+ if (db_name.str)
tmp_field->db_name= db_name;
}
@@ -6612,9 +6682,9 @@ int Item_string::save_in_field(Field *field, bool no_conversions)
Item *Item_string::clone_item(THD *thd)
{
- return new (thd->mem_root)
- Item_string(thd, name.str, str_value.ptr(),
- str_value.length(), collation.collation);
+ LEX_CSTRING val;
+ str_value.get_value(&val);
+ return new (thd->mem_root) Item_string(thd, name, val, collation.collation);
}
@@ -6844,8 +6914,7 @@ Item_float::Item_float(THD *thd, const char *str_arg, size_t length):
{
int error;
char *end_not_used;
- value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end_not_used,
- &error);
+ value= my_charset_bin.strntod((char*) str_arg, length, &end_not_used, &error);
if (unlikely(error))
{
char tmp[NAME_LEN + 2];
@@ -7223,7 +7292,7 @@ Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg)
all_fields->push_front((Item*)this, thd->mem_root);
ref= new (thd->mem_root)
Item_ref(thd, &select->context, &ref_pointer_array[el],
- table_name, &field_name);
+ table_name, field_name);
return ref;
}
return this;
@@ -7449,8 +7518,7 @@ Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel)
if (field_item)
{
Item_ref *ref= new (thd->mem_root) Item_ref(thd, &sel->context,
- NullS, NullS,
- &field_item->field_name);
+ field_item->field_name);
return ref;
}
DBUG_ASSERT(0);
@@ -7612,10 +7680,10 @@ void Item_temptable_field::print(String *str, enum_query_type query_type)
Item_ref::Item_ref(THD *thd, Name_resolution_context *context_arg,
- Item **item, const char *table_name_arg,
- const LEX_CSTRING *field_name_arg,
+ Item **item, const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg,
bool alias_name_used_arg):
- Item_ident(thd, context_arg, NullS, table_name_arg, field_name_arg),
+ Item_ident(thd, context_arg, null_clex_str, table_name_arg, field_name_arg),
ref(item), reference_trough_name(0)
{
alias_name_used= alias_name_used_arg;
@@ -7662,7 +7730,7 @@ public:
};
Item_ref::Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item,
- const LEX_CSTRING *field_name_arg,
+ const LEX_CSTRING &field_name_arg,
bool alias_name_used_arg):
Item_ident(thd, view_arg, field_name_arg),
ref(item), reference_trough_name(0)
@@ -8096,7 +8164,7 @@ void Item_ref::print(String *str, enum_query_type query_type)
if ((*ref)->type() != Item::CACHE_ITEM &&
(*ref)->type() != Item::WINDOW_FUNC_ITEM &&
ref_type() != VIEW_REF &&
- !table_name && name.str && alias_name_used)
+ !table_name.str && name.str && alias_name_used)
{
THD *thd= current_thd;
append_identifier(thd, str, &(*ref)->real_item()->name);
@@ -8330,13 +8398,13 @@ void Item_ref::make_send_field(THD *thd, Send_field *field)
/* Non-zero in case of a view */
if (name.str)
field->col_name= name;
- if (table_name)
+ if (table_name.str)
field->table_name= table_name;
- if (db_name)
+ if (db_name.str)
field->db_name= db_name;
if (orig_field_name.str)
field->org_col_name= orig_field_name;
- if (orig_table_name)
+ if (orig_table_name.str)
field->org_table_name= orig_table_name;
}
diff --git a/sql/item.h b/sql/item.h
index 2b49487d677..fb480b4c578 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -623,6 +623,13 @@ class st_select_lex_unit;
class Item_func_not;
class Item_splocal;
+/* Item::common_flags */
+/* Indicates that name of this Item autogenerated or set by user */
+#define IS_AUTO_GENERATED_NAME 1
+/* Indicates that this item is in CYCLE clause of WITH */
+#define IS_IN_WITH_CYCLE 2
+
+
/**
String_copier that sends Item specific warnings.
*/
@@ -784,10 +791,11 @@ protected:
/**
Create a field based on the exact data type handler.
*/
- Field *create_table_field_from_handler(TABLE *table)
+ Field *create_table_field_from_handler(MEM_ROOT *root, TABLE *table)
{
const Type_handler *h= type_handler();
- return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+ return h->make_and_init_table_field(root, &name,
+ Record_addr(maybe_null),
*this, table);
}
/**
@@ -800,11 +808,12 @@ protected:
@retval NULL error
@retval !NULL on success
*/
- Field *tmp_table_field_from_field_type(TABLE *table)
+ Field *tmp_table_field_from_field_type(MEM_ROOT *root, TABLE *table)
{
DBUG_ASSERT(is_fixed());
const Type_handler *h= type_handler()->type_handler_for_tmp_table(this);
- return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+ return h->make_and_init_table_field(root, &name,
+ Record_addr(maybe_null),
*this, table);
}
/**
@@ -816,17 +825,20 @@ protected:
- does not need to set Field::is_created_from_null_item for the result
See create_tmp_field_ex() for details on parameters and return values.
*/
- Field *create_tmp_field_ex_simple(TABLE *table,
+ Field *create_tmp_field_ex_simple(MEM_ROOT *root,
+ TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(!param->make_copy_field());
DBUG_ASSERT(!is_result_field());
DBUG_ASSERT(type() != NULL_ITEM);
- return tmp_table_field_from_field_type(table);
+ return tmp_table_field_from_field_type(root, table);
}
- Field *create_tmp_field_int(TABLE *table, uint convert_int_length);
- Field *tmp_table_field_from_field_type_maybe_null(TABLE *table,
+ Field *create_tmp_field_int(MEM_ROOT *root, TABLE *table,
+ uint convert_int_length);
+ Field *tmp_table_field_from_field_type_maybe_null(MEM_ROOT *root,
+ TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param,
bool is_explicit_null);
@@ -927,8 +939,9 @@ public:
True if any item except Item_sum contains a field. Set during parsing.
*/
bool with_field;
- bool is_autogenerated_name; /* indicate was name of this Item
- autogenerated or set by user */
+ uint8 common_flags;
+ bool is_autogenerated_name()
+ { return (common_flags & IS_AUTO_GENERATED_NAME); }
// alloc & destruct is done as start of select on THD::mem_root
Item(THD *thd);
/*
@@ -948,6 +961,15 @@ public:
#endif
} /*lint -e1509 */
void set_name(THD *thd, const char *str, size_t length, CHARSET_INFO *cs);
+ void set_name(THD *thd, String *str)
+ {
+ set_name(thd, str->ptr(), str->length(), str->charset());
+ }
+ void set_name(THD *thd, const LEX_CSTRING &str,
+ CHARSET_INFO *cs= system_charset_info)
+ {
+ set_name(thd, str.str, str.length, cs);
+ }
void set_name_no_truncate(THD *thd, const char *str, uint length,
CHARSET_INFO *cs);
void init_make_send_field(Send_field *tmp_field, const Type_handler *h);
@@ -1111,9 +1133,9 @@ public:
{
return type_handler()->max_display_length(this);
}
- TYPELIB *get_typelib() const { return NULL; }
+ const TYPELIB *get_typelib() const { return NULL; }
void set_maybe_null(bool maybe_null_arg) { maybe_null= maybe_null_arg; }
- void set_typelib(TYPELIB *typelib)
+ void set_typelib(const TYPELIB *typelib)
{
// Non-field Items (e.g. hybrid functions) never have ENUM/SET types yet.
DBUG_ASSERT(0);
@@ -1459,7 +1481,6 @@ public:
{
return type_handler()->Item_val_bool(this);
}
- virtual String *val_raw(String*) { return 0; }
bool eval_const_cond()
{
@@ -1527,8 +1548,7 @@ public:
int save_str_value_in_field(Field *field, String *result);
virtual Field *get_tmp_table_field() { return 0; }
- virtual Field *create_field_for_create_select(TABLE *table);
- virtual Field *create_field_for_schema(THD *thd, TABLE *table);
+ virtual Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table);
virtual const char *full_name() const { return name.str ? name.str : "???"; }
const char *field_name_or_null()
{ return real_item()->type() == Item::FIELD_ITEM ? name.str : NULL; }
@@ -1825,6 +1845,18 @@ public:
*/
virtual void top_level_item() {}
/*
+ Return TRUE if it is item of top WHERE level (AND/OR) and it is
+ important, return FALSE if it not important (we can not use to simplify
+ calculations) or not top level
+ */
+ virtual bool is_top_level_item() const
+ { return FALSE; /* not important */}
+ /*
+ return IN/ALL/ANY subquery or NULL
+ */
+ virtual Item_in_subselect* get_IN_subquery()
+ { return NULL; /* in is not IN/ALL/ANY */ }
+ /*
set field of temporary table for Item which can be switched on temporary
table during query processing (grouping and so on)
*/
@@ -1835,6 +1867,15 @@ public:
virtual bool need_parentheses_in_default() { return false; }
virtual void save_in_result_field(bool no_conversions) {}
/*
+ Data type format implied by the CHECK CONSTRAINT,
+ to be sent to the client in the result set metadata.
+ */
+ virtual bool set_format_by_check_constraint(Send_field_extended_metadata *)
+ const
+ {
+ return false;
+ }
+ /*
set value of aggregate function in case of no rows for grouping were found
*/
virtual void no_rows_in_result() {}
@@ -1901,6 +1942,7 @@ public:
virtual bool cleanup_excluding_const_fields_processor (void *arg)
{ return cleanup_processor(arg); }
virtual bool collect_item_field_processor(void *arg) { return 0; }
+ virtual bool unknown_splocal_processor(void *arg) { return 0; }
virtual bool collect_outer_ref_processor(void *arg) {return 0; }
virtual bool check_inner_refs_processor(void *arg) { return 0; }
virtual bool find_item_in_field_list_processor(void *arg) { return 0; }
@@ -2030,6 +2072,9 @@ public:
{
uint errors; /* Bits of possible errors */
const char *name; /* Not supported function */
+ Alter_info *alter_info;
+ vcol_func_processor_result() :
+ errors(0), name(NULL), alter_info(NULL) {}
};
struct func_processor_rename
{
@@ -2060,6 +2105,44 @@ public:
/*============== End of Item processor list ======================*/
/*
+ Given a condition P from the WHERE clause or from an ON expression of
+ the processed SELECT S and a set of join tables from S marked in the
+ parameter 'allowed'={T} a call of P->find_not_null_fields({T}) has to
+ find the set fields {F} of the tables from 'allowed' such that:
+ - each field from {F} is declared as nullable
+ - each record of table t from {T} that contains NULL as the value for at
+ at least one field from {F} can be ignored when building the result set
+ for S
+ It is assumed here that the condition P is conjunctive and all its column
+ references belong to T.
+
+ Examples:
+ CREATE TABLE t1 (a int, b int);
+ CREATE TABLE t2 (a int, b int);
+
+ SELECT * FROM t1,t2 WHERE t1.a=t2.a and t1.b > 5;
+ A call of find_not_null_fields() for the whole WHERE condition and {t1,t2}
+ should find {t1.a,t1.b,t2.a}
+
+ SELECT * FROM t1 LEFT JOIN ON (t1.a=t2.a and t2.a > t2.b);
+ A call of find_not_null_fields() for the ON expression and {t2}
+ should find {t2.a,t2.b}
+
+ The function returns TRUE if it succeeds to prove that all records of
+ a table from {T} can be ignored. Otherwise it always returns FALSE.
+
+ Example:
+ SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t2.a IS NULL;
+ A call of find_not_null_fields() for the WHERE condition and {t1,t2}
+ will return TRUE.
+
+ It is assumed that the implementation of this virtual function saves
+ the info on the found set of fields in the structures associates with
+ tables from {T}.
+ */
+ virtual bool find_not_null_fields(table_map allowed) { return false; }
+
+ /*
Does not guarantee deep copy (depends on copy ctor).
See build_clone() for deep copy.
*/
@@ -2127,7 +2210,8 @@ public:
const Type_handler *type_handler_long_or_longlong() const
{
- return Type_handler::type_handler_long_or_longlong(max_char_length());
+ return Type_handler::type_handler_long_or_longlong(max_char_length(),
+ unsigned_flag);
}
/**
@@ -2138,7 +2222,8 @@ public:
@retval NULL (on error)
@retval a pointer to a newly create Field (on success)
*/
- virtual Field *create_tmp_field_ex(TABLE *table,
+ virtual Field *create_tmp_field_ex(MEM_ROOT *root,
+ TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)= 0;
virtual Item_field *field_for_view_update() { return 0; }
@@ -2264,14 +2349,6 @@ public:
is_expensive_cache= walk(&Item::is_expensive_processor, 0, NULL);
return MY_TEST(is_expensive_cache);
}
- virtual Field::geometry_type get_geometry_type() const
- { return Field::GEOM_GEOMETRY; };
- uint uint_geometry_type() const
- { return get_geometry_type(); }
- void set_geometry_type(uint type)
- {
- DBUG_ASSERT(0);
- }
String *check_well_formed_result(String *str, bool send_error= 0);
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
bool too_big_for_varchar() const
@@ -2316,7 +2393,7 @@ public:
if (join_tab_idx_arg < join_tab_idx)
join_tab_idx= join_tab_idx_arg;
}
- virtual uint get_join_tab_idx() { return join_tab_idx; }
+ uint get_join_tab_idx() const { return join_tab_idx; }
table_map view_used_tables(TABLE_LIST *view)
{
@@ -2375,7 +2452,8 @@ public:
}
bool pushable_cond_checker_for_subquery(uchar *arg)
{
- return excl_dep_on_in_subq_left_part((Item_in_subselect *)arg);
+ DBUG_ASSERT(((Item*) arg)->get_IN_subquery());
+ return excl_dep_on_in_subq_left_part(((Item*)arg)->get_IN_subquery());
}
Item *build_pushable_cond(THD *thd,
Pushdown_checker checker,
@@ -2483,56 +2561,6 @@ public:
};
-class Type_geometry_attributes
-{
- uint m_geometry_type;
- static const uint m_geometry_type_unknown= Field::GEOM_GEOMETRYCOLLECTION + 1;
- void copy(const Type_handler *handler, const Type_all_attributes *gattr)
- {
- // Ignore implicit NULLs
- m_geometry_type= handler == &type_handler_geometry ?
- gattr->uint_geometry_type() :
- m_geometry_type_unknown;
- }
-public:
- Type_geometry_attributes()
- :m_geometry_type(m_geometry_type_unknown)
- { }
- Type_geometry_attributes(const Type_handler *handler,
- const Type_all_attributes *gattr)
- :m_geometry_type(m_geometry_type_unknown)
- {
- copy(handler, gattr);
- }
- void join(const Item *item)
- {
- // Ignore implicit NULLs
- if (m_geometry_type == m_geometry_type_unknown)
- copy(item->type_handler(), item);
- else if (item->type_handler() == &type_handler_geometry)
- {
- m_geometry_type=
- Field_geom::geometry_type_merge((Field_geom::geometry_type)
- m_geometry_type,
- (Field_geom::geometry_type)
- item->uint_geometry_type());
- }
- }
- Field::geometry_type get_geometry_type() const
- {
- return m_geometry_type == m_geometry_type_unknown ?
- Field::GEOM_GEOMETRY :
- (Field::geometry_type) m_geometry_type;
- }
- void set_geometry_type(uint type)
- {
- DBUG_ASSERT(type <= m_geometry_type_unknown);
- m_geometry_type= type;
- }
-};
-
-
-
/**
Compare two Items for List<Item>::add_unique()
*/
@@ -2744,12 +2772,15 @@ protected:
{
my_string_metadata_get(this, str->charset(), str->ptr(), str->length());
}
- Metadata(const String *str, uint repertoire_arg)
+ Metadata(const String *str, my_repertoire_t repertoire_arg)
{
MY_STRING_METADATA::repertoire= repertoire_arg;
MY_STRING_METADATA::char_length= str->numchars();
}
- uint repertoire() const { return MY_STRING_METADATA::repertoire; }
+ my_repertoire_t repertoire() const
+ {
+ return MY_STRING_METADATA::repertoire;
+ }
size_t char_length() const { return MY_STRING_METADATA::char_length; }
};
void fix_charset_and_length(CHARSET_INFO *cs,
@@ -2772,7 +2803,8 @@ protected:
}
Item_basic_value(THD *thd): Item(thd) {}
public:
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root,
+ TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
@@ -2784,7 +2816,8 @@ public:
DECLARE c CURSOR FOR SELECT 'test';
OPEN c;
*/
- return tmp_table_field_from_field_type_maybe_null(table, src, param,
+ return tmp_table_field_from_field_type_maybe_null(root,
+ table, src, param,
type() == Item::NULL_ITEM);
}
bool eq(const Item *item, bool binary_cmp) const;
@@ -2856,10 +2889,11 @@ public:
inline bool const_item() const;
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root,
+ TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
- return create_tmp_field_ex_simple(table, src, param);
+ return create_tmp_field_ex_simple(root, table, src, param);
}
inline int save_in_field(Field *field, bool no_conversions);
inline bool send(Protocol *protocol, st_value *buffer);
@@ -2962,8 +2996,8 @@ public:
The inherited implementation would create a column
based on result_type(), which is less exact.
*/
- Field *create_field_for_create_select(TABLE *table)
- { return create_table_field_from_handler(table); }
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
+ { return create_table_field_from_handler(root, table); }
bool is_valid_limit_clause_variable_with_error() const
{
@@ -3153,7 +3187,8 @@ public:
return TRUE;
}
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root,
+ TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
/*
@@ -3161,7 +3196,7 @@ public:
DECLARE c CURSOR FOR SELECT NAME_CONST('x','y') FROM t1;
OPEN c;
*/
- return tmp_table_field_from_field_type_maybe_null(table, src, param,
+ return tmp_table_field_from_field_type_maybe_null(root, table, src, param,
type() == Item::NULL_ITEM);
}
int save_in_field(Field *field, bool no_conversions)
@@ -3197,7 +3232,7 @@ public:
class Item_num: public Item_literal
{
public:
- Item_num(THD *thd): Item_literal(thd) { collation.set_numeric(); }
+ Item_num(THD *thd): Item_literal(thd) { collation= DTCollation_numeric(); }
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs);
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
@@ -3212,6 +3247,11 @@ class st_select_lex;
class Item_result_field :public Item_fixed_hybrid /* Item with result field */
{
+protected:
+ Field *create_tmp_field_ex_from_handler(MEM_ROOT *root, TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param,
+ const Type_handler *h);
public:
Field *result_field; /* Save result here */
Item_result_field(THD *thd): Item_fixed_hybrid(thd), result_field(0) {}
@@ -3221,8 +3261,13 @@ public:
{}
~Item_result_field() {} /* Required with gcc 2.95 */
Field *get_tmp_table_field() { return result_field; }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
- const Tmp_field_param *param);
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(fixed);
+ const Type_handler *h= type_handler()->type_handler_for_tmp_table(this);
+ return create_tmp_field_ex_from_handler(root, table, src, param, h);
+ }
void get_tmp_field_src(Tmp_field_src *src, const Tmp_field_param *param);
/*
This implementation of used_tables() used by Item_avg_field and
@@ -3249,14 +3294,16 @@ protected:
updated during fix_fields() to values from Field object and life-time
of those is shorter than life-time of Item_field.
*/
- const char *orig_db_name;
- const char *orig_table_name;
+ LEX_CSTRING orig_db_name;
+ LEX_CSTRING orig_table_name;
LEX_CSTRING orig_field_name;
+ void undeclared_spvar_error() const;
+
public:
Name_resolution_context *context;
- const char *db_name;
- const char *table_name;
+ LEX_CSTRING db_name;
+ LEX_CSTRING table_name;
LEX_CSTRING field_name;
bool alias_name_used; /* true if item was resolved against alias */
/*
@@ -3286,10 +3333,10 @@ public:
*/
bool can_be_depended;
Item_ident(THD *thd, Name_resolution_context *context_arg,
- const char *db_name_arg, const char *table_name_arg,
- const LEX_CSTRING *field_name_arg);
+ const LEX_CSTRING &db_name_arg, const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg);
Item_ident(THD *thd, Item_ident *item);
- Item_ident(THD *thd, TABLE_LIST *view_arg, const LEX_CSTRING *field_name_arg);
+ Item_ident(THD *thd, TABLE_LIST *view_arg, const LEX_CSTRING &field_name_arg);
const char *full_name() const;
void cleanup();
st_select_lex *get_depended_from() const;
@@ -3320,12 +3367,19 @@ public:
if any_privileges set to TRUE then here real effective privileges will
be stored
*/
- uint have_privileges;
+ privilege_t have_privileges;
/* field need any privileges (for VIEW creation) */
bool any_privileges;
Item_field(THD *thd, Name_resolution_context *context_arg,
- const char *db_arg,const char *table_name_arg,
- const LEX_CSTRING *field_name_arg);
+ const LEX_CSTRING &db_arg, const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg);
+ Item_field(THD *thd, Name_resolution_context *context_arg,
+ const LEX_CSTRING &field_name_arg)
+ :Item_field(thd, context_arg, null_clex_str, null_clex_str, field_name_arg)
+ { }
+ Item_field(THD *thd, Name_resolution_context *context_arg)
+ :Item_field(thd, context_arg, null_clex_str, null_clex_str, null_clex_str)
+ { }
/*
Constructor needed to process subselect with temporary tables (see Item)
*/
@@ -3357,6 +3411,7 @@ public:
my_decimal *val_decimal_result(my_decimal *);
bool val_bool_result();
bool is_null_result();
+ bool is_json_type();
bool send(Protocol *protocol, st_value *buffer);
Load_data_outvar *get_load_data_outvar()
{
@@ -3402,12 +3457,13 @@ public:
return &type_handler_null;
return field->type_handler();
}
- Field *create_tmp_field_from_item_field(TABLE *new_table,
+ Field *create_tmp_field_from_item_field(MEM_ROOT *root, TABLE *new_table,
Item_ref *orig_item,
const Tmp_field_param *param);
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root,
+ TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param);
- TYPELIB *get_typelib() const { return field->get_typelib(); }
+ const TYPELIB *get_typelib() const { return field->get_typelib(); }
enum_monotonicity_info get_monotonicity_info() const
{
return MONOTONIC_STRICT_INCREASING;
@@ -3461,7 +3517,9 @@ public:
bool is_result_field() { return false; }
void save_in_result_field(bool no_conversions);
Item *get_tmp_table_item(THD *thd);
+ bool find_not_null_fields(table_map allowed);
bool collect_item_field_processor(void * arg);
+ bool unknown_splocal_processor(void *arg);
bool add_field_to_set_processor(void * arg);
bool find_item_in_field_list_processor(void *arg);
bool register_field_in_read_map(void *arg);
@@ -3476,16 +3534,7 @@ public:
bool switch_to_nullable_fields_processor(void *arg);
bool update_vcol_processor(void *arg);
bool rename_fields_processor(void *arg);
- bool check_vcol_func_processor(void *arg)
- {
- context= 0;
- if (field && (field->unireg_check == Field::NEXT_NUMBER))
- {
- // Auto increment fields are unsupported
- return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC);
- }
- return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF);
- }
+ bool check_vcol_func_processor(void *arg);
bool set_fields_as_dependent_processor(void *arg)
{
if (!(used_tables() & OUTER_REF_TABLE_BIT))
@@ -3526,11 +3575,6 @@ public:
DBUG_ASSERT(fixed);
return field->table->pos_in_table_list->outer_join;
}
- Field::geometry_type get_geometry_type() const
- {
- DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY);
- return field->get_geometry_type();
- }
bool check_index_dependence(void *arg);
friend class Item_default_value;
friend class Item_insert_value;
@@ -3664,7 +3708,7 @@ public:
return result_field->type_handler();
return &type_handler_null;
}
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(0);
@@ -3711,8 +3755,7 @@ public:
class Item_param :public Item_basic_value,
private Settable_routine_parameter,
public Rewritable_query_parameter,
- private Type_handler_hybrid_field_type,
- public Type_geometry_attributes
+ private Type_handler_hybrid_field_type
{
/*
NO_VALUE is a special value meaning that the parameter has not been
@@ -3887,12 +3930,6 @@ public:
return false;
}
- Field::geometry_type get_geometry_type() const
- { return Type_geometry_attributes::get_geometry_type(); };
-
- void set_geometry_type(uint type)
- { Type_geometry_attributes::set_geometry_type(type); }
-
Item_param(THD *thd, const LEX_CSTRING *name_arg,
uint pos_in_query_arg, uint len_in_query_arg);
@@ -4035,7 +4072,7 @@ public:
bool set_limit_clause_param(longlong nr)
{
- value.set_handler(&type_handler_longlong);
+ value.set_handler(&type_handler_slonglong);
set_int(nr, MY_INT64_NUM_DECIMAL_DIGITS);
return !unsigned_flag && value.integer < 0;
}
@@ -4155,8 +4192,8 @@ public:
Item_int(THD *thd, const char *str_arg, size_t length=64);
const Type_handler *type_handler() const
{ return type_handler_long_or_longlong(); }
- Field *create_field_for_create_select(TABLE *table)
- { return tmp_table_field_from_field_type(table); }
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
+ { return tmp_table_field_from_field_type(root, table); }
const longlong *const_ptr_longlong() const { return &value; }
longlong val_int() { return value; }
longlong val_int_min() const { return value; }
@@ -4209,9 +4246,7 @@ public:
Item_uint(THD *thd, ulonglong i): Item_int(thd, i, 10) {}
Item_uint(THD *thd, const char *str_arg, longlong i, uint length);
double val_real() { return ulonglong2double((ulonglong)value); }
- String *val_str(String*);
Item *clone_item(THD *thd);
- virtual void print(String *str, enum_query_type query_type);
Item *neg(THD *thd);
uint decimal_precision() const { return max_length; }
Item *get_copy(THD *thd)
@@ -4350,7 +4385,7 @@ protected:
const Metadata metadata)
{
fix_from_value(dv, metadata);
- set_name(thd, str_value.ptr(), str_value.length(), str_value.charset());
+ set_name(thd, &str_value);
}
protected:
/* Just create an item and do not fill string representation */
@@ -4374,7 +4409,7 @@ public:
}
// Constructors with the item name set from its value
Item_string(THD *thd, const char *str, uint length, CHARSET_INFO *cs,
- Derivation dv, uint repertoire)
+ Derivation dv, my_repertoire_t repertoire)
:Item_literal(thd)
{
str_value.set_or_copy_aligned(str, length, cs);
@@ -4388,7 +4423,7 @@ public:
fix_and_set_name_from_value(thd, dv, Metadata(&str_value));
}
Item_string(THD *thd, const String *str, CHARSET_INFO *tocs, uint *conv_errors,
- Derivation dv, uint repertoire)
+ Derivation dv, my_repertoire_t repertoire)
:Item_literal(thd)
{
if (str_value.copy(str, tocs, conv_errors))
@@ -4397,21 +4432,21 @@ public:
fix_and_set_name_from_value(thd, dv, Metadata(&str_value, repertoire));
}
// Constructors with an externally provided item name
- Item_string(THD *thd, const char *name_par, const char *str, size_t length,
+ Item_string(THD *thd, const LEX_CSTRING &name_par, const LEX_CSTRING &str,
CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
:Item_literal(thd)
{
- str_value.set_or_copy_aligned(str, length, cs);
+ str_value.set_or_copy_aligned(str.str, str.length, cs);
fix_from_value(dv, Metadata(&str_value));
- set_name(thd, name_par,safe_strlen(name_par), system_charset_info);
+ set_name(thd, name_par);
}
- Item_string(THD *thd, const char *name_par, const char *str, size_t length,
- CHARSET_INFO *cs, Derivation dv, uint repertoire)
+ Item_string(THD *thd, const LEX_CSTRING &name_par, const LEX_CSTRING &str,
+ CHARSET_INFO *cs, Derivation dv, my_repertoire_t repertoire)
:Item_literal(thd)
{
- str_value.set_or_copy_aligned(str, length, cs);
+ str_value.set_or_copy_aligned(str.str, str.length, cs);
fix_from_value(dv, Metadata(&str_value, repertoire));
- set_name(thd, name_par, safe_strlen(name_par), system_charset_info);
+ set_name(thd, name_par);
}
void print_value(String *to) const
{
@@ -4486,13 +4521,13 @@ public:
class Item_string_with_introducer :public Item_string
{
public:
- Item_string_with_introducer(THD *thd, const char *str, uint length,
+ Item_string_with_introducer(THD *thd, const LEX_CSTRING &str,
CHARSET_INFO *cs):
- Item_string(thd, str, length, cs)
+ Item_string(thd, str.str, str.length, cs)
{ }
- Item_string_with_introducer(THD *thd, const char *name_arg,
- const char *str, uint length, CHARSET_INFO *tocs):
- Item_string(thd, name_arg, str, length, tocs)
+ Item_string_with_introducer(THD *thd, const LEX_CSTRING &name_arg,
+ const LEX_CSTRING &str, CHARSET_INFO *tocs):
+ Item_string(thd, name_arg, str, tocs)
{ }
virtual bool is_cs_specified() const
{
@@ -4529,23 +4564,23 @@ public:
class Item_static_string_func :public Item_string
{
- const char *func_name;
+ const LEX_CSTRING func_name;
public:
- Item_static_string_func(THD *thd, const char *name_par, const char *str,
- uint length, CHARSET_INFO *cs,
+ Item_static_string_func(THD *thd, const LEX_CSTRING &name_par,
+ const LEX_CSTRING &str, CHARSET_INFO *cs,
Derivation dv= DERIVATION_COERCIBLE):
- Item_string(thd, NullS, str, length, cs, dv), func_name(name_par)
+ Item_string(thd, LEX_CSTRING({NullS,0}), str, cs, dv), func_name(name_par)
{}
- Item_static_string_func(THD *thd, const char *name_par,
+ Item_static_string_func(THD *thd, const LEX_CSTRING &name_par,
const String *str,
CHARSET_INFO *tocs, uint *conv_errors,
- Derivation dv, uint repertoire):
+ Derivation dv, my_repertoire_t repertoire):
Item_string(thd, str, tocs, conv_errors, dv, repertoire),
func_name(name_par)
{}
Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs)
{
- return const_charset_converter(thd, tocs, true, func_name);
+ return const_charset_converter(thd, tocs, true, func_name.str);
}
virtual inline void print(String *str, enum_query_type query_type)
@@ -4558,7 +4593,7 @@ public:
bool check_vcol_func_processor(void *arg)
{ // VCOL_TIME_FUNC because the value is not constant, but does not
// require fix_fields() to be re-run for every statement.
- return mark_unsupported_function(func_name, arg, VCOL_TIME_FUNC);
+ return mark_unsupported_function(func_name.str, arg, VCOL_TIME_FUNC);
}
};
@@ -4567,53 +4602,16 @@ public:
class Item_partition_func_safe_string: public Item_string
{
public:
- Item_partition_func_safe_string(THD *thd, const char *name_arg, uint length,
- CHARSET_INFO *cs= NULL):
- Item_string(thd, name_arg, length, cs)
- {}
- bool check_vcol_func_processor(void *arg)
- {
- return mark_unsupported_function("safe_string", arg, VCOL_IMPOSSIBLE);
- }
-};
-
-
-class Item_return_date_time :public Item_partition_func_safe_string
-{
- enum_field_types date_time_field_type;
-public:
- Item_return_date_time(THD *thd, const char *name_arg, uint length_arg,
- enum_field_types field_type_arg, uint dec_arg= 0):
- Item_partition_func_safe_string(thd, name_arg, length_arg, &my_charset_bin),
- date_time_field_type(field_type_arg)
- { decimals= dec_arg; }
- const Type_handler *type_handler() const
- {
- return Type_handler::get_handler_by_field_type(date_time_field_type);
- }
-};
-
-
-class Item_blob :public Item_partition_func_safe_string
-{
-public:
- Item_blob(THD *thd, const char *name_arg, uint length):
- Item_partition_func_safe_string(thd, name_arg, (uint) safe_strlen(name_arg),
- &my_charset_bin)
- { max_length= length; }
- enum Type type() const { return TYPE_HOLDER; }
- const Type_handler *type_handler() const
+ Item_partition_func_safe_string(THD *thd, const LEX_CSTRING &name_arg,
+ uint length, CHARSET_INFO *cs):
+ Item_string(thd, name_arg, LEX_CSTRING({0,0}), cs)
{
- return Type_handler::blob_type_handler(max_length);
+ max_length= length;
}
- const Type_handler *real_type_handler() const
+ bool check_vcol_func_processor(void *arg)
{
- // Should not be called, Item_blob is used for SHOW purposes only.
- DBUG_ASSERT(0);
- return &type_handler_varchar;
+ return mark_unsupported_function("safe_string", arg, VCOL_IMPOSSIBLE);
}
- Field *create_field_for_schema(THD *thd, TABLE *table)
- { return tmp_table_field_from_field_type(table); }
};
@@ -4626,15 +4624,15 @@ public:
class Item_empty_string :public Item_partition_func_safe_string
{
public:
- Item_empty_string(THD *thd, const char *header,uint length,
- CHARSET_INFO *cs= NULL):
- Item_partition_func_safe_string(thd, "", 0,
- cs ? cs : &my_charset_utf8_general_ci)
- {
- name.str= header;
- name.length= strlen(name.str);
- max_length= length * collation.collation->mbmaxlen;
- }
+ Item_empty_string(THD *thd, const LEX_CSTRING &header, uint length,
+ CHARSET_INFO *cs= &my_charset_utf8mb3_general_ci)
+ :Item_partition_func_safe_string(thd, header, length * cs->mbmaxlen, cs)
+ { }
+ Item_empty_string(THD *thd, const char *header, uint length,
+ CHARSET_INFO *cs= &my_charset_utf8mb3_general_ci)
+ :Item_partition_func_safe_string(thd, LEX_CSTRING({header, strlen(header)}),
+ length * cs->mbmaxlen, cs)
+ { }
void make_send_field(THD *thd, Send_field *field);
};
@@ -4651,7 +4649,9 @@ public:
}
const Type_handler *type_handler() const
{
- return Type_handler::get_handler_by_field_type(int_field_type);
+ const Type_handler *h=
+ Type_handler::get_handler_by_field_type(int_field_type);
+ return unsigned_flag ? h->type_handler_unsigned() : h;
}
};
@@ -4825,13 +4825,13 @@ public:
Item_temporal_literal(THD *thd)
:Item_literal(thd)
{
- collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+ collation= DTCollation_numeric();
decimals= 0;
}
Item_temporal_literal(THD *thd, uint dec_arg):
Item_literal(thd)
{
- collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+ collation= DTCollation_numeric();
decimals= dec_arg;
}
@@ -5299,10 +5299,14 @@ public:
Item **ref;
bool reference_trough_name;
Item_ref(THD *thd, Name_resolution_context *context_arg,
- const char *db_arg, const char *table_name_arg,
- const LEX_CSTRING *field_name_arg):
+ const LEX_CSTRING &db_arg, const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg):
Item_ident(thd, context_arg, db_arg, table_name_arg, field_name_arg),
set_properties_only(0), ref(0), reference_trough_name(1) {}
+ Item_ref(THD *thd, Name_resolution_context *context_arg,
+ const LEX_CSTRING &field_name_arg)
+ :Item_ref(thd, context_arg, null_clex_str, null_clex_str, field_name_arg)
+ { }
/*
This constructor is used in two scenarios:
A) *item = NULL
@@ -5318,10 +5322,10 @@ public:
with Bar, and if we have a more broader set of problems like this.
*/
Item_ref(THD *thd, Name_resolution_context *context_arg, Item **item,
- const char *table_name_arg, const LEX_CSTRING *field_name_arg,
+ const LEX_CSTRING &table_name_arg, const LEX_CSTRING &field_name_arg,
bool alias_name_used_arg= FALSE);
Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item,
- const LEX_CSTRING *field_name_arg, bool alias_name_used_arg= FALSE);
+ const LEX_CSTRING &field_name_arg, bool alias_name_used_arg= FALSE);
/* Constructor need to process subselect with temporary tables (see Item) */
Item_ref(THD *thd, Item_ref *item)
@@ -5368,7 +5372,7 @@ public:
Field *get_tmp_table_field()
{ return result_field ? result_field : (*ref)->get_tmp_table_field(); }
Item *get_tmp_table_item(THD *thd);
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param);
Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *);
table_map used_tables() const;
@@ -5397,6 +5401,10 @@ public:
{
return depended_from ? 0 : (*ref)->not_null_tables();
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return depended_from ? false : (*ref)->find_not_null_fields(allowed);
+ }
void save_in_result_field(bool no_conversions)
{
(*ref)->save_in_field(result_field, no_conversions);
@@ -5405,10 +5413,11 @@ public:
{
return ref ? (*ref)->real_item() : this;
}
- TYPELIB *get_typelib() const
+ const TYPELIB *get_typelib() const
{
return ref ? (*ref)->get_typelib() : NULL;
}
+ bool is_json_type() { return (*ref)->is_json_type(); }
bool walk(Item_processor processor, bool walk_subquery, void *arg)
{
@@ -5542,8 +5551,8 @@ class Item_direct_ref :public Item_ref
{
public:
Item_direct_ref(THD *thd, Name_resolution_context *context_arg, Item **item,
- const char *table_name_arg,
- const LEX_CSTRING *field_name_arg,
+ const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg,
bool alias_name_used_arg= FALSE):
Item_ref(thd, context_arg, item, table_name_arg,
field_name_arg, alias_name_used_arg)
@@ -5551,7 +5560,7 @@ public:
/* Constructor need to process subselect with temporary tables (see Item) */
Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {}
Item_direct_ref(THD *thd, TABLE_LIST *view_arg, Item **item,
- const LEX_CSTRING *field_name_arg,
+ const LEX_CSTRING &field_name_arg,
bool alias_name_used_arg= FALSE):
Item_ref(thd, view_arg, item, field_name_arg,
alias_name_used_arg)
@@ -5591,7 +5600,7 @@ class Item_direct_ref_to_ident :public Item_direct_ref
public:
Item_direct_ref_to_ident(THD *thd, Item_ident *item):
Item_direct_ref(thd, item->context, (Item**)&item, item->table_name,
- &item->field_name, FALSE)
+ item->field_name, FALSE)
{
ident= item;
ref= (Item**)&ident;
@@ -5783,8 +5792,8 @@ class Item_direct_view_ref :public Item_direct_ref
public:
Item_direct_view_ref(THD *thd, Name_resolution_context *context_arg,
Item **item,
- const char *table_name_arg,
- LEX_CSTRING *field_name_arg,
+ LEX_CSTRING &table_name_arg,
+ LEX_CSTRING &field_name_arg,
TABLE_LIST *view_arg):
Item_direct_ref(thd, context_arg, item, table_name_arg, field_name_arg),
item_equal(0), view(view_arg),
@@ -5966,7 +5975,7 @@ public:
Item_outer_ref(THD *thd, Name_resolution_context *context_arg,
Item_field *outer_field_arg):
Item_direct_ref(thd, context_arg, 0, outer_field_arg->table_name,
- &outer_field_arg->field_name),
+ outer_field_arg->field_name),
outer_ref(outer_field_arg), in_sum_func(0),
found_in_select_list(0), found_in_group_by(0)
{
@@ -5975,7 +5984,7 @@ public:
fixed= 0; /* reset flag set in set_properties() */
}
Item_outer_ref(THD *thd, Name_resolution_context *context_arg, Item **item,
- const char *table_name_arg, LEX_CSTRING *field_name_arg,
+ const LEX_CSTRING &table_name_arg, LEX_CSTRING &field_name_arg,
bool alias_name_used_arg):
Item_direct_ref(thd, context_arg, item, table_name_arg, field_name_arg,
alias_name_used_arg),
@@ -6016,8 +6025,8 @@ protected:
public:
Item_ref_null_helper(THD *thd, Name_resolution_context *context_arg,
Item_in_subselect* master, Item **item,
- const char *table_name_arg,
- const LEX_CSTRING *field_name_arg):
+ const LEX_CSTRING &table_name_arg,
+ const LEX_CSTRING &field_name_arg):
Item_ref(thd, context_arg, item, table_name_arg, field_name_arg),
owner(master) {}
void save_val(Field *to);
@@ -6061,14 +6070,11 @@ public:
};
#ifdef MYSQL_SERVER
-#include "gstream.h"
-#include "spatial.h"
#include "item_sum.h"
#include "item_func.h"
#include "item_row.h"
#include "item_cmpfunc.h"
#include "item_strfunc.h"
-#include "item_geofunc.h"
#include "item_timefunc.h"
#include "item_subselect.h"
#include "item_xmlfunc.h"
@@ -6145,7 +6151,7 @@ public:
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(0);
@@ -6156,7 +6162,7 @@ public:
table_map used_tables() const { return (table_map) 1L; }
bool const_item() const { return 0; }
bool is_null() { return null_value; }
- bool check_vcol_func_processor(void *arg)
+ bool check_vcol_func_processor(void *arg)
{
return mark_unsupported_function("copy", arg, VCOL_IMPOSSIBLE);
}
@@ -6383,12 +6389,10 @@ class Item_default_value : public Item_field
{
void calculate();
public:
- Item *arg;
- Field *cached_field;
- Item_default_value(THD *thd, Name_resolution_context *context_arg, Item *a)
- :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL,
- &null_clex_str),
- arg(a), cached_field(NULL) {}
+ Item *arg= nullptr;
+ Field *cached_field= nullptr;
+ Item_default_value(THD *thd, Name_resolution_context *context_arg, Item *a) :
+ Item_field(thd, context_arg), arg(a) {}
enum Type type() const { return DEFAULT_VALUE_ITEM; }
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
@@ -6443,7 +6447,8 @@ public:
return false;
}
bool is_evaluable_expression() const { return false; }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root,
+ TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(0);
@@ -6568,8 +6573,7 @@ class Item_insert_value : public Item_field
public:
Item *arg;
Item_insert_value(THD *thd, Name_resolution_context *context_arg, Item *a)
- :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL,
- &null_clex_str),
+ :Item_field(thd, context_arg),
arg(a) {}
bool eq(const Item *item, bool binary_cmp) const;
bool fix_fields(THD *, Item **);
@@ -6630,10 +6634,9 @@ public:
Item_trigger_field(THD *thd, Name_resolution_context *context_arg,
row_version_type row_ver_arg,
- const LEX_CSTRING *field_name_arg,
- ulong priv, const bool ro)
- :Item_field(thd, context_arg,
- (const char *)NULL, (const char *)NULL, field_name_arg),
+ const LEX_CSTRING &field_name_arg,
+ privilege_t priv, const bool ro)
+ :Item_field(thd, context_arg, field_name_arg),
row_version(row_ver_arg), field_idx((uint)-1), original_privilege(priv),
want_privilege(priv), table_grants(NULL), read_only (ro)
{}
@@ -6674,8 +6677,8 @@ private:
want_privilege and cleanup() is responsible for restoring of
original want_privilege once parameter's value is updated).
*/
- ulong original_privilege;
- ulong want_privilege;
+ privilege_t original_privilege;
+ privilege_t want_privilege;
GRANT_INFO *table_grants;
/*
Trigger field is read-only unless it belongs to the NEW row in a
@@ -6683,6 +6686,7 @@ private:
*/
bool read_only;
public:
+ bool unknown_splocal_processor(void *arg) { return false; }
bool check_vcol_func_processor(void *arg);
};
@@ -6755,10 +6759,10 @@ public:
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
- return create_tmp_field_ex_simple(table, src, param);
+ return create_tmp_field_ex_simple(root, table, src, param);
}
virtual void keep_array() {}
@@ -6858,8 +6862,6 @@ class Item_cache_int: public Item_cache
protected:
longlong value;
public:
- Item_cache_int(THD *thd): Item_cache(thd, &type_handler_longlong),
- value(0) {}
Item_cache_int(THD *thd, const Type_handler *handler):
Item_cache(thd, handler), value(0) {}
@@ -7267,11 +7269,10 @@ public:
single SP/PS execution.
*/
class Item_type_holder: public Item,
- public Type_handler_hybrid_field_type,
- public Type_geometry_attributes
+ public Type_handler_hybrid_field_type
{
protected:
- TYPELIB *enum_set_typelib;
+ const TYPELIB *enum_set_typelib;
public:
Item_type_holder(THD *thd, Item *item)
:Item(thd, item),
@@ -7288,12 +7289,12 @@ public:
bool maybe_null_arg)
:Item(thd),
Type_handler_hybrid_field_type(handler),
- Type_geometry_attributes(handler, attr),
enum_set_typelib(attr->get_typelib())
{
name= item->name;
Type_std_attributes::set(*attr);
maybe_null= maybe_null_arg;
+ common_flags= item->common_flags;
}
const Type_handler *type_handler() const
@@ -7307,7 +7308,7 @@ public:
}
enum Type type() const { return TYPE_HOLDER; }
- TYPELIB *get_typelib() const { return enum_set_typelib; }
+ const TYPELIB *get_typelib() const { return enum_set_typelib; }
/*
When handling a query like this:
VALUES ('') UNION VALUES( _utf16 0x0020 COLLATE utf16_bin);
@@ -7330,21 +7331,13 @@ public:
my_decimal *val_decimal(my_decimal *);
String *val_str(String*);
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
return Item_type_holder::real_type_handler()->
- make_and_init_table_field(&name, Record_addr(maybe_null),
+ make_and_init_table_field(root, &name, Record_addr(maybe_null),
*this, table);
}
- Field::geometry_type get_geometry_type() const
- {
- return Type_geometry_attributes::get_geometry_type();
- }
- void set_geometry_type(uint type)
- {
- Type_geometry_attributes::set_geometry_type(type);
- }
Item* get_copy(THD *thd) { return 0; }
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 2904980c825..2b662c4de7d 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2019, MariaDB
+ Copyright (c) 2009, 2020, 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
@@ -33,6 +33,8 @@
#include "sql_parse.h" // check_stack_overrun
#include "sql_base.h" // dynamic_column_error_message
+#define PCRE2_STATIC 1 /* Important on Windows */
+#include "pcre2.h" /* pcre2 header file */
/*
Compare row signature of two expressions
@@ -1195,11 +1197,9 @@ longlong Item_func_truth::val_int()
}
-bool Item_in_optimizer::is_top_level_item()
+bool Item_in_optimizer::is_top_level_item() const
{
- if (invisible_mode())
- return FALSE;
- return ((Item_in_subselect *)args[1])->is_top_level_item();
+ return args[1]->is_top_level_item();
}
@@ -1230,6 +1230,15 @@ bool Item_in_optimizer::eval_not_null_tables(void *opt_arg)
}
+bool Item_in_optimizer::find_not_null_fields(table_map allowed)
+{
+ if (!(~allowed & used_tables()) && is_top_level_item())
+ {
+ return args[0]->find_not_null_fields(allowed);
+ }
+ return false;
+}
+
void Item_in_optimizer::print(String *str, enum_query_type query_type)
{
if (query_type & QT_PARSABLE)
@@ -1254,10 +1263,9 @@ void Item_in_optimizer::print(String *str, enum_query_type query_type)
void Item_in_optimizer::restore_first_argument()
{
- if (!invisible_mode())
- {
- args[0]= ((Item_in_subselect *)args[1])->left_expr;
- }
+ Item_in_subselect *in_subs= args[1]->get_IN_subquery();
+ if (in_subs)
+ args[0]= in_subs->left_exp();
}
@@ -1281,8 +1289,8 @@ bool Item_in_optimizer::fix_left(THD *thd)
the pointer to the post-transformation item. Because of that, on the
next execution we need to copy args[1]->left_expr again.
*/
- ref0= &(((Item_in_subselect *)args[1])->left_expr);
- args[0]= ((Item_in_subselect *)args[1])->left_expr;
+ ref0= args[1]->get_IN_subquery()->left_exp_ptr();
+ args[0]= (*ref0);
}
if ((*ref0)->fix_fields_if_needed(thd, ref0) ||
(!cache && !(cache= (*ref0)->get_cache(thd))))
@@ -1408,9 +1416,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
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);
+ return (args[1]->get_IN_subquery() == NULL);
}
@@ -1572,7 +1578,7 @@ longlong Item_in_optimizer::val_int()
"<outer_value_list> [NOT] IN (SELECT <inner_value_list>...)"
where one or more of the outer values is NULL.
*/
- if (((Item_in_subselect*)args[1])->is_top_level_item())
+ if (args[1]->is_top_level_item())
{
/*
We're evaluating a top level item, e.g.
@@ -1595,7 +1601,7 @@ longlong Item_in_optimizer::val_int()
SELECT evaluated over the non-NULL values produces at least
one row, FALSE otherwise
*/
- Item_in_subselect *item_subs=(Item_in_subselect*)args[1];
+ Item_in_subselect *item_subs= args[1]->get_IN_subquery();
bool all_left_cols_null= true;
const uint ncols= cache->cols();
@@ -1741,8 +1747,7 @@ Item *Item_in_optimizer::transform(THD *thd, Item_transformer transformer,
((Item_subselect*)(args[1]))->substype() ==
Item_subselect::ANY_SUBS));
- Item_in_subselect *in_arg= (Item_in_subselect*)args[1];
- thd->change_item_tree(&in_arg->left_expr, args[0]);
+ thd->change_item_tree(args[1]->get_IN_subquery()->left_exp_ptr(), args[0]);
}
return (this->*transformer)(thd, argument);
}
@@ -2076,7 +2081,17 @@ bool Item_func_between::eval_not_null_tables(void *opt_arg)
(args[1]->not_null_tables() &
args[2]->not_null_tables()));
return 0;
-}
+}
+
+
+bool Item_func_between::find_not_null_fields(table_map allowed)
+{
+ if (negated || !is_top_level_item() || (~allowed & used_tables()))
+ return false;
+ return args[0]->find_not_null_fields(allowed) ||
+ args[1]->find_not_null_fields(allowed) ||
+ args[2]->find_not_null_fields(allowed);
+}
bool Item_func_between::count_sargable_conds(void *arg)
@@ -2134,7 +2149,7 @@ bool Item_func_between::fix_length_and_dec_numeric(THD *thd)
if (cvt_arg1 && cvt_arg2)
{
// Works for all types
- m_comparator.set_handler(&type_handler_longlong);
+ m_comparator.set_handler(&type_handler_slonglong);
}
}
}
@@ -4343,6 +4358,15 @@ Item_func_in::eval_not_null_tables(void *opt_arg)
}
+bool
+Item_func_in::find_not_null_fields(table_map allowed)
+{
+ if (negated || !is_top_level_item() || (~allowed & used_tables()))
+ return 0;
+ return args[0]->find_not_null_fields(allowed);
+}
+
+
void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
@@ -4477,7 +4501,7 @@ bool Item_func_in::value_list_convert_const_to_int(THD *thd)
all_converted= false;
}
if (all_converted)
- m_comparator.set_handler(&type_handler_longlong);
+ m_comparator.set_handler(&type_handler_slonglong);
}
}
return thd->is_fatal_error; // Catch errrors in convert_const_to_int
@@ -4704,43 +4728,73 @@ void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding)
}
-longlong Item_func_bit_or::val_int()
+class Func_handler_bit_or_int_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
{
- DBUG_ASSERT(fixed == 1);
- ulonglong arg1= (ulonglong) args[0]->val_int();
- if (args[0]->null_value)
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
{
- null_value=1; /* purecov: inspected */
- return 0; /* purecov: inspected */
+ DBUG_ASSERT(item->is_fixed());
+ Longlong_null a= item->arguments()[0]->to_longlong_null();
+ return a.is_null() ? a : a | item->arguments()[1]->to_longlong_null();
}
- ulonglong arg2= (ulonglong) args[1]->val_int();
- if (args[1]->null_value)
+};
+
+
+class Func_handler_bit_or_dec_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
{
- null_value=1;
- return 0;
+ DBUG_ASSERT(item->is_fixed());
+ VDec a(item->arguments()[0]);
+ return a.is_null() ? Longlong_null() :
+ a.to_xlonglong_null() | VDec(item->arguments()[1]).to_xlonglong_null();
}
- null_value=0;
- return (longlong) (arg1 | arg2);
+};
+
+
+bool Item_func_bit_or::fix_length_and_dec()
+{
+ static Func_handler_bit_or_int_to_ulonglong ha_int_to_ull;
+ static Func_handler_bit_or_dec_to_ulonglong ha_dec_to_ull;
+ return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
}
-longlong Item_func_bit_and::val_int()
+class Func_handler_bit_and_int_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
{
- DBUG_ASSERT(fixed == 1);
- ulonglong arg1= (ulonglong) args[0]->val_int();
- if (args[0]->null_value)
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
{
- null_value=1; /* purecov: inspected */
- return 0; /* purecov: inspected */
+ DBUG_ASSERT(item->is_fixed());
+ Longlong_null a= item->arguments()[0]->to_longlong_null();
+ return a.is_null() ? a : a & item->arguments()[1]->to_longlong_null();
}
- ulonglong arg2= (ulonglong) args[1]->val_int();
- if (args[1]->null_value)
+};
+
+
+class Func_handler_bit_and_dec_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
{
- null_value=1; /* purecov: inspected */
- return 0; /* purecov: inspected */
+ DBUG_ASSERT(item->is_fixed());
+ VDec a(item->arguments()[0]);
+ return a.is_null() ? Longlong_null() :
+ a.to_xlonglong_null() & VDec(item->arguments()[1]).to_xlonglong_null();
}
- null_value=0;
- return (longlong) (arg1 & arg2);
+};
+
+
+bool Item_func_bit_and::fix_length_and_dec()
+{
+ static Func_handler_bit_and_int_to_ulonglong ha_int_to_ull;
+ static Func_handler_bit_and_dec_to_ulonglong ha_dec_to_ull;
+ return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
}
Item_cond::Item_cond(THD *thd, Item_cond *item)
@@ -4946,6 +5000,82 @@ Item_cond::eval_not_null_tables(void *opt_arg)
}
+/**
+ @note
+ This implementation of the virtual function find_not_null_fields()
+ infers null-rejectedness if fields from tables marked in 'allowed' from
+ this condition.
+ Currently only top level AND conjuncts that are not disjunctions are used
+ for the inference. Usage of any top level and-or formula with l OR levels
+ would require a stack of bitmaps for fields of the height h=2*l+1 So we
+ would have to allocate h-1 additional field bitmaps for each table marked
+ in 'allowed'.
+*/
+
+bool
+Item_cond::find_not_null_fields(table_map allowed)
+{
+ Item *item;
+ bool is_and_cond= functype() == Item_func::COND_AND_FUNC;
+ if (!is_and_cond)
+ {
+ /* Now only fields of top AND level conjuncts are taken into account */
+ return false;
+ }
+ uint isnull_func_cnt= 0;
+ List_iterator<Item> li(list);
+ while ((item=li++))
+ {
+ bool is_mult_eq= item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::MULT_EQUAL_FUNC;
+ if (is_mult_eq)
+ {
+ if (!item->find_not_null_fields(allowed))
+ continue;
+ }
+
+ if (~allowed & item->used_tables())
+ continue;
+
+ /* It is assumed that all constant conjuncts are already eliminated */
+
+ /*
+ First infer null-rejectedness of fields from all conjuncts but
+ IS NULL predicates
+ */
+ bool isnull_func= item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::ISNULL_FUNC;
+ if (isnull_func)
+ {
+ isnull_func_cnt++;
+ continue;
+ }
+ if (!item->find_not_null_fields(allowed))
+ continue;
+ }
+
+ /* Now try no get contradictions using IS NULL conjuncts */
+ if (isnull_func_cnt)
+ {
+ li.rewind();
+ while ((item=li++) && isnull_func_cnt)
+ {
+ if (~allowed & item->used_tables())
+ continue;
+
+ bool isnull_func= item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::ISNULL_FUNC;
+ if (isnull_func)
+ {
+ if (item->find_not_null_fields(allowed))
+ return true;
+ isnull_func_cnt--;
+ }
+ }
+ }
+ return false;
+}
+
void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
@@ -5391,6 +5521,19 @@ longlong Item_func_isnull::val_int()
}
+bool Item_func_isnull::find_not_null_fields(table_map allowed)
+{
+ if (!(~allowed & used_tables()) &&
+ args[0]->real_item()->type() == Item::FIELD_ITEM)
+ {
+ Field *field= ((Item_field *)(args[0]->real_item()))->field;
+ if (bitmap_is_set(&field->table->tmp_set, field->field_index))
+ return true;
+ }
+ return false;
+}
+
+
void Item_func_isnull::print(String *str, enum_query_type query_type)
{
if (const_item() && !args[0]->maybe_null &&
@@ -5487,7 +5630,7 @@ longlong Item_func_like::val_int()
null_value=0;
if (canDoTurboBM)
return turboBM_matches(res->ptr(), res->length()) ? !negated : negated;
- return my_wildcmp(cmp_collation.collation,
+ return cmp_collation.collation->wildcmp(
res->ptr(),res->ptr()+res->length(),
res2->ptr(),res2->ptr()+res2->length(),
escape,wild_one,wild_many) ? negated : !negated;
@@ -5580,14 +5723,14 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str,
return TRUE;
}
- if (use_mb(cmp_cs))
+ if (cmp_cs->use_mb())
{
CHARSET_INFO *cs= escape_str->charset();
my_wc_t wc;
- int rc= cs->cset->mb_wc(cs, &wc,
- (const uchar*) escape_str_ptr,
- (const uchar*) escape_str_ptr +
- escape_str->length());
+ int rc= cs->mb_wc(&wc,
+ (const uchar*) escape_str_ptr,
+ (const uchar*) escape_str_ptr +
+ escape_str->length());
*escape= (int) (rc > 0 ? wc : '\\');
}
else
@@ -5655,7 +5798,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
{
const char* tmp = first + 1;
for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ;
- canDoTurboBM = (tmp == last) && !use_mb(args[0]->collation.collation);
+ canDoTurboBM = (tmp == last) && !args[0]->collation.collation->use_mb();
}
if (canDoTurboBM)
{
@@ -5713,15 +5856,28 @@ int Regexp_processor_pcre::default_regex_flags()
return default_regex_flags_pcre(current_thd);
}
-void Regexp_processor_pcre::set_recursion_limit(THD *thd)
+void Regexp_processor_pcre::cleanup()
{
- long stack_used;
- DBUG_ASSERT(thd == current_thd);
- stack_used= available_stack_size(thd->thread_stack, &stack_used);
- m_pcre_extra.match_limit_recursion=
- (ulong)((my_thread_stack_size - STACK_MIN_SIZE - stack_used)/my_pcre_frame_size);
+ pcre2_match_data_free(m_pcre_match_data);
+ pcre2_code_free(m_pcre);
+ reset();
}
+void Regexp_processor_pcre::init(CHARSET_INFO *data_charset, int extra_flags)
+{
+ m_library_flags= default_regex_flags() | extra_flags |
+ (data_charset != &my_charset_bin ?
+ (PCRE2_UTF | PCRE2_UCP) : 0) |
+ ((data_charset->state &
+ (MY_CS_BINSORT | MY_CS_CSSORT)) ? 0 : PCRE2_CASELESS);
+
+ // Convert text data to utf-8.
+ m_library_charset= data_charset == &my_charset_bin ?
+ &my_charset_bin : &my_charset_utf8mb3_general_ci;
+
+ m_conversion_is_needed= (data_charset != &my_charset_bin) &&
+ !my_charset_same(data_charset, m_library_charset);
+}
/**
Convert string to lib_charset, if needed.
@@ -5755,8 +5911,8 @@ String *Regexp_processor_pcre::convert_if_needed(String *str, String *converter)
bool Regexp_processor_pcre::compile(String *pattern, bool send_error)
{
- const char *pcreErrorStr;
- int pcreErrorOffset;
+ int pcreErrorNumber;
+ PCRE2_SIZE pcreErrorOffset;
if (is_compiled())
{
@@ -5769,19 +5925,39 @@ bool Regexp_processor_pcre::compile(String *pattern, bool send_error)
if (!(pattern= convert_if_needed(pattern, &pattern_converter)))
return true;
- m_pcre= pcre_compile(pattern->c_ptr_safe(), m_library_flags,
- &pcreErrorStr, &pcreErrorOffset, NULL);
+ pcre2_compile_context *cctx= NULL;
+#ifndef pcre2_set_depth_limit
+ // old pcre2 uses stack - put a limit on that (new pcre2 prefers heap)
+ cctx= pcre2_compile_context_create(NULL);
+ pcre2_set_compile_recursion_guard(cctx, [](uint32_t cur, void *end) -> int
+ { return available_stack_size(&cur, end) < STACK_MIN_SIZE; },
+ current_thd->mysys_var->stack_ends_here);
+#endif
+ m_pcre= pcre2_compile((PCRE2_SPTR8) pattern->ptr(), pattern->length(),
+ m_library_flags,
+ &pcreErrorNumber, &pcreErrorOffset, cctx);
+ pcre2_compile_context_free(cctx); // NULL is ok here
if (unlikely(m_pcre == NULL))
{
if (send_error)
{
char buff[MAX_FIELD_WIDTH];
- my_snprintf(buff, sizeof(buff), "%s at offset %d", pcreErrorStr, pcreErrorOffset);
+ int lmsg= pcre2_get_error_message(pcreErrorNumber,
+ (PCRE2_UCHAR8 *)buff, sizeof(buff));
+ if (lmsg >= 0)
+ my_snprintf(buff+lmsg, sizeof(buff)-lmsg,
+ " at offset %d", pcreErrorOffset);
my_error(ER_REGEXP_ERROR, MYF(0), buff);
}
return true;
}
+ m_pcre_match_data= pcre2_match_data_create_from_pattern(m_pcre, NULL);
+ if (m_pcre_match_data == NULL)
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return true;
+ }
return false;
}
@@ -5802,124 +5978,54 @@ bool Regexp_processor_pcre::compile(Item *item, bool send_error)
*/
void Regexp_processor_pcre::pcre_exec_warn(int rc) const
{
- char buf[64];
- const char *errmsg= NULL;
+ PCRE2_UCHAR8 buf[128];
THD *thd= current_thd;
- /*
- Make a descriptive message only for those pcre_exec() error codes
- that can actually happen in MariaDB.
- */
- switch (rc)
+ int errlen= pcre2_get_error_message(rc, buf, sizeof(buf));
+ if (errlen <= 0)
{
- case PCRE_ERROR_NULL:
- errmsg= "pcre_exec: null argument passed";
- break;
- case PCRE_ERROR_BADOPTION:
- errmsg= "pcre_exec: bad option";
- break;
- case PCRE_ERROR_BADMAGIC:
- errmsg= "pcre_exec: bad magic - not a compiled regex";
- break;
- case PCRE_ERROR_UNKNOWN_OPCODE:
- errmsg= "pcre_exec: error in compiled regex";
- break;
- case PCRE_ERROR_NOMEMORY:
- errmsg= "pcre_exec: Out of memory";
- break;
- case PCRE_ERROR_NOSUBSTRING:
- errmsg= "pcre_exec: no substring";
- break;
- case PCRE_ERROR_MATCHLIMIT:
- errmsg= "pcre_exec: match limit exceeded";
- break;
- case PCRE_ERROR_CALLOUT:
- errmsg= "pcre_exec: callout error";
- break;
- case PCRE_ERROR_BADUTF8:
- errmsg= "pcre_exec: Invalid utf8 byte sequence in the subject string";
- break;
- case PCRE_ERROR_BADUTF8_OFFSET:
- errmsg= "pcre_exec: Started at invalid location within utf8 byte sequence";
- break;
- case PCRE_ERROR_PARTIAL:
- errmsg= "pcre_exec: partial match";
- break;
- case PCRE_ERROR_INTERNAL:
- errmsg= "pcre_exec: internal error";
- break;
- case PCRE_ERROR_BADCOUNT:
- errmsg= "pcre_exec: ovesize is negative";
- break;
- case PCRE_ERROR_RECURSIONLIMIT:
- my_snprintf(buf, sizeof(buf), "pcre_exec: recursion limit of %ld exceeded",
- m_pcre_extra.match_limit_recursion);
- errmsg= buf;
- break;
- case PCRE_ERROR_BADNEWLINE:
- errmsg= "pcre_exec: bad newline options";
- break;
- case PCRE_ERROR_BADOFFSET:
- errmsg= "pcre_exec: start offset negative or greater than string length";
- break;
- case PCRE_ERROR_SHORTUTF8:
- errmsg= "pcre_exec: ended in middle of utf8 sequence";
- break;
- case PCRE_ERROR_JIT_STACKLIMIT:
- errmsg= "pcre_exec: insufficient stack memory for JIT compile";
- break;
- case PCRE_ERROR_RECURSELOOP:
- errmsg= "pcre_exec: Recursion loop detected";
- break;
- case PCRE_ERROR_BADMODE:
- errmsg= "pcre_exec: compiled pattern passed to wrong bit library function";
- break;
- case PCRE_ERROR_BADENDIANNESS:
- errmsg= "pcre_exec: compiled pattern passed to wrong endianness processor";
- break;
- case PCRE_ERROR_JIT_BADOPTION:
- errmsg= "pcre_exec: bad jit option";
- break;
- case PCRE_ERROR_BADLENGTH:
- errmsg= "pcre_exec: negative length";
- 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;
+ my_snprintf((char *)buf, sizeof(buf), "pcre_exec: Internal error (%d)", rc);
}
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_REGEXP_ERROR, ER_THD(thd, ER_REGEXP_ERROR), errmsg);
+ ER_REGEXP_ERROR, ER_THD(thd, ER_REGEXP_ERROR), buf);
}
/**
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,
+int Regexp_processor_pcre::pcre_exec_with_warn(const pcre2_code *code,
+ pcre2_match_data *data,
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);
+ int options)
+{
+ pcre2_match_context *mctx= NULL;
+#ifndef pcre2_set_depth_limit
+ // old pcre2 uses stack - put a limit on that (new pcre2 prefers heap)
+ mctx= pcre2_match_context_create(NULL);
+ pcre2_set_recursion_limit(mctx,
+ available_stack_size(&mctx, current_thd->mysys_var->stack_ends_here)/544);
+#endif
+ int rc= pcre2_match(code, (PCRE2_SPTR8) subject, (PCRE2_SIZE) length,
+ (PCRE2_SIZE) startoffset, options, data, mctx);
+ pcre2_match_context_free(mctx); // NULL is ok here
DBUG_EXECUTE_IF("pcre_exec_error_123", rc= -123;);
- if (unlikely(rc < PCRE_ERROR_NOMATCH))
+ if (unlikely(rc < PCRE2_ERROR_NOMATCH))
+ {
+ m_SubStrVec= NULL;
pcre_exec_warn(rc);
+ }
+ else
+ m_SubStrVec= pcre2_get_ovector_pointer(data);
return rc;
}
bool Regexp_processor_pcre::exec(const char *str, size_t length, size_t offset)
{
- m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, &m_pcre_extra, str, (int)length, (int)offset, 0,
- m_SubStrVec, array_elements(m_SubStrVec));
+ m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, m_pcre_match_data,
+ str, (int)length, (int)offset, 0);
return false;
}
@@ -5929,10 +6035,8 @@ bool Regexp_processor_pcre::exec(String *str, int offset,
{
if (!(str= convert_if_needed(str, &subject_converter)))
return true;
- m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, &m_pcre_extra,
- str->c_ptr_safe(), str->length(),
- offset, 0,
- m_SubStrVec, array_elements(m_SubStrVec));
+ m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, m_pcre_match_data,
+ str->ptr(), str->length(), offset, 0);
if (m_pcre_exec_rc > 0)
{
uint i;
@@ -5941,10 +6045,9 @@ bool Regexp_processor_pcre::exec(String *str, int offset,
/*
Convert byte offset into character offset.
*/
- m_SubStrVec[i]= (int) str->charset()->cset->numchars(str->charset(),
- str->ptr(),
- str->ptr() +
- m_SubStrVec[i]);
+ m_SubStrVec[i]= (int) str->charset()->numchars(str->ptr(),
+ str->ptr() +
+ m_SubStrVec[i]);
}
}
return false;
@@ -5982,12 +6085,6 @@ void Regexp_processor_pcre::fix_owner(Item_func *owner,
}
-bool Item_func_regex::fix_fields(THD *thd, Item **ref)
-{
- re.set_recursion_limit(thd);
- return Item_bool_func::fix_fields(thd, ref);
-}
-
bool
Item_func_regex::fix_length_and_dec()
{
@@ -6014,13 +6111,6 @@ longlong Item_func_regex::val_int()
}
-bool Item_func_regexp_instr::fix_fields(THD *thd, Item **ref)
-{
- re.set_recursion_limit(thd);
- return Item_int_func::fix_fields(thd, ref);
-}
-
-
bool
Item_func_regexp_instr::fix_length_and_dec()
{
@@ -6043,7 +6133,7 @@ longlong Item_func_regexp_instr::val_int()
if ((null_value= re.exec(args[0], 0, 1)))
return 0;
- return re.match() ? re.subpattern_start(0) + 1 : 0;
+ return re.match() ? (longlong) (re.subpattern_start(0) + 1) : 0;
}
@@ -6418,6 +6508,21 @@ Item *Item_cond_and::neg_transformer(THD *thd) /* NOT(a AND b AND ...) -> */
}
+bool
+Item_cond_and::set_format_by_check_constraint(
+ Send_field_extended_metadata *to) const
+{
+ List_iterator_fast<Item> li(const_cast<List<Item>&>(list));
+ Item *item;
+ while ((item= li++))
+ {
+ if (item->set_format_by_check_constraint(to))
+ return true;
+ }
+ return false;
+}
+
+
Item *Item_cond_or::neg_transformer(THD *thd) /* NOT(a OR b OR ...) -> */
/* NOT a AND NOT b AND ... */
{
@@ -6979,6 +7084,48 @@ void Item_equal::update_used_tables()
}
+/**
+ @note
+ This multiple equality can contains elements belonging not to tables {T}
+ marked in 'allowed' . So we can ascertain null-rejectedness of field f
+ belonging to table t from {T} only if one of the following equality
+ predicate can be extracted from this multiple equality:
+ - f=const
+ - f=f' where f' is a field of some table from {T}
+*/
+
+bool Item_equal::find_not_null_fields(table_map allowed)
+{
+ if (!(allowed & used_tables()))
+ return false;
+ bool checked= false;
+ Item_equal_fields_iterator it(*this);
+ Item *item;
+ while ((item= it++))
+ {
+ if (~allowed & item->used_tables())
+ continue;
+ if ((with_const || checked) && !item->find_not_null_fields(allowed))
+ continue;
+ Item_equal_fields_iterator it1(*this);
+ Item *item1;
+ while ((item1= it1++) && item1 != item)
+ {
+ if (~allowed & item1->used_tables())
+ continue;
+ if (!item->find_not_null_fields(allowed) &&
+ !item1->find_not_null_fields(allowed))
+ {
+ checked= true;
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+
+
bool Item_equal::count_sargable_conds(void *arg)
{
SELECT_LEX *sel= (SELECT_LEX *) arg;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index ab8e15372ad..fa715badfc7 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -24,8 +24,6 @@
#endif
#include "item_func.h" /* Item_int_func, Item_bool_func */
-#define PCRE_STATIC 1 /* Important on Windows */
-#include "pcre.h" /* pcre header file */
#include "item.h"
extern Item_result item_cmp_type(Item_result a,Item_result b);
@@ -292,6 +290,7 @@ public:
Item_func_truth(thd, a, true, false) {}
~Item_func_isnottrue() {}
virtual const char* func_name() const { return "isnottrue"; }
+ bool find_not_null_fields(table_map allowed) { return false; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_isnottrue>(thd, this); }
bool eval_not_null_tables(void *) { not_null_tables_cache= 0; return false; }
@@ -324,6 +323,7 @@ public:
Item_func_truth(thd, a, false, false) {}
~Item_func_isnotfalse() {}
virtual const char* func_name() const { return "isnotfalse"; }
+ bool find_not_null_fields(table_map allowed) { return false; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_isnotfalse>(thd, this); }
bool eval_not_null_tables(void *) { not_null_tables_cache= 0; return false; }
@@ -367,34 +367,37 @@ public:
Item_bool_func(thd, a, b), cache(0), expr_cache(0),
save_cache(0), result_for_null_param(UNKNOWN)
{ m_with_subquery= true; }
- bool fix_fields(THD *, Item **);
+ bool fix_fields(THD *, Item **) override;
bool fix_left(THD *thd);
- table_map not_null_tables() const { return 0; }
- bool is_null();
- longlong val_int();
- void cleanup();
- enum Functype functype() const { return IN_OPTIMIZER_FUNC; }
- const char *func_name() const { return "<in_optimizer>"; }
+ table_map not_null_tables() const override { return 0; }
+ bool is_null() override;
+ longlong val_int() override;
+ void cleanup() override;
+ enum Functype functype() const override { return IN_OPTIMIZER_FUNC; }
+ const char *func_name() const override { return "<in_optimizer>"; }
Item_cache **get_cache() { return &cache; }
void keep_top_level_cache();
- Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
- virtual Item *expr_cache_insert_transformer(THD *thd, uchar *unused);
- bool is_expensive_processor(void *arg);
- bool is_expensive();
- void set_join_tab_idx(uint join_tab_idx_arg)
+ Item *transform(THD *thd, Item_transformer transformer, uchar *arg) override;
+ Item *expr_cache_insert_transformer(THD *thd, uchar *unused) override;
+ bool is_expensive_processor(void *arg) override;
+ bool is_expensive() override;
+ void set_join_tab_idx(uint join_tab_idx_arg) override
{ args[1]->set_join_tab_idx(join_tab_idx_arg); }
- virtual void get_cache_parameters(List<Item> &parameters);
- bool is_top_level_item();
- bool eval_not_null_tables(void *opt_arg);
- void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
+ void get_cache_parameters(List<Item> &parameters) override;
+ bool is_top_level_item() const override;
+ bool eval_not_null_tables(void *opt_arg) override;
+ bool find_not_null_fields(table_map allowed) override;
+ void fix_after_pullout(st_select_lex *new_parent, Item **ref,
+ bool merge) override;
bool invisible_mode();
void reset_cache() { cache= NULL; }
- virtual void print(String *str, enum_query_type query_type);
+ void print(String *str, enum_query_type query_type) override;
void restore_first_argument();
Item* get_wrapped_in_subselect_item()
{ return args[1]; }
- Item *get_copy(THD *thd)
+ Item *get_copy(THD *thd) override
{ return get_item_copy<Item_in_optimizer>(thd, this); }
+ enum precedence precedence() const override { return args[1]->precedence(); }
};
@@ -582,6 +585,7 @@ public:
void print(String *str, enum_query_type query_type)
{ Item_func::print_op(str, query_type); }
longlong val_int();
+ bool find_not_null_fields(table_map allowed) { return false; }
Item *neg_transformer(THD *thd);
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
@@ -598,16 +602,17 @@ class Item_func_not :public Item_bool_func
public:
Item_func_not(THD *thd, Item *a):
Item_bool_func(thd, 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"; }
- enum precedence precedence() const { return NEG_PRECEDENCE; }
- Item *neg_transformer(THD *thd);
- bool fix_fields(THD *, Item **);
- virtual void print(String *str, enum_query_type query_type);
- Item *get_copy(THD *thd)
+ void top_level_item() override { abort_on_null= 1; }
+ bool is_top_level_item() const override { return abort_on_null; }
+ longlong val_int() override;
+ enum Functype functype() const override { return NOT_FUNC; }
+ const char *func_name() const override { return "not"; }
+ bool find_not_null_fields(table_map allowed) override { return false; }
+ enum precedence precedence() const override { return NEG_PRECEDENCE; }
+ Item *neg_transformer(THD *thd) override;
+ bool fix_fields(THD *, Item **) override;
+ void print(String *str, enum_query_type query_type) override;
+ Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_not>(thd, this); }
};
@@ -739,7 +744,7 @@ public:
{ return get_item_copy<Item_func_eq>(thd, this); }
};
-class Item_func_equal :public Item_bool_rowready_func2
+class Item_func_equal final :public Item_bool_rowready_func2
{
public:
Item_func_equal(THD *thd, Item *a, Item *b):
@@ -747,6 +752,7 @@ public:
longlong val_int();
bool fix_length_and_dec();
table_map not_null_tables() const { return 0; }
+ bool find_not_null_fields(table_map allowed) { return false; }
enum Functype functype() const { return EQUAL_FUNC; }
enum Functype rev_functype() const { return EQUAL_FUNC; }
cond_result eq_cmp_result() const { return COND_TRUE; }
@@ -884,19 +890,22 @@ public:
Item_func_opt_neg(THD *thd, List<Item> &list):
Item_bool_func(thd, list), negated(0), pred_level(0) {}
public:
- inline void top_level_item() { pred_level= 1; }
- bool is_top_level_item() const { return pred_level; }
- Item *neg_transformer(THD *thd)
+ void top_level_item() override { pred_level= 1; }
+ bool is_top_level_item() const override { return pred_level; }
+ Item *neg_transformer(THD *thd) override
{
negated= !negated;
return this;
}
- bool eq(const Item *item, bool binary_cmp) const;
- CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
- Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *) = 0;
+ bool eq(const Item *item, bool binary_cmp) const override;
+ CHARSET_INFO *compare_collation() const override
+ {
+ return cmp_collation.collation;
+ }
+ Item *propagate_equal_fields(THD *, const Context &,
+ COND_EQUAL *) override= 0;
};
-
class Item_func_between :public Item_func_opt_neg
{
protected:
@@ -924,6 +933,7 @@ public:
bool fix_length_and_dec_numeric(THD *);
virtual void print(String *str, enum_query_type query_type);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool count_sargable_conds(void *arg);
void add_key_fields(JOIN *join, KEY_FIELD **key_fields,
@@ -1107,9 +1117,19 @@ public:
bool native_op(THD *thd, Native *to);
bool fix_length_and_dec()
{
+ /*
+ Set nullability from args[1] by default.
+ Note, some type handlers may reset maybe_null
+ in Item_hybrid_func_fix_attributes() if args[1]
+ is NOT NULL but cannot always be converted to
+ the data type of "this" safely.
+ E.g. Type_handler_inet6 does:
+ IFNULL(inet6_not_null_expr, 'foo') -> INET6 NULL
+ IFNULL(inet6_not_null_expr, '::1') -> INET6 NOT NULL
+ */
+ maybe_null= args[1]->maybe_null;
if (Item_func_case_abbreviation2::fix_length_and_dec2(args))
return TRUE;
- maybe_null= args[1]->maybe_null;
return FALSE;
}
const char *func_name() const { return "ifnull"; }
@@ -1425,7 +1445,7 @@ public:
((Item_int*) item)->unsigned_flag= (bool)
((packed_longlong*) base)[pos].unsigned_flag;
}
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
friend int cmp_longlong(void *cmp_arg, packed_longlong *a,packed_longlong *b);
};
@@ -2450,6 +2470,7 @@ public:
const char *func_name() const { return "in"; }
enum precedence precedence() const { return IN_PRECEDENCE; }
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool count_sargable_conds(void *arg);
Item *get_copy(THD *thd)
@@ -2594,6 +2615,7 @@ public:
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
table_map not_null_tables() const { return 0; }
+ bool find_not_null_fields(table_map allowed);
Item *neg_transformer(THD *thd);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_isnull>(thd, this); }
@@ -2794,53 +2816,38 @@ public:
};
+typedef struct pcre2_real_code_8 pcre2_code;
+typedef struct pcre2_real_match_data_8 pcre2_match_data;
+#define PCRE2_SIZE size_t
class Regexp_processor_pcre
{
- pcre *m_pcre;
- pcre_extra m_pcre_extra;
+ pcre2_code *m_pcre;
+ pcre2_match_data *m_pcre_match_data;
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];
+ PCRE2_SIZE *m_SubStrVec;
void pcre_exec_warn(int rc) const;
- int pcre_exec_with_warn(const pcre *code, const pcre_extra *extra,
+ int pcre_exec_with_warn(const pcre2_code *code,
+ pcre2_match_data *data,
const char *subject, int length, int startoffset,
- int options, int *ovector, int ovecsize);
+ int options);
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_pcre(NULL), m_pcre_match_data(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_pcre_extra.flags= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
- m_pcre_extra.match_limit_recursion= 100L;
- }
+ m_library_charset(&my_charset_utf8mb3_general_ci)
+ {}
int default_regex_flags();
- void set_recursion_limit(THD *);
- void init(CHARSET_INFO *data_charset, int extra_flags)
- {
- 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);
- }
+ void init(CHARSET_INFO *data_charset, int extra_flags);
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);
@@ -2853,28 +2860,25 @@ public:
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
+ size_t subpattern_start(int n) const
{
return m_pcre_exec_rc <= 0 ? 0 : m_SubStrVec[n * 2];
}
- int subpattern_end(int n) const
+ size_t subpattern_end(int n) const
{
return m_pcre_exec_rc <= 0 ? 0 : m_SubStrVec[n * 2 + 1];
}
- int subpattern_length(int n) const
+ size_t subpattern_length(int n) const
{
return subpattern_end(n) - subpattern_start(n);
}
void reset()
{
m_pcre= NULL;
+ m_pcre_match_data= NULL;
m_prev_pattern.length(0);
}
- void cleanup()
- {
- pcre_free(m_pcre);
- reset();
- }
+ void cleanup();
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; }
@@ -2897,7 +2901,6 @@ public:
DBUG_VOID_RETURN;
}
longlong val_int();
- bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
const char *func_name() const { return "regexp"; }
enum precedence precedence() const { return IN_PRECEDENCE; }
@@ -2938,7 +2941,6 @@ public:
DBUG_VOID_RETURN;
}
longlong val_int();
- bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
const char *func_name() const { return "regexp_instr"; }
Item *get_copy(THD *thd) { return 0; }
@@ -3018,6 +3020,7 @@ public:
Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
Item *build_clone(THD *thd);
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
@@ -3183,6 +3186,7 @@ public:
eval_item= NULL;
}
void update_used_tables();
+ bool find_not_null_fields(table_map allowed);
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
bool link_item_fields,
COND_EQUAL **cond_equal_ref);
@@ -3338,7 +3342,7 @@ public:
};
-class Item_cond_and :public Item_cond
+class Item_cond_and final :public Item_cond
{
public:
COND_EQUAL m_cond_equal; /* contains list of Item_equal objects for
@@ -3361,6 +3365,7 @@ public:
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
bool link_item_fields,
COND_EQUAL **cond_equal_ref);
+ bool set_format_by_check_constraint(Send_field_extended_metadata *to) const;
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
table_map usable_tables, SARGABLE_PARAM **sargables);
SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr);
@@ -3374,7 +3379,7 @@ inline bool is_cond_and(Item *item)
return func_item && func_item->functype() == Item_func::COND_AND_FUNC;
}
-class Item_cond_or :public Item_cond
+class Item_cond_or final :public Item_cond
{
public:
Item_cond_or(THD *thd): Item_cond(thd) {}
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 5f6e7b29d5c..62f4d9f9fee 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -33,118 +33,46 @@
#include "set_var.h"
#include "sp_head.h"
#include "sp.h"
-#include "item_inetfunc.h"
#include "sql_time.h"
+#include "sql_type_geom.h"
+#include <mysql/plugin_function.h>
-/*
-=============================================================================
- LOCAL DECLARATIONS
-=============================================================================
-*/
-
-/**
- Adapter for functions that takes exactly zero arguments.
-*/
-
-class Create_func_arg0 : public Create_func
-{
-public:
- virtual Item *create_func(THD *thd, LEX_CSTRING *name,
- List<Item> *item_list);
- /**
- Builder method, with no arguments.
- @param thd The current thread
- @return An item representing the function call
- */
- virtual Item *create_builder(THD *thd) = 0;
-
-protected:
- /** Constructor. */
- Create_func_arg0() {}
- /** Destructor. */
- virtual ~Create_func_arg0() {}
-};
-
-
-/**
- Adapter for functions that takes exactly one argument.
-*/
-
-class Create_func_arg1 : public Create_func
+extern "C" uchar*
+get_native_fct_hash_key(const uchar *buff, size_t *length,
+ my_bool /* unused */)
{
-public:
- virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- /**
- Builder method, with one argument.
- @param thd The current thread
- @param arg1 The first argument of the function
- @return An item representing the function call
- */
- virtual Item *create_1_arg(THD *thd, Item *arg1) = 0;
-
-protected:
- /** Constructor. */
- Create_func_arg1() {}
- /** Destructor. */
- virtual ~Create_func_arg1() {}
-};
-
+ Native_func_registry *func= (Native_func_registry*) buff;
+ *length= func->name.length;
+ return (uchar*) func->name.str;
+}
-/**
- Adapter for functions that takes exactly two arguments.
-*/
-class Create_func_arg2 : public Create_func
+bool Native_func_registry_array::append_to_hash(HASH *hash) const
{
-public:
- virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+ DBUG_ENTER("Native_func_registry_array::append_to_hash");
+ for (size_t i= 0; i < count(); i++)
+ {
+ const Native_func_registry &func= element(i);
+ DBUG_ASSERT(func.builder != NULL);
+ if (my_hash_insert(hash, (uchar*) &func))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
- /**
- Builder method, with two arguments.
- @param thd The current thread
- @param arg1 The first argument of the function
- @param arg2 The second argument of the function
- @return An item representing the function call
- */
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) = 0;
-protected:
- /** Constructor. */
- Create_func_arg2() {}
- /** Destructor. */
- virtual ~Create_func_arg2() {}
-};
+#ifdef HAVE_SPATIAL
+extern Native_func_registry_array native_func_registry_array_geom;
+#endif
-/**
- Adapter for functions that takes exactly three arguments.
+/*
+=============================================================================
+ LOCAL DECLARATIONS
+=============================================================================
*/
-class Create_func_arg3 : public Create_func
-{
-public:
- virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- /**
- Builder method, with three arguments.
- @param thd The current thread
- @param arg1 The first argument of the function
- @param arg2 The second argument of the function
- @param arg3 The third argument of the function
- @return An item representing the function call
- */
- virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0;
-
-protected:
- /** Constructor. */
- Create_func_arg3() {}
- /** Destructor. */
- virtual ~Create_func_arg3() {}
-};
-
-
/**
Function builder for Stored Functions.
*/
@@ -165,30 +93,6 @@ protected:
};
-#ifndef HAVE_SPATIAL
-/**
- Common (non) builder for geometry functions.
- This builder is used in <code>--without-geometry</code> builds only,
- to report an error.
-*/
-
-class Create_func_no_geom : public Create_func
-{
-public:
- virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- /** Singleton. */
- static Create_func_no_geom s_singleton;
-
-protected:
- /** Constructor. */
- Create_func_no_geom() {}
- /** Destructor. */
- virtual ~Create_func_no_geom() {}
-};
-#endif
-
-
/*
Concrete functions builders (native functions).
Please keep this list sorted in alphabetical order,
@@ -260,51 +164,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_area : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_area s_singleton;
-
-protected:
- Create_func_area() {}
- virtual ~Create_func_area() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_as_wkb : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_as_wkb s_singleton;
-
-protected:
- Create_func_as_wkb() {}
- virtual ~Create_func_as_wkb() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_as_wkt : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_as_wkt s_singleton;
-
-protected:
- Create_func_as_wkt() {}
- virtual ~Create_func_as_wkt() {}
-};
-#endif
-
-
class Create_func_asin : public Create_func_arg1
{
public:
@@ -409,20 +268,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_centroid : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_centroid s_singleton;
-
-protected:
- Create_func_centroid() {}
- virtual ~Create_func_centroid() {}
-};
-
-
class Create_func_chr : public Create_func_arg1
{
public:
@@ -436,35 +281,6 @@ protected:
};
-class Create_func_convexhull : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_convexhull s_singleton;
-
-protected:
- Create_func_convexhull() {}
- virtual ~Create_func_convexhull() {}
-};
-
-
-class Create_func_pointonsurface : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_pointonsurface s_singleton;
-
-protected:
- Create_func_pointonsurface() {}
- virtual ~Create_func_pointonsurface() {}
-};
-
-
-#endif /*HAVE_SPATIAL*/
-
-
class Create_func_char_length : public Create_func_arg1
{
public:
@@ -630,34 +446,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_mbr_contains : public Create_func_arg2
-{
- public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_mbr_contains s_singleton;
-
- protected:
- Create_func_mbr_contains() {}
- virtual ~Create_func_mbr_contains() {}
-};
-
-
-class Create_func_contains : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_contains s_singleton;
-
-protected:
- Create_func_contains() {}
- virtual ~Create_func_contains() {}
-};
-#endif
-
-
class Create_func_nvl2 : public Create_func_arg3
{
public:
@@ -736,21 +524,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_crosses : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_crosses s_singleton;
-
-protected:
- Create_func_crosses() {}
- virtual ~Create_func_crosses() {}
-};
-#endif
-
-
class Create_func_datediff : public Create_func_arg2
{
public:
@@ -855,62 +628,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_dimension : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_dimension s_singleton;
-
-protected:
- Create_func_dimension() {}
- virtual ~Create_func_dimension() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_mbr_disjoint : public Create_func_arg2
-{
- public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_mbr_disjoint s_singleton;
-
- protected:
- Create_func_mbr_disjoint() {}
- virtual ~Create_func_mbr_disjoint() {}
-};
-
-
-class Create_func_disjoint : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_disjoint s_singleton;
-
-protected:
- Create_func_disjoint() {}
- virtual ~Create_func_disjoint() {}
-};
-
-
-class Create_func_distance : public Create_func_arg2
-{
- public:
- virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_distance s_singleton;
-
- protected:
- Create_func_distance() {}
- virtual ~Create_func_distance() {}
-};
-#endif
-
-
class Create_func_elt : public Create_native_func
{
public:
@@ -950,76 +667,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_endpoint : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_endpoint s_singleton;
-
-protected:
- Create_func_endpoint() {}
- virtual ~Create_func_endpoint() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_envelope : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_envelope s_singleton;
-
-protected:
- Create_func_envelope() {}
- virtual ~Create_func_envelope() {}
-};
-
-class Create_func_boundary : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_boundary s_singleton;
-
-protected:
- Create_func_boundary() {}
- virtual ~Create_func_boundary() {}
-};
-#endif /*HAVE_SPATIAL*/
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_mbr_equals : public Create_func_arg2
-{
- public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_mbr_equals s_singleton;
-
- protected:
- Create_func_mbr_equals() {}
- virtual ~Create_func_mbr_equals() {}
-};
-
-
-class Create_func_equals : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_equals s_singleton;
-
-protected:
- Create_func_equals() {}
- virtual ~Create_func_equals() {}
-};
-#endif
-
-
class Create_func_exp : public Create_func_arg1
{
public:
@@ -1046,21 +693,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_exteriorring : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_exteriorring s_singleton;
-
-protected:
- Create_func_exteriorring() {}
- virtual ~Create_func_exteriorring() {}
-};
-#endif
-
-
class Create_func_field : public Create_native_func
{
public:
@@ -1165,94 +797,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_geometry_from_text : public Create_native_func
-{
-public:
- virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- static Create_func_geometry_from_text s_singleton;
-
-protected:
- Create_func_geometry_from_text() {}
- virtual ~Create_func_geometry_from_text() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_geometry_from_wkb : public Create_native_func
-{
-public:
- virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- static Create_func_geometry_from_wkb s_singleton;
-
-protected:
- Create_func_geometry_from_wkb() {}
- virtual ~Create_func_geometry_from_wkb() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_geometry_from_json : public Create_native_func
-{
-public:
- virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- static Create_func_geometry_from_json s_singleton;
-
-protected:
- Create_func_geometry_from_json() {}
- virtual ~Create_func_geometry_from_json() {}
-};
-
-
-class Create_func_as_geojson : public Create_native_func
-{
-public:
- virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
-
- static Create_func_as_geojson s_singleton;
-
-protected:
- Create_func_as_geojson() {}
- virtual ~Create_func_as_geojson() {}
-};
-#endif /*HAVE_SPATIAL*/
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_geometry_type : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_geometry_type s_singleton;
-
-protected:
- Create_func_geometry_type() {}
- virtual ~Create_func_geometry_type() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_geometryn : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_geometryn s_singleton;
-
-protected:
- Create_func_geometryn() {}
- virtual ~Create_func_geometryn() {}
-};
-#endif
-
-
class Create_func_get_lock : public Create_func_arg2
{
public:
@@ -1266,36 +810,6 @@ 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
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_glength s_singleton;
-
-protected:
- Create_func_glength() {}
- virtual ~Create_func_glength() {}
-};
-#endif
-
-
class Create_func_greatest : public Create_native_func
{
public:
@@ -1335,110 +849,6 @@ protected:
};
-class Create_func_inet_ntoa : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_inet_ntoa s_singleton;
-
-protected:
- Create_func_inet_ntoa() {}
- virtual ~Create_func_inet_ntoa() {}
-};
-
-
-class Create_func_inet_aton : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_inet_aton s_singleton;
-
-protected:
- Create_func_inet_aton() {}
- virtual ~Create_func_inet_aton() {}
-};
-
-
-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:
@@ -1452,127 +862,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_interiorringn : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_interiorringn s_singleton;
-
-protected:
- Create_func_interiorringn() {}
- virtual ~Create_func_interiorringn() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_relate : public Create_func_arg3
-{
-public:
- virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
-
- static Create_func_relate s_singleton;
-
-protected:
- Create_func_relate() {}
- virtual ~Create_func_relate() {}
-};
-
-
-class Create_func_mbr_intersects : public Create_func_arg2
-{
- public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_mbr_intersects s_singleton;
-
- protected:
- Create_func_mbr_intersects() {}
- virtual ~Create_func_mbr_intersects() {}
-};
-
-
-class Create_func_intersects : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_intersects s_singleton;
-
-protected:
- Create_func_intersects() {}
- virtual ~Create_func_intersects() {}
-};
-
-
-class Create_func_intersection : public Create_func_arg2
-{
-public:
- virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_intersection s_singleton;
-
-protected:
- Create_func_intersection() {}
- virtual ~Create_func_intersection() {}
-};
-
-
-class Create_func_difference : public Create_func_arg2
-{
-public:
- virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_difference s_singleton;
-
-protected:
- Create_func_difference() {}
- virtual ~Create_func_difference() {}
-};
-
-
-class Create_func_union : public Create_func_arg2
-{
-public:
- virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_union s_singleton;
-
-protected:
- Create_func_union() {}
- virtual ~Create_func_union() {}
-};
-
-
-class Create_func_symdifference : public Create_func_arg2
-{
-public:
- virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_symdifference s_singleton;
-
-protected:
- Create_func_symdifference() {}
- virtual ~Create_func_symdifference() {}
-};
-
-
-class Create_func_buffer : public Create_func_arg2
-{
-public:
- virtual Item* create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_buffer s_singleton;
-
-protected:
- Create_func_buffer() {}
- virtual ~Create_func_buffer() {}
-};
-#endif /*HAVE_SPATIAL*/
-
-
class Create_func_is_free_lock : public Create_func_arg1
{
public:
@@ -1599,49 +888,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_isclosed : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_isclosed s_singleton;
-
-protected:
- Create_func_isclosed() {}
- virtual ~Create_func_isclosed() {}
-};
-
-
-class Create_func_isring : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_isring s_singleton;
-
-protected:
- Create_func_isring() {}
- virtual ~Create_func_isring() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_isempty : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_isempty s_singleton;
-
-protected:
- Create_func_isempty() {}
- virtual ~Create_func_isempty() {}
-};
-#endif
-
-
class Create_func_isnull : public Create_func_arg1
{
public:
@@ -1655,21 +901,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_issimple : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_issimple s_singleton;
-
-protected:
- Create_func_issimple() {}
- virtual ~Create_func_issimple() {}
-};
-#endif
-
-
class Create_func_json_exists : public Create_func_arg2
{
public:
@@ -2378,51 +1609,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_numgeometries : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_numgeometries s_singleton;
-
-protected:
- Create_func_numgeometries() {}
- virtual ~Create_func_numgeometries() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_numinteriorring : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_numinteriorring s_singleton;
-
-protected:
- Create_func_numinteriorring() {}
- virtual ~Create_func_numinteriorring() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_numpoints : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_numpoints s_singleton;
-
-protected:
- Create_func_numpoints() {}
- virtual ~Create_func_numpoints() {}
-};
-#endif
-
-
class Create_func_oct : public Create_func_arg1
{
public:
@@ -2449,34 +1635,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_mbr_overlaps : public Create_func_arg2
-{
- public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_mbr_overlaps s_singleton;
-
- protected:
- Create_func_mbr_overlaps() {}
- virtual ~Create_func_mbr_overlaps() {}
-};
-
-
-class Create_func_overlaps : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_overlaps s_singleton;
-
-protected:
- Create_func_overlaps() {}
- virtual ~Create_func_overlaps() {}
-};
-#endif
-
-
class Create_func_period_add : public Create_func_arg2
{
public:
@@ -2516,21 +1674,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_pointn : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_pointn s_singleton;
-
-protected:
- Create_func_pointn() {}
- virtual ~Create_func_pointn() {}
-};
-#endif
-
-
class Create_func_pow : public Create_func_arg2
{
public:
@@ -2622,6 +1765,15 @@ protected:
};
+class Create_func_release_all_locks : public Create_func_arg0
+{
+public:
+ virtual Item *create_builder(THD *thd);
+
+ static Create_func_release_all_locks s_singleton;
+};
+
+
class Create_func_release_lock : public Create_func_arg1
{
public:
@@ -2848,36 +2000,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_srid : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_srid s_singleton;
-
-protected:
- Create_func_srid() {}
- virtual ~Create_func_srid() {}
-};
-#endif
-
-
-#ifdef HAVE_SPATIAL
-class Create_func_startpoint : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_startpoint s_singleton;
-
-protected:
- Create_func_startpoint() {}
- virtual ~Create_func_startpoint() {}
-};
-#endif
-
-
class Create_func_str_to_date : public Create_func_arg2
{
public:
@@ -3034,21 +2156,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_touches : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_touches s_singleton;
-
-protected:
- Create_func_touches() {}
- virtual ~Create_func_touches() {}
-};
-#endif
-
-
class Create_func_ucase : public Create_func_arg1
{
public:
@@ -3179,33 +2286,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_mbr_within : public Create_func_arg2
-{
- public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_mbr_within s_singleton;
-
- protected:
- Create_func_mbr_within() {}
- virtual ~Create_func_mbr_within() {}
-};
-
-
-class Create_func_within : public Create_func_arg2
-{
-public:
- virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
-
- static Create_func_within s_singleton;
-
-protected:
- Create_func_within() {}
- virtual ~Create_func_within() {}
-};
-#endif
-
#ifdef WITH_WSREP
class Create_func_wsrep_last_written_gtid : public Create_func_arg0
{
@@ -3246,20 +2326,6 @@ protected:
};
#endif /* WITH_WSREP */
-#ifdef HAVE_SPATIAL
-class Create_func_x : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_x s_singleton;
-
-protected:
- Create_func_x() {}
- virtual ~Create_func_x() {}
-};
-#endif
-
class Create_func_xml_extractvalue : public Create_func_arg2
{
@@ -3287,21 +2353,6 @@ protected:
};
-#ifdef HAVE_SPATIAL
-class Create_func_y : public Create_func_arg1
-{
-public:
- virtual Item *create_1_arg(THD *thd, Item *arg1);
-
- static Create_func_y s_singleton;
-
-protected:
- Create_func_y() {}
- virtual ~Create_func_y() {}
-};
-#endif
-
-
class Create_func_year_week : public Create_native_func
{
public:
@@ -3336,7 +2387,7 @@ static bool has_named_parameters(List<Item> *params)
List_iterator<Item> it(*params);
while ((param= it++))
{
- if (! param->is_autogenerated_name)
+ if (! param->is_autogenerated_name())
return true;
}
}
@@ -3344,21 +2395,6 @@ static bool has_named_parameters(List<Item> *params)
return false;
}
-#ifndef HAVE_SPATIAL
-Create_func_no_geom Create_func_no_geom::s_singleton;
-
-Item*
-Create_func_no_geom::create_func(THD * /* unused */,
- LEX_CSTRING /* unused */,
- List<Item> * /* unused */)
-{
- /* FIXME: error message can't be translated. */
- my_error(ER_FEATURE_DISABLED, MYF(0),
- sym_group_geom.name, sym_group_geom.needed_define);
- return NULL;
-}
-#endif
-
Item*
Create_qfunc::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list)
@@ -3597,7 +2633,7 @@ Create_func_arg1::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list
Item *param_1= item_list->pop();
- if (unlikely(! param_1->is_autogenerated_name))
+ if (unlikely(! param_1->is_autogenerated_name()))
{
my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
@@ -3624,8 +2660,8 @@ Create_func_arg2::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list
Item *param_1= item_list->pop();
Item *param_2= item_list->pop();
- if (unlikely(!param_1->is_autogenerated_name ||
- !param_2->is_autogenerated_name))
+ if (unlikely(!param_1->is_autogenerated_name() ||
+ !param_2->is_autogenerated_name()))
{
my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
@@ -3653,9 +2689,9 @@ Create_func_arg3::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list
Item *param_2= item_list->pop();
Item *param_3= item_list->pop();
- if (unlikely(!param_1->is_autogenerated_name ||
- !param_2->is_autogenerated_name ||
- !param_3->is_autogenerated_name))
+ if (unlikely(!param_1->is_autogenerated_name() ||
+ !param_2->is_autogenerated_name() ||
+ !param_3->is_autogenerated_name()))
{
my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str);
return NULL;
@@ -3710,39 +2746,6 @@ Create_func_aes_decrypt::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
-#ifdef HAVE_SPATIAL
-Create_func_area Create_func_area::s_singleton;
-
-Item*
-Create_func_area::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_area(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_as_wkb Create_func_as_wkb::s_singleton;
-
-Item*
-Create_func_as_wkb::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_as_wkb(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_as_wkt Create_func_as_wkt::s_singleton;
-
-Item*
-Create_func_as_wkt::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_as_wkt(thd, arg1);
-}
-#endif
-
-
Create_func_asin Create_func_asin::s_singleton;
Item*
@@ -3854,16 +2857,6 @@ Create_func_ceiling::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_centroid Create_func_centroid::s_singleton;
-
-Item*
-Create_func_centroid::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_centroid(thd, arg1);
-}
-
-
Create_func_chr Create_func_chr::s_singleton;
Item*
@@ -3874,25 +2867,6 @@ Create_func_chr::create_1_arg(THD *thd, Item *arg1)
}
-Create_func_convexhull Create_func_convexhull::s_singleton;
-
-Item*
-Create_func_convexhull::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_convexhull(thd, arg1);
-}
-
-
-Create_func_pointonsurface Create_func_pointonsurface::s_singleton;
-
-Item*
-Create_func_pointonsurface::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_pointonsurface(thd, arg1);
-}
-#endif /*HAVE_SPATIAL*/
-
-
Create_func_char_length Create_func_char_length::s_singleton;
Item*
@@ -4050,28 +3024,6 @@ Create_func_connection_id::create_builder(THD *thd)
}
-#ifdef HAVE_SPATIAL
-Create_func_mbr_contains Create_func_mbr_contains::s_singleton;
-
-Item*
-Create_func_mbr_contains::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
- Item_func::SP_CONTAINS_FUNC);
-}
-
-
-Create_func_contains Create_func_contains::s_singleton;
-
-Item*
-Create_func_contains::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_CONTAINS_FUNC);
-}
-#endif
-
-
Create_func_nvl2 Create_func_nvl2::s_singleton;
Item*
@@ -4125,19 +3077,6 @@ Create_func_crc32::create_1_arg(THD *thd, Item *arg1)
return new (thd->mem_root) Item_func_crc32(thd, arg1);
}
-
-#ifdef HAVE_SPATIAL
-Create_func_crosses Create_func_crosses::s_singleton;
-
-Item*
-Create_func_crosses::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_CROSSES_FUNC);
-}
-#endif
-
-
Create_func_datediff Create_func_datediff::s_singleton;
Item*
@@ -4270,48 +3209,6 @@ Create_func_des_encrypt::create_native(THD *thd, LEX_CSTRING *name,
}
-#ifdef HAVE_SPATIAL
-Create_func_dimension Create_func_dimension::s_singleton;
-
-Item*
-Create_func_dimension::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_dimension(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_mbr_disjoint Create_func_mbr_disjoint::s_singleton;
-
-Item*
-Create_func_mbr_disjoint::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
- Item_func::SP_DISJOINT_FUNC);
-}
-
-
-Create_func_disjoint Create_func_disjoint::s_singleton;
-
-Item*
-Create_func_disjoint::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_DISJOINT_FUNC);
-}
-
-
-Create_func_distance Create_func_distance::s_singleton;
-
-Item*
-Create_func_distance::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_distance(thd, arg1, arg2);
-}
-#endif
-
-
Create_func_elt Create_func_elt::s_singleton;
Item*
@@ -4380,60 +3277,6 @@ Create_func_encrypt::create_native(THD *thd, LEX_CSTRING *name,
}
-#ifdef HAVE_SPATIAL
-Create_func_endpoint Create_func_endpoint::s_singleton;
-
-Item*
-Create_func_endpoint::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_spatial_decomp(thd, arg1,
- Item_func::SP_ENDPOINT);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_envelope Create_func_envelope::s_singleton;
-
-Item*
-Create_func_envelope::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_envelope(thd, arg1);
-}
-
-
-Create_func_boundary Create_func_boundary::s_singleton;
-
-Item*
-Create_func_boundary::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_boundary(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_mbr_equals Create_func_mbr_equals::s_singleton;
-
-Item*
-Create_func_mbr_equals::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
- Item_func::SP_EQUALS_FUNC);
-}
-
-
-Create_func_equals Create_func_equals::s_singleton;
-
-Item*
-Create_func_equals::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_EQUALS_FUNC);
-}
-#endif
-
-
Create_func_exp Create_func_exp::s_singleton;
Item*
@@ -4496,18 +3339,6 @@ Create_func_export_set::create_native(THD *thd, LEX_CSTRING *name,
}
-#ifdef HAVE_SPATIAL
-Create_func_exteriorring Create_func_exteriorring::s_singleton;
-
-Item*
-Create_func_exteriorring::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_spatial_decomp(thd, arg1,
- Item_func::SP_EXTERIORRING);
-}
-#endif
-
-
Create_func_field Create_func_field::s_singleton;
Item*
@@ -4650,203 +3481,6 @@ Create_func_from_unixtime::create_native(THD *thd, LEX_CSTRING *name,
}
-#ifdef HAVE_SPATIAL
-Create_func_geometry_from_text Create_func_geometry_from_text::s_singleton;
-
-Item*
-Create_func_geometry_from_text::create_native(THD *thd, LEX_CSTRING *name,
- List<Item> *item_list)
-{
- Item *func= NULL;
- int arg_count= 0;
-
- if (item_list != NULL)
- arg_count= item_list->elements;
-
- switch (arg_count) {
- case 1:
- {
- Item *param_1= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_text(thd, param_1);
- thd->lex->uncacheable(UNCACHEABLE_RAND);
- break;
- }
- case 2:
- {
- Item *param_1= item_list->pop();
- Item *param_2= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_text(thd, param_1, param_2);
- break;
- }
- default:
- {
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
- break;
- }
- }
-
- return func;
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_geometry_from_wkb Create_func_geometry_from_wkb::s_singleton;
-
-Item*
-Create_func_geometry_from_wkb::create_native(THD *thd, LEX_CSTRING *name,
- List<Item> *item_list)
-{
- Item *func= NULL;
- int arg_count= 0;
-
- if (item_list != NULL)
- arg_count= item_list->elements;
-
- switch (arg_count) {
- case 1:
- {
- Item *param_1= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_wkb(thd, param_1);
- thd->lex->uncacheable(UNCACHEABLE_RAND);
- break;
- }
- case 2:
- {
- Item *param_1= item_list->pop();
- Item *param_2= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_wkb(thd, param_1, param_2);
- break;
- }
- default:
- {
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
- break;
- }
- }
-
- return func;
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_geometry_from_json Create_func_geometry_from_json::s_singleton;
-
-Item*
-Create_func_geometry_from_json::create_native(THD *thd, LEX_CSTRING *name,
- List<Item> *item_list)
-{
- Item *func= NULL;
- int arg_count= 0;
-
- if (item_list != NULL)
- arg_count= item_list->elements;
-
- switch (arg_count) {
- case 1:
- {
- Item *json= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_json(thd, json);
- thd->lex->uncacheable(UNCACHEABLE_RAND);
- break;
- }
- case 2:
- {
- Item *json= item_list->pop();
- Item *options= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_json(thd, json, options);
- break;
- }
- case 3:
- {
- Item *json= item_list->pop();
- Item *options= item_list->pop();
- Item *srid= item_list->pop();
- func= new (thd->mem_root) Item_func_geometry_from_json(thd, json, options,
- srid);
- break;
- }
- default:
- {
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
- break;
- }
- }
-
- return func;
-}
-
-
-Create_func_as_geojson Create_func_as_geojson::s_singleton;
-
-Item*
-Create_func_as_geojson::create_native(THD *thd, LEX_CSTRING *name,
- List<Item> *item_list)
-{
- Item *func= NULL;
- int arg_count= 0;
-
- if (item_list != NULL)
- arg_count= item_list->elements;
-
- switch (arg_count) {
- case 1:
- {
- Item *geom= item_list->pop();
- func= new (thd->mem_root) Item_func_as_geojson(thd, geom);
- thd->lex->uncacheable(UNCACHEABLE_RAND);
- break;
- }
- case 2:
- {
- Item *geom= item_list->pop();
- Item *max_dec= item_list->pop();
- func= new (thd->mem_root) Item_func_as_geojson(thd, geom, max_dec);
- break;
- }
- case 3:
- {
- Item *geom= item_list->pop();
- Item *max_dec= item_list->pop();
- Item *options= item_list->pop();
- func= new (thd->mem_root) Item_func_as_geojson(thd, geom, max_dec, options);
- break;
- }
- default:
- {
- my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
- break;
- }
- }
-
- return func;
-}
-#endif /*HAVE_SPATIAL*/
-
-
-#ifdef HAVE_SPATIAL
-Create_func_geometry_type Create_func_geometry_type::s_singleton;
-
-Item*
-Create_func_geometry_type::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_geometry_type(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_geometryn Create_func_geometryn::s_singleton;
-
-Item*
-Create_func_geometryn::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_decomp_n(thd, arg1, arg2,
- Item_func::SP_GEOMETRYN);
-}
-#endif
-
Create_func_get_lock Create_func_get_lock::s_singleton;
@@ -4859,28 +3493,6 @@ 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(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_glength Create_func_glength::s_singleton;
-
-Item*
-Create_func_glength::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_glength(thd, arg1);
-}
-#endif
-
-
Create_func_greatest Create_func_greatest::s_singleton;
Item*
@@ -4920,78 +3532,6 @@ Create_func_ifnull::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
-Create_func_inet_ntoa Create_func_inet_ntoa::s_singleton;
-
-Item*
-Create_func_inet_ntoa::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_inet_ntoa(thd, 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(thd, 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(thd, arg1);
-}
-
-
-Create_func_inet_aton Create_func_inet_aton::s_singleton;
-
-Item*
-Create_func_inet_aton::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_inet_aton(thd, 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(thd, 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(thd, 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(thd, 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(thd, arg1);
-}
-
-
Create_func_instr Create_func_instr::s_singleton;
Item*
@@ -5001,98 +3541,6 @@ Create_func_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
-#ifdef HAVE_SPATIAL
-Create_func_interiorringn Create_func_interiorringn::s_singleton;
-
-Item*
-Create_func_interiorringn::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_decomp_n(thd, arg1, arg2,
- Item_func::SP_INTERIORRINGN);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_relate Create_func_relate::s_singleton;
-
-Item*
-Create_func_relate::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *matrix)
-{
- return new (thd->mem_root) Item_func_spatial_relate(thd, arg1, arg2, matrix);
-}
-
-
-Create_func_mbr_intersects Create_func_mbr_intersects::s_singleton;
-
-Item*
-Create_func_mbr_intersects::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
- Item_func::SP_INTERSECTS_FUNC);
-}
-
-
-Create_func_intersects Create_func_intersects::s_singleton;
-
-Item*
-Create_func_intersects::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_INTERSECTS_FUNC);
-}
-
-
-Create_func_intersection Create_func_intersection::s_singleton;
-
-Item*
-Create_func_intersection::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
- Gcalc_function::op_intersection);
-}
-
-
-Create_func_difference Create_func_difference::s_singleton;
-
-Item*
-Create_func_difference::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
- Gcalc_function::op_difference);
-}
-
-
-Create_func_union Create_func_union::s_singleton;
-
-Item*
-Create_func_union::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
- Gcalc_function::op_union);
-}
-
-
-Create_func_symdifference Create_func_symdifference::s_singleton;
-
-Item*
-Create_func_symdifference::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
- Gcalc_function::op_symdifference);
-}
-
-
-Create_func_buffer Create_func_buffer::s_singleton;
-
-Item*
-Create_func_buffer::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_buffer(thd, arg1, arg2);
-}
-#endif /*HAVE_SPATAI*/
-
-
Create_func_is_free_lock Create_func_is_free_lock::s_singleton;
Item*
@@ -5115,35 +3563,6 @@ Create_func_is_used_lock::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_isclosed Create_func_isclosed::s_singleton;
-
-Item*
-Create_func_isclosed::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_isclosed(thd, arg1);
-}
-
-
-Create_func_isring Create_func_isring::s_singleton;
-
-Item*
-Create_func_isring::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_isring(thd, arg1);
-}
-
-
-Create_func_isempty Create_func_isempty::s_singleton;
-
-Item*
-Create_func_isempty::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_isempty(thd, arg1);
-}
-#endif /*HAVE_SPATIAL*/
-
-
Create_func_isnull Create_func_isnull::s_singleton;
Item*
@@ -5153,17 +3572,6 @@ Create_func_isnull::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_issimple Create_func_issimple::s_singleton;
-
-Item*
-Create_func_issimple::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_issimple(thd, arg1);
-}
-#endif
-
-
Create_func_json_exists Create_func_json_exists::s_singleton;
Item*
@@ -6211,39 +4619,6 @@ Create_func_nullif::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
-#ifdef HAVE_SPATIAL
-Create_func_numgeometries Create_func_numgeometries::s_singleton;
-
-Item*
-Create_func_numgeometries::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_numgeometries(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_numinteriorring Create_func_numinteriorring::s_singleton;
-
-Item*
-Create_func_numinteriorring::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_numinteriorring(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_numpoints Create_func_numpoints::s_singleton;
-
-Item*
-Create_func_numpoints::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_numpoints(thd, arg1);
-}
-#endif
-
-
Create_func_oct Create_func_oct::s_singleton;
Item*
@@ -6264,28 +4639,6 @@ Create_func_ord::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_mbr_overlaps Create_func_mbr_overlaps::s_singleton;
-
-Item*
-Create_func_mbr_overlaps::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
- Item_func::SP_OVERLAPS_FUNC);
-}
-
-
-Create_func_overlaps Create_func_overlaps::s_singleton;
-
-Item*
-Create_func_overlaps::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_OVERLAPS_FUNC);
-}
-#endif
-
-
Create_func_period_add Create_func_period_add::s_singleton;
Item*
@@ -6313,18 +4666,6 @@ Create_func_pi::create_builder(THD *thd)
}
-#ifdef HAVE_SPATIAL
-Create_func_pointn Create_func_pointn::s_singleton;
-
-Item*
-Create_func_pointn::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_decomp_n(thd, arg1, arg2,
- Item_func::SP_POINTN);
-}
-#endif
-
-
Create_func_pow Create_func_pow::s_singleton;
Item*
@@ -6430,6 +4771,17 @@ Create_func_rand::create_native(THD *thd, LEX_CSTRING *name,
}
+Create_func_release_all_locks Create_func_release_all_locks::s_singleton;
+
+Item*
+Create_func_release_all_locks::create_builder(THD *thd)
+{
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new (thd->mem_root) Item_func_release_all_locks(thd);
+}
+
+
Create_func_release_lock Create_func_release_lock::s_singleton;
Item*
@@ -6663,29 +5015,6 @@ Create_func_sqrt::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_srid Create_func_srid::s_singleton;
-
-Item*
-Create_func_srid::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_srid(thd, arg1);
-}
-#endif
-
-
-#ifdef HAVE_SPATIAL
-Create_func_startpoint Create_func_startpoint::s_singleton;
-
-Item*
-Create_func_startpoint::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_spatial_decomp(thd, arg1,
- Item_func::SP_STARTPOINT);
-}
-#endif
-
-
Create_func_str_to_date Create_func_str_to_date::s_singleton;
Item*
@@ -6819,18 +5148,6 @@ Create_func_to_seconds::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_touches Create_func_touches::s_singleton;
-
-Item*
-Create_func_touches::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_TOUCHES_FUNC);
-}
-#endif
-
-
Create_func_ucase Create_func_ucase::s_singleton;
Item*
@@ -6933,9 +5250,9 @@ Item*
Create_func_version::create_builder(THD *thd)
{
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
- return new (thd->mem_root) Item_static_string_func(thd, "version()",
- server_version,
- (uint) strlen(server_version),
+ static Lex_cstring name(STRING_WITH_LEN("version()"));
+ return new (thd->mem_root) Item_static_string_func(thd, name,
+ Lex_cstring_strlen(server_version),
system_charset_info,
DERIVATION_SYSCONST);
}
@@ -6960,27 +5277,6 @@ Create_func_weekofyear::create_1_arg(THD *thd, Item *arg1)
}
-#ifdef HAVE_SPATIAL
-Create_func_mbr_within Create_func_mbr_within::s_singleton;
-
-Item*
-Create_func_mbr_within::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
- Item_func::SP_WITHIN_FUNC);
-}
-
-
-Create_func_within Create_func_within::s_singleton;
-
-Item*
-Create_func_within::create_2_arg(THD *thd, Item *arg1, Item *arg2)
-{
- return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
- Item_func::SP_WITHIN_FUNC);
-}
-#endif
-
#ifdef WITH_WSREP
Create_func_wsrep_last_written_gtid
Create_func_wsrep_last_written_gtid::s_singleton;
@@ -7039,17 +5335,6 @@ Create_func_wsrep_sync_wait_upto::create_native(THD *thd,
}
#endif /* WITH_WSREP */
-#ifdef HAVE_SPATIAL
-Create_func_x Create_func_x::s_singleton;
-
-Item*
-Create_func_x::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_x(thd, arg1);
-}
-#endif
-
-
Create_func_xml_extractvalue Create_func_xml_extractvalue::s_singleton;
Item*
@@ -7068,17 +5353,6 @@ Create_func_xml_update::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg
}
-#ifdef HAVE_SPATIAL
-Create_func_y Create_func_y::s_singleton;
-
-Item*
-Create_func_y::create_1_arg(THD *thd, Item *arg1)
-{
- return new (thd->mem_root) Item_func_y(thd, arg1);
-}
-#endif
-
-
Create_func_year_week Create_func_year_week::s_singleton;
Item*
@@ -7119,12 +5393,6 @@ Create_func_year_week::create_native(THD *thd, LEX_CSTRING *name,
#define BUILDER(F) & F::s_singleton
-#ifdef HAVE_SPATIAL
- #define GEOM_BUILDER(F) & F::s_singleton
-#else
- #define GEOM_BUILDER(F) & Create_func_no_geom::s_singleton
-#endif
-
/*
MySQL native functions.
MAINTAINER:
@@ -7143,12 +5411,7 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("ADDTIME") }, BUILDER(Create_func_addtime)},
{ { STRING_WITH_LEN("AES_DECRYPT") }, BUILDER(Create_func_aes_decrypt)},
{ { STRING_WITH_LEN("AES_ENCRYPT") }, BUILDER(Create_func_aes_encrypt)},
- { { STRING_WITH_LEN("AREA") }, GEOM_BUILDER(Create_func_area)},
- { { STRING_WITH_LEN("ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
{ { STRING_WITH_LEN("ASIN") }, BUILDER(Create_func_asin)},
- { { STRING_WITH_LEN("ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { STRING_WITH_LEN("ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
{ { STRING_WITH_LEN("ATAN") }, BUILDER(Create_func_atan)},
{ { STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)},
{ { STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)},
@@ -7156,11 +5419,8 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)},
{ { STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)},
{ { STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)},
- { { STRING_WITH_LEN("BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
- { { STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
{ { STRING_WITH_LEN("CEIL") }, BUILDER(Create_func_ceiling)},
{ { STRING_WITH_LEN("CEILING") }, BUILDER(Create_func_ceiling)},
- { { STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
{ { STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)},
{ { STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)},
{ { STRING_WITH_LEN("CHR") }, BUILDER(Create_func_chr)},
@@ -7176,11 +5436,9 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)},
{ { STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)},
{ { STRING_WITH_LEN("CONVERT_TZ") }, BUILDER(Create_func_convert_tz)},
- { { STRING_WITH_LEN("CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
{ { STRING_WITH_LEN("COS") }, BUILDER(Create_func_cos)},
{ { STRING_WITH_LEN("COT") }, BUILDER(Create_func_cot)},
{ { STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)},
- { { STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
{ { STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)},
{ { STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)},
{ { STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)},
@@ -7191,17 +5449,11 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("DECODE_ORACLE") }, BUILDER(Create_func_decode_oracle)},
{ { STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)},
{ { STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)},
- { { STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
- { { STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
{ { STRING_WITH_LEN("ELT") }, BUILDER(Create_func_elt)},
{ { STRING_WITH_LEN("ENCODE") }, BUILDER(Create_func_encode)},
{ { STRING_WITH_LEN("ENCRYPT") }, BUILDER(Create_func_encrypt)},
- { { STRING_WITH_LEN("ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
- { { STRING_WITH_LEN("ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
- { { STRING_WITH_LEN("EQUALS") }, GEOM_BUILDER(Create_func_equals)},
{ { STRING_WITH_LEN("EXP") }, BUILDER(Create_func_exp)},
{ { STRING_WITH_LEN("EXPORT_SET") }, BUILDER(Create_func_export_set)},
- { { STRING_WITH_LEN("EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
{ { STRING_WITH_LEN("EXTRACTVALUE") }, BUILDER(Create_func_xml_extractvalue)},
{ { STRING_WITH_LEN("FIELD") }, BUILDER(Create_func_field)},
{ { STRING_WITH_LEN("FIND_IN_SET") }, BUILDER(Create_func_find_in_set)},
@@ -7211,37 +5463,12 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("FROM_BASE64") }, BUILDER(Create_func_from_base64)},
{ { STRING_WITH_LEN("FROM_DAYS") }, BUILDER(Create_func_from_days)},
{ { STRING_WITH_LEN("FROM_UNIXTIME") }, BUILDER(Create_func_from_unixtime)},
- { { STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
- { { STRING_WITH_LEN("GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
- { { STRING_WITH_LEN("GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { STRING_WITH_LEN("GET_LOCK") }, BUILDER(Create_func_get_lock)},
- { { STRING_WITH_LEN("GLENGTH") }, GEOM_BUILDER(Create_func_glength)},
{ { STRING_WITH_LEN("GREATEST") }, BUILDER(Create_func_greatest)},
{ { STRING_WITH_LEN("HEX") }, BUILDER(Create_func_hex)},
{ { STRING_WITH_LEN("IFNULL") }, BUILDER(Create_func_ifnull)},
- { { STRING_WITH_LEN("INET_ATON") }, BUILDER(Create_func_inet_aton)},
- { { STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)},
- { { STRING_WITH_LEN("INET6_ATON") }, BUILDER(Create_func_inet6_aton)},
- { { STRING_WITH_LEN("INET6_NTOA") }, BUILDER(Create_func_inet6_ntoa)},
- { { STRING_WITH_LEN("IS_IPV4") }, BUILDER(Create_func_is_ipv4)},
- { { STRING_WITH_LEN("IS_IPV6") }, BUILDER(Create_func_is_ipv6)},
- { { STRING_WITH_LEN("IS_IPV4_COMPAT") }, BUILDER(Create_func_is_ipv4_compat)},
- { { STRING_WITH_LEN("IS_IPV4_MAPPED") }, BUILDER(Create_func_is_ipv4_mapped)},
{ { STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)},
- { { STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
- { { STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
- { { STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
- { { STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
{ { STRING_WITH_LEN("ISNULL") }, BUILDER(Create_func_isnull)},
- { { STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)},
- { { STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
{ { STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)},
{ { STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)},
{ { STRING_WITH_LEN("JSON_ARRAY") }, BUILDER(Create_func_json_array)},
@@ -7282,10 +5509,6 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("LIKE_RANGE_MIN") }, BUILDER(Create_func_like_range_min)},
{ { STRING_WITH_LEN("LIKE_RANGE_MAX") }, BUILDER(Create_func_like_range_max)},
#endif
- { { STRING_WITH_LEN("LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { STRING_WITH_LEN("LN") }, BUILDER(Create_func_ln)},
{ { STRING_WITH_LEN("LOAD_FILE") }, BUILDER(Create_func_load_file)},
{ { STRING_WITH_LEN("LOCATE") }, BUILDER(Create_func_locate)},
@@ -7302,50 +5525,18 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)},
{ { STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)},
{ { STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)},
- { { STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)},
- { { STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
- { { STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_mbr_equals)},
- { { STRING_WITH_LEN("MBREQUALS") }, GEOM_BUILDER(Create_func_mbr_equals)},
- { { STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
- { { STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)},
- { { STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)},
- { { STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_mbr_within)},
{ { STRING_WITH_LEN("MD5") }, BUILDER(Create_func_md5)},
- { { STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { STRING_WITH_LEN("MONTHNAME") }, BUILDER(Create_func_monthname)},
- { { STRING_WITH_LEN("MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)},
{ { STRING_WITH_LEN("NVL") }, BUILDER(Create_func_ifnull)},
{ { STRING_WITH_LEN("NVL2") }, BUILDER(Create_func_nvl2)},
{ { STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)},
- { { STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
- { { STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
- { { STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
{ { STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)},
{ { STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_octet_length)},
{ { STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)},
- { { STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)},
{ { STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)},
{ { STRING_WITH_LEN("PERIOD_DIFF") }, BUILDER(Create_func_period_diff)},
{ { STRING_WITH_LEN("PI") }, BUILDER(Create_func_pi)},
- { { STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)},
- { { STRING_WITH_LEN("POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
- { { STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
{ { STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)},
{ { STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)},
{ { STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)},
@@ -7354,6 +5545,8 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("REGEXP_SUBSTR") }, BUILDER(Create_func_regexp_substr)},
{ { STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)},
{ { STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)},
+ { { STRING_WITH_LEN("RELEASE_ALL_LOCKS") },
+ BUILDER(Create_func_release_all_locks)},
{ { STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)},
{ { STRING_WITH_LEN("REPLACE_ORACLE") },
BUILDER(Create_func_replace_oracle)},
@@ -7373,90 +5566,8 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("SOUNDEX") }, BUILDER(Create_func_soundex)},
{ { STRING_WITH_LEN("SPACE") }, BUILDER(Create_func_space)},
{ { STRING_WITH_LEN("SQRT") }, BUILDER(Create_func_sqrt)},
- { { STRING_WITH_LEN("SRID") }, GEOM_BUILDER(Create_func_srid)},
- { { STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
{ { STRING_WITH_LEN("STRCMP") }, BUILDER(Create_func_strcmp)},
{ { STRING_WITH_LEN("STR_TO_DATE") }, BUILDER(Create_func_str_to_date)},
- { { STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)},
- { { STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { STRING_WITH_LEN("ST_ASGEOJSON") }, GEOM_BUILDER(Create_func_as_geojson)},
- { { STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
- { { STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
- { { STRING_WITH_LEN("ST_BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
- { { STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
- { { STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
- { { STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)},
- { { STRING_WITH_LEN("ST_CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
- { { STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
- { { STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)},
- { { STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
- { { STRING_WITH_LEN("ST_DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)},
- { { STRING_WITH_LEN("ST_DISTANCE") }, GEOM_BUILDER(Create_func_distance)},
- { { STRING_WITH_LEN("ST_ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
- { { STRING_WITH_LEN("ST_ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
- { { STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
- { { STRING_WITH_LEN("ST_EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
- { { STRING_WITH_LEN("ST_GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
- { { STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
- { { STRING_WITH_LEN("ST_GEOMFROMGEOJSON") }, GEOM_BUILDER(Create_func_geometry_from_json)},
- { { STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
-#ifndef DBUG_OFF
- { { STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)},
-#endif
- { { STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
- { { STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
- { { STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)},
- { { STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)},
- { { STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
- { { STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
- { { STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)},
- { { STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
- { { STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)},
- { { STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
- { { STRING_WITH_LEN("ST_NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
- { { STRING_WITH_LEN("ST_NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
- { { STRING_WITH_LEN("ST_OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)},
- { { STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)},
- { { STRING_WITH_LEN("ST_POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
- { { STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
- { { STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
- { { STRING_WITH_LEN("ST_RELATE") }, GEOM_BUILDER(Create_func_relate)},
- { { STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)},
- { { STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
- { { STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)},
- { { STRING_WITH_LEN("ST_TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
- { { STRING_WITH_LEN("ST_UNION") }, GEOM_BUILDER(Create_func_union)},
- { { STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)},
- { { STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)},
- { { STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)},
{ { STRING_WITH_LEN("SUBSTR_ORACLE") },
BUILDER(Create_func_substr_oracle)},
{ { STRING_WITH_LEN("SUBSTRING_INDEX") }, BUILDER(Create_func_substr_index)},
@@ -7465,7 +5576,6 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("TIMEDIFF") }, BUILDER(Create_func_timediff)},
{ { STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)},
{ { STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)},
- { { STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
{ { STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)},
{ { STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)},
{ { STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)},
@@ -7481,29 +5591,18 @@ static Native_func_registry func_array[] =
{ { STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)},
{ { STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)},
{ { STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)},
- { { STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)},
#ifdef WITH_WSREP
{ { STRING_WITH_LEN("WSREP_LAST_WRITTEN_GTID") }, BUILDER(Create_func_wsrep_last_written_gtid)},
{ { STRING_WITH_LEN("WSREP_LAST_SEEN_GTID") }, BUILDER(Create_func_wsrep_last_seen_gtid)},
{ { STRING_WITH_LEN("WSREP_SYNC_WAIT_UPTO_GTID") }, BUILDER(Create_func_wsrep_sync_wait_upto)},
#endif /* WITH_WSREP */
- { { STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)},
- { { STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)},
- { { STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)},
-
- { {0, 0}, NULL}
+ { { STRING_WITH_LEN("YEARWEEK") }, BUILDER(Create_func_year_week)}
};
-static HASH native_functions_hash;
+Native_func_registry_array
+ native_func_registry_array(func_array, array_elements(func_array));
-extern "C" uchar*
-get_native_fct_hash_key(const uchar *buff, size_t *length,
- my_bool /* unused */)
-{
- Native_func_registry *func= (Native_func_registry*) buff;
- *length= func->name.length;
- return (uchar*) func->name.str;
-}
+static HASH native_functions_hash;
/*
Load the hash table for native functions.
@@ -7514,20 +5613,42 @@ get_native_fct_hash_key(const uchar *buff, size_t *length,
int item_create_init()
{
DBUG_ENTER("item_create_init");
+ size_t count= native_func_registry_array.count();
+#ifdef HAVE_SPATIAL
+ count+= native_func_registry_array_geom.count();
+#endif
+ if (my_hash_init(key_memory_native_functions, & native_functions_hash,
+ system_charset_info, (ulong) count, 0, 0, (my_hash_get_key)
+ get_native_fct_hash_key, NULL, MYF(0)))
+ DBUG_RETURN(1);
- if (my_hash_init(& native_functions_hash,
- system_charset_info,
- array_elements(func_array),
- 0,
- 0,
- (my_hash_get_key) get_native_fct_hash_key,
- NULL, /* Nothing to free */
- MYF(0)))
+ if (native_func_registry_array.append_to_hash(&native_functions_hash))
+ DBUG_RETURN(1);
+
+#ifdef HAVE_SPATIAL
+ if (native_func_registry_array_geom.append_to_hash(&native_functions_hash))
DBUG_RETURN(1);
+#endif
+
+#ifndef DBUG_OFF
+ for (uint i=0 ; i < native_functions_hash.records ; i++)
+ {
+ Native_func_registry *func;
+ func= (Native_func_registry*) my_hash_element(& native_functions_hash, i);
+ DBUG_PRINT("info", ("native function: %s length: %u",
+ func->name.str, (uint) func->name.length));
+ }
+#endif
- DBUG_RETURN(item_create_append(func_array));
+ DBUG_RETURN(0);
}
+
+/*
+ This function is used (dangerously) by plugin/versioning/versioning.cc
+ TODO: MDEV-20842 Wrap SQL functions defined in
+ plugin/versioning/versioning.cc into MariaDB_FUNCTION_PLUGIN
+*/
int item_create_append(Native_func_registry array[])
{
Native_func_registry *func;
@@ -7540,15 +5661,6 @@ int item_create_append(Native_func_registry array[])
DBUG_RETURN(1);
}
-#ifndef DBUG_OFF
- for (uint i=0 ; i < native_functions_hash.records ; i++)
- {
- func= (Native_func_registry*) my_hash_element(& native_functions_hash, i);
- DBUG_PRINT("info", ("native function: %s length: %u",
- func->name.str, (uint) func->name.length));
- }
-#endif
-
DBUG_RETURN(0);
}
@@ -7565,6 +5677,24 @@ void item_create_cleanup()
DBUG_VOID_RETURN;
}
+
+static Create_func *
+function_plugin_find_native_function_builder(THD *thd, const LEX_CSTRING &name)
+{
+ plugin_ref plugin;
+ if ((plugin= my_plugin_lock_by_name(thd, &name, MariaDB_FUNCTION_PLUGIN)))
+ {
+ Create_func *builder=
+ reinterpret_cast<Plugin_function*>(plugin_decl(plugin)->info)->
+ create_func();
+ // TODO: MDEV-20846 Add proper unlocking for MariaDB_FUNCTION_PLUGIN
+ plugin_unlock(thd, plugin);
+ return builder;
+ }
+ return NULL;
+}
+
+
Create_func *
find_native_function_builder(THD *thd, const LEX_CSTRING *name)
{
@@ -7576,12 +5706,13 @@ find_native_function_builder(THD *thd, const LEX_CSTRING *name)
(uchar*) name->str,
name->length);
- if (func)
- {
- builder= func->builder;
- }
+ if (func && (builder= func->builder))
+ return builder;
- return builder;
+ if ((builder= function_plugin_find_native_function_builder(thd, *name)))
+ return builder;
+
+ return NULL;
}
Create_qfunc *
diff --git a/sql/item_create.h b/sql/item_create.h
index 5890e8ad057..c9bdb23dffe 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -69,6 +69,111 @@ protected:
/**
+ Adapter for functions that takes exactly zero arguments.
+*/
+
+class Create_func_arg0 : public Create_func
+{
+public:
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list);
+
+ /**
+ Builder method, with no arguments.
+ @param thd The current thread
+ @return An item representing the function call
+ */
+ virtual Item *create_builder(THD *thd) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg0() {}
+ /** Destructor. */
+ virtual ~Create_func_arg0() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly one argument.
+*/
+
+class Create_func_arg1 : public Create_func
+{
+public:
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ /**
+ Builder method, with one argument.
+ @param thd The current thread
+ @param arg1 The first argument of the function
+ @return An item representing the function call
+ */
+ virtual Item *create_1_arg(THD *thd, Item *arg1) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg1() {}
+ /** Destructor. */
+ virtual ~Create_func_arg1() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly two arguments.
+*/
+
+class Create_func_arg2 : public Create_func
+{
+public:
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ /**
+ Builder method, with two arguments.
+ @param thd The current thread
+ @param arg1 The first argument of the function
+ @param arg2 The second argument of the function
+ @return An item representing the function call
+ */
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg2() {}
+ /** Destructor. */
+ virtual ~Create_func_arg2() {}
+};
+
+
+/**
+ Adapter for functions that takes exactly three arguments.
+*/
+
+class Create_func_arg3 : public Create_func
+{
+public:
+ virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ /**
+ Builder method, with three arguments.
+ @param thd The current thread
+ @param arg1 The first argument of the function
+ @param arg2 The second argument of the function
+ @param arg3 The third argument of the function
+ @return An item representing the function call
+ */
+ virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) = 0;
+
+protected:
+ /** Constructor. */
+ Create_func_arg3() {}
+ /** Destructor. */
+ virtual ~Create_func_arg3() {}
+};
+
+
+
+
+/**
Adapter for native functions with a variable number of arguments.
The main use of this class is to discard the following calls:
<code>foo(expr1 AS name1, expr2 AS name2, ...)</code>
@@ -210,5 +315,30 @@ Item *create_func_dyncol_get(THD *thd, Item *num, Item *str,
const char *c_len, const char *c_dec,
CHARSET_INFO *cs);
Item *create_func_dyncol_json(THD *thd, Item *str);
+
+
+class Native_func_registry_array
+{
+ const Native_func_registry *m_elements;
+ size_t m_count;
+public:
+ Native_func_registry_array()
+ :m_elements(NULL),
+ m_count(0)
+ { }
+ Native_func_registry_array(const Native_func_registry *elements, size_t count)
+ :m_elements(elements),
+ m_count(count)
+ { }
+ const Native_func_registry& element(size_t i) const
+ {
+ DBUG_ASSERT(i < m_count);
+ return m_elements[i];
+ }
+ size_t count() const { return m_count; }
+ bool append_to_hash(HASH *hash) const;
+};
+
+
#endif
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 7a15f919742..c0714bc263f 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -405,6 +405,25 @@ Item_func::eval_not_null_tables(void *opt_arg)
}
+bool
+Item_func::find_not_null_fields(table_map allowed)
+{
+ if (~allowed & used_tables())
+ return false;
+
+ Item **arg,**arg_end;
+ if (arg_count)
+ {
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if (!(*arg)->find_not_null_fields(allowed))
+ continue;
+ }
+ }
+ return false;
+}
+
+
void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
@@ -656,16 +675,6 @@ bool Item_func::is_expensive_processor(uchar *arg)
}
*/
-my_decimal *Item_func::val_decimal(my_decimal *decimal_value)
-{
- DBUG_ASSERT(fixed);
- longlong nr= val_int();
- if (null_value)
- return 0; /* purecov: inspected */
- int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value);
- return decimal_value;
-}
-
bool Item_hybrid_func::fix_attributes(Item **items, uint nitems)
{
@@ -1207,7 +1216,10 @@ void Item_func_minus::fix_unsigned_flag()
{
if (unsigned_flag &&
(current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION))
+ {
unsigned_flag=0;
+ set_handler(Item_func_minus::type_handler()->type_handler_signed());
+ }
}
@@ -1223,9 +1235,8 @@ bool Item_func_minus::fix_length_and_dec()
if (Item_func_minus::type_handler()->Item_func_minus_fix_length_and_dec(this))
DBUG_RETURN(TRUE);
DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr()));
- if ((m_depends_on_sql_mode_no_unsigned_subtraction= unsigned_flag) &&
- (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION))
- unsigned_flag= false;
+ m_depends_on_sql_mode_no_unsigned_subtraction= unsigned_flag;
+ fix_unsigned_flag();
DBUG_RETURN(FALSE);
}
@@ -1730,9 +1741,9 @@ static void calc_hash_for_unique(ulong &nr1, ulong &nr2, String *str)
uchar l[4];
int4store(l, str->length());
cs= str->charset();
- cs->coll->hash_sort(cs, l, sizeof(l), &nr1, &nr2);
+ cs->hash_sort(l, sizeof(l), &nr1, &nr2);
cs= str->charset();
- cs->coll->hash_sort(cs, (uchar *)str->ptr(), str->length(), &nr1, &nr2);
+ cs->hash_sort((uchar *)str->ptr(), str->length(), &nr1, &nr2);
}
longlong Item_func_hash::val_int()
@@ -1830,7 +1841,7 @@ void Item_func_neg::fix_length_and_dec_int()
Ensure that result is converted to DECIMAL, as longlong can't hold
the negated number
*/
- set_handler_by_result_type(DECIMAL_RESULT);
+ set_handler(&type_handler_newdecimal);
DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT"));
}
}
@@ -2129,44 +2140,103 @@ double Item_func_cot::val_real()
// Shift-functions, same as << and >> in C/C++
-longlong Item_func_shift_left::val_int()
+class Func_handler_shift_left_int_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
{
- DBUG_ASSERT(fixed == 1);
- uint shift;
- ulonglong res= ((ulonglong) args[0]->val_int() <<
- (shift=(uint) args[1]->val_int()));
- if (args[0]->null_value || args[1]->null_value)
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
{
- null_value=1;
- return 0;
+ DBUG_ASSERT(item->is_fixed());
+ return item->arguments()[0]->to_longlong_null() <<
+ item->arguments()[1]->to_longlong_null();
}
- null_value=0;
- return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
+};
+
+
+class Func_handler_shift_left_decimal_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return VDec(item->arguments()[0]).to_xlonglong_null() <<
+ item->arguments()[1]->to_longlong_null();
+ }
+};
+
+
+bool Item_func_shift_left::fix_length_and_dec()
+{
+ static Func_handler_shift_left_int_to_ulonglong ha_int_to_ull;
+ static Func_handler_shift_left_decimal_to_ulonglong ha_dec_to_ull;
+ return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
}
-longlong Item_func_shift_right::val_int()
+
+class Func_handler_shift_right_int_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
{
- DBUG_ASSERT(fixed == 1);
- uint shift;
- ulonglong res= (ulonglong) args[0]->val_int() >>
- (shift=(uint) args[1]->val_int());
- if (args[0]->null_value || args[1]->null_value)
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
{
- null_value=1;
- return 0;
+ DBUG_ASSERT(item->fixed == 1);
+ return item->arguments()[0]->to_longlong_null() >>
+ item->arguments()[1]->to_longlong_null();
}
- null_value=0;
- return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
+};
+
+
+class Func_handler_shift_right_decimal_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return VDec(item->arguments()[0]).to_xlonglong_null() >>
+ item->arguments()[1]->to_longlong_null();
+ }
+};
+
+
+bool Item_func_shift_right::fix_length_and_dec()
+{
+ static Func_handler_shift_right_int_to_ulonglong ha_int_to_ull;
+ static Func_handler_shift_right_decimal_to_ulonglong ha_dec_to_ull;
+ return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
}
-longlong Item_func_bit_neg::val_int()
+class Func_handler_bit_neg_int_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
{
- DBUG_ASSERT(fixed == 1);
- ulonglong res= (ulonglong) args[0]->val_int();
- if ((null_value=args[0]->null_value))
- return 0;
- return ~res;
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return ~ item->arguments()[0]->to_longlong_null();
+ }
+};
+
+
+class Func_handler_bit_neg_decimal_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return ~ VDec(item->arguments()[0]).to_xlonglong_null();
+ }
+};
+
+
+bool Item_func_bit_neg::fix_length_and_dec()
+{
+ static Func_handler_bit_neg_int_to_ulonglong ha_int_to_ull;
+ static Func_handler_bit_neg_decimal_to_ulonglong ha_dec_to_ull;
+ return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
}
@@ -2210,27 +2280,17 @@ void Item_func_int_val::fix_length_and_dec_int_or_decimal()
fix_char_length(precision + sign_length);
if (precision > 9)
{
-#if MYSQL_VERSION_ID > 100500
-#error Remove the '#else' branch and the conditional compilation
if (unsigned_flag)
set_handler(&type_handler_ulonglong);
else
set_handler(&type_handler_slonglong);
-#else
- set_handler(&type_handler_longlong);
-#endif
}
else
{
-#if MYSQL_VERSION_ID > 100500
-#error Remove the '#else' branch and the conditional compilation
if (unsigned_flag)
set_handler(&type_handler_ulong);
else
set_handler(&type_handler_slong);
-#else
- set_handler(&type_handler_long);
-#endif
}
}
}
@@ -3071,11 +3131,10 @@ longlong Item_func_locate::val_int()
if (!b->length()) // Found empty string at start
return start + 1;
- if (!cmp_collation.collation->coll->instr(cmp_collation.collation,
- a->ptr()+start,
- (uint) (a->length()-start),
- b->ptr(), b->length(),
- &match, 1))
+ if (!cmp_collation.collation->instr(a->ptr() + start,
+ (uint) (a->length() - start),
+ b->ptr(), b->length(),
+ &match, 1))
return 0;
return (longlong) match.mb_len + start0 + 1;
}
@@ -3188,7 +3247,7 @@ longlong Item_func_ord::val_int()
null_value=0;
if (!res->length()) return 0;
#ifdef USE_MB
- if (use_mb(res->charset()))
+ if (res->use_mb())
{
const char *str=res->ptr();
uint32 n=0, l=my_ismbchar(res->charset(),str,str+res->length());
@@ -3274,14 +3333,14 @@ longlong Item_func_find_in_set::val_int()
const char *str_begin= buffer->ptr();
const char *str_end= buffer->ptr();
const char *real_end= str_end+buffer->length();
- const uchar *find_str= (const uchar *) find->ptr();
+ const char *find_str= find->ptr();
uint find_str_len= find->length();
int position= 0;
while (1)
{
int symbol_len;
- if ((symbol_len= cs->cset->mb_wc(cs, &wc, (uchar*) str_end,
- (uchar*) real_end)) > 0)
+ if ((symbol_len= cs->mb_wc(&wc, (uchar*) str_end,
+ (uchar*) real_end)) > 0)
{
const char *substr_end= str_end + symbol_len;
bool is_last_item= (substr_end == real_end);
@@ -3291,9 +3350,8 @@ longlong Item_func_find_in_set::val_int()
position++;
if (is_last_item && !is_separator)
str_end= substr_end;
- if (!my_strnncoll(cs, (const uchar *) str_begin,
- (uint) (str_end - str_begin),
- find_str, find_str_len))
+ if (!cs->strnncoll(str_begin, (uint) (str_end - str_begin),
+ find_str, find_str_len))
return (longlong) position;
else
str_begin= substr_end;
@@ -3311,13 +3369,39 @@ longlong Item_func_find_in_set::val_int()
return 0;
}
-longlong Item_func_bit_count::val_int()
+
+class Func_handler_bit_count_int_to_slong:
+ public Item_handled_func::Handler_slong2
{
- DBUG_ASSERT(fixed == 1);
- ulonglong value= (ulonglong) args[0]->val_int();
- if ((null_value= args[0]->null_value))
- return 0; /* purecov: inspected */
- return (longlong) my_count_bits(value);
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return item->arguments()[0]->to_longlong_null().bit_count();
+ }
+};
+
+
+class Func_handler_bit_count_decimal_to_slong:
+ public Item_handled_func::Handler_slong2
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return VDec(item->arguments()[0]).to_xlonglong_null().bit_count();
+ }
+};
+
+
+bool Item_func_bit_count::fix_length_and_dec()
+{
+ static Func_handler_bit_count_int_to_slong ha_int_to_slong;
+ static Func_handler_bit_count_decimal_to_slong ha_dec_to_slong;
+ set_func_handler(args[0]->cmp_type() == INT_RESULT ?
+ (const Handler *) &ha_int_to_slong :
+ (const Handler *) &ha_dec_to_slong);
+ return m_func_handler->fix_length_and_dec(this);
}
@@ -3774,12 +3858,13 @@ longlong Item_master_pos_wait::val_int()
THD* thd = current_thd;
String *log_name = args[0]->val_str(&value);
int event_count= 0;
+ DBUG_ENTER("Item_master_pos_wait::val_int");
null_value=0;
if (thd->slave_thread || !log_name || !log_name->length())
{
null_value = 1;
- return 0;
+ DBUG_RETURN(0);
}
#ifdef HAVE_REPLICATION
longlong pos = (ulong)args[1]->val_int();
@@ -3815,13 +3900,15 @@ longlong Item_master_pos_wait::val_int()
}
mi->release();
#endif
- return event_count;
+ DBUG_PRINT("exit", ("event_count: %d null_value: %d", event_count,
+ (int) null_value));
+ DBUG_RETURN(event_count);
#ifdef HAVE_REPLICATION
err:
{
null_value = 1;
- return 0;
+ DBUG_RETURN(0);
}
#endif
}
@@ -3832,11 +3919,12 @@ longlong Item_master_gtid_wait::val_int()
DBUG_ASSERT(fixed == 1);
longlong result= 0;
String *gtid_pos __attribute__((unused)) = args[0]->val_str(&value);
+ DBUG_ENTER("Item_master_gtid_wait::val_int");
if (args[0]->null_value)
{
null_value= 1;
- return 0;
+ DBUG_RETURN(0);
}
null_value=0;
@@ -3853,7 +3941,7 @@ longlong Item_master_gtid_wait::val_int()
#else
null_value= 0;
#endif /* REPLICATION */
- return result;
+ DBUG_RETURN(result);
}
@@ -4130,16 +4218,17 @@ longlong Item_func_get_lock::val_int()
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))
+ my_hash_init(key_memory_User_level_lock, &thd->ull_hash,
+ &my_charset_bin, 16 /* small hash */, 0, 0, ull_get_key,
+ NULL, 0))
{
DBUG_RETURN(0);
}
MDL_request ull_request;
- ull_request.init(MDL_key::USER_LOCK, res->c_ptr_safe(), "",
+ MDL_REQUEST_INIT(&ull_request, MDL_key::USER_LOCK, res->c_ptr_safe(), "",
MDL_SHARED_NO_WRITE, MDL_EXPLICIT);
- MDL_key *ull_key = &ull_request.key;
+ MDL_key *ull_key= &ull_request.key;
if ((ull= (User_level_lock*)
@@ -4147,7 +4236,7 @@ longlong Item_func_get_lock::val_int()
{
/* Recursive lock */
ull->refs++;
- null_value = 0;
+ null_value= 0;
DBUG_PRINT("info", ("recursive lock, ref-count: %d", (int) ull->refs));
DBUG_RETURN(1);
}
@@ -4163,7 +4252,8 @@ longlong Item_func_get_lock::val_int()
DBUG_RETURN(0);
}
- ull= (User_level_lock*) my_malloc(sizeof(User_level_lock),
+ ull= (User_level_lock*) my_malloc(key_memory_User_level_lock,
+ sizeof(User_level_lock),
MYF(MY_WME|MY_THREAD_SPECIFIC));
if (ull == NULL)
{
@@ -4187,6 +4277,30 @@ longlong Item_func_get_lock::val_int()
/**
+ Release all user level locks.
+ @return
+ - N if N-lock released
+ - 0 if lock wasn't held
+*/
+longlong Item_func_release_all_locks::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ THD *thd= current_thd;
+ ulong num_unlocked= 0;
+ DBUG_ENTER("Item_func_release_all_locks::val_int");
+ for (size_t i= 0; i < thd->ull_hash.records; i++)
+ {
+ auto ull= (User_level_lock *) my_hash_element(&thd->ull_hash, i);
+ thd->mdl_context.release_lock(ull->lock);
+ num_unlocked+= ull->refs;
+ my_free(ull);
+ }
+ my_hash_free(&thd->ull_hash);
+ DBUG_RETURN(num_unlocked);
+}
+
+
+/**
Release a user level lock.
@return
- 1 if lock released
@@ -4386,7 +4500,7 @@ 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}
+ { &key_LOCK_item_func_sleep, "LOCK_item_func_sleep", PSI_FLAG_GLOBAL}
};
@@ -4507,7 +4621,7 @@ user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name,
size_t 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,
+ if (!(entry = (user_var_entry*) my_malloc(key_memory_user_var_entry, size,
MYF(MY_WME | ME_FATAL |
MY_THREAD_SPECIFIC))))
return 0;
@@ -4599,8 +4713,10 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
null_item= (args[0]->type() == NULL_ITEM);
if (!m_var_entry->charset() || !null_item)
m_var_entry->set_charset(args[0]->collation.derivation == DERIVATION_NUMERIC ?
- default_charset() : args[0]->collation.collation);
- collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT);
+ &my_charset_numeric : args[0]->collation.collation);
+ collation.set(m_var_entry->charset(),
+ args[0]->collation.derivation == DERIVATION_NUMERIC ?
+ DERIVATION_NUMERIC : DERIVATION_IMPLICIT);
switch (args[0]->result_type()) {
case STRING_RESULT:
case TIME_RESULT:
@@ -4612,7 +4728,8 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref)
set_handler(&type_handler_double);
break;
case INT_RESULT:
- set_handler(Type_handler::type_handler_long_or_longlong(max_char_length()));
+ set_handler(Type_handler::type_handler_long_or_longlong(max_char_length(),
+ unsigned_flag));
break;
case DECIMAL_RESULT:
set_handler(&type_handler_newdecimal);
@@ -4652,11 +4769,14 @@ Item_func_set_user_var::fix_length_and_dec()
{
maybe_null=args[0]->maybe_null;
decimals=args[0]->decimals;
- collation.set(DERIVATION_IMPLICIT);
if (args[0]->collation.derivation == DERIVATION_NUMERIC)
- fix_length_and_charset(args[0]->max_char_length(), default_charset());
+ {
+ collation.set(DERIVATION_NUMERIC);
+ fix_length_and_charset(args[0]->max_char_length(), &my_charset_numeric);
+ }
else
{
+ collation.set(DERIVATION_IMPLICIT);
fix_length_and_charset(args[0]->max_char_length(),
args[0]->collation.collation);
}
@@ -4761,10 +4881,10 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
if (entry->value == pos)
entry->value=0;
- entry->value= (char*) my_realloc(entry->value, length,
+ entry->value= (char*) my_realloc(key_memory_user_var_entry_value,
+ entry->value, length,
MYF(MY_ALLOW_ZERO_PTR | MY_WME |
- ME_FATAL |
- MY_THREAD_SPECIFIC));
+ ME_FATAL | MY_THREAD_SPECIFIC));
if (!entry->value)
return 1;
}
@@ -4782,6 +4902,12 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, size_t length,
entry->unsigned_flag= unsigned_arg;
}
entry->type=type;
+#ifdef USER_VAR_TRACKING
+#ifndef EMBEDDED_LIBRARY
+ THD *thd= current_thd;
+ thd->session_tracker.user_variables.mark_as_changed(thd, entry);
+#endif
+#endif // USER_VAR_TRACKING
return 0;
}
@@ -4871,7 +4997,7 @@ longlong user_var_entry::val_int(bool *null_value) const
/** Get the value of a variable as a string. */
String *user_var_entry::val_str(bool *null_value, String *str,
- uint decimals)
+ uint decimals) const
{
if ((*null_value= (value == 0)))
return (String*) 0;
@@ -5047,13 +5173,13 @@ Item_func_set_user_var::update()
case REAL_RESULT:
{
res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal),
- REAL_RESULT, default_charset(), 0);
+ REAL_RESULT, &my_charset_numeric, 0);
break;
}
case INT_RESULT:
{
res= update_hash((void*) &save_result.vint, sizeof(save_result.vint),
- INT_RESULT, default_charset(), unsigned_flag);
+ INT_RESULT, &my_charset_numeric, unsigned_flag);
break;
}
case STRING_RESULT:
@@ -5073,7 +5199,7 @@ Item_func_set_user_var::update()
else
res= update_hash((void*) save_result.vdec,
sizeof(my_decimal), DECIMAL_RESULT,
- default_charset(), 0);
+ &my_charset_numeric, 0);
break;
}
case ROW_RESULT:
@@ -5204,7 +5330,7 @@ void Item_func_set_user_var::make_send_field(THD *thd, Send_field *tmp_field)
if (result_field)
{
result_field->make_send_field(tmp_field);
- DBUG_ASSERT(tmp_field->table_name != 0);
+ DBUG_ASSERT(tmp_field->table_name.str != 0);
if (Item::name.str)
tmp_field->col_name= Item::name; // Use user supplied name
}
@@ -5512,22 +5638,31 @@ bool Item_func_get_user_var::fix_length_and_dec()
{
unsigned_flag= m_var_entry->unsigned_flag;
max_length= (uint32)m_var_entry->length;
- collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT);
- set_handler_by_result_type(m_var_entry->type);
- switch (result_type()) {
+ switch (m_var_entry->type) {
case REAL_RESULT:
+ collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
fix_char_length(DBL_DIG + 8);
+ set_handler(&type_handler_double);
break;
case INT_RESULT:
+ collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
fix_char_length(MAX_BIGINT_WIDTH);
decimals=0;
+ if (unsigned_flag)
+ set_handler(&type_handler_ulonglong);
+ else
+ set_handler(&type_handler_slonglong);
break;
case STRING_RESULT:
+ collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT);
max_length= MAX_BLOB_WIDTH - 1;
+ set_handler(&type_handler_long_blob);
break;
case DECIMAL_RESULT:
+ collation.set(&my_charset_numeric, DERIVATION_NUMERIC);
fix_char_length(DECIMAL_MAX_STR_LENGTH);
decimals= DECIMAL_MAX_SCALE;
+ set_handler(&type_handler_newdecimal);
break;
case ROW_RESULT: // Keep compiler happy
case TIME_RESULT:
@@ -5724,7 +5859,7 @@ bool Item_func_get_system_var::fix_length_and_dec()
case SHOW_SINT:
case SHOW_SLONG:
case SHOW_SLONGLONG:
- collation.set_numeric();
+ collation= DTCollation_numeric();
fix_char_length(MY_INT64_NUM_DECIMAL_DIGITS);
decimals=0;
break;
@@ -5735,9 +5870,8 @@ bool Item_func_get_system_var::fix_length_and_dec()
(char*) var->value_ptr(current_thd, var_type, &component) :
*(char**) var->value_ptr(current_thd, var_type, &component);
if (cptr)
- max_length= (uint32)system_charset_info->cset->numchars(system_charset_info,
- cptr,
- cptr + strlen(cptr));
+ max_length= (uint32) system_charset_info->numchars(cptr,
+ cptr + strlen(cptr));
mysql_mutex_unlock(&LOCK_global_system_variables);
collation.set(system_charset_info, DERIVATION_SYSCONST);
max_length*= system_charset_info->mbmaxlen;
@@ -5747,9 +5881,8 @@ bool Item_func_get_system_var::fix_length_and_dec()
{
mysql_mutex_lock(&LOCK_global_system_variables);
LEX_STRING *ls= ((LEX_STRING*)var->value_ptr(current_thd, var_type, &component));
- max_length= (uint32)system_charset_info->cset->numchars(system_charset_info,
- ls->str,
- ls->str + ls->length);
+ max_length= (uint32) system_charset_info->numchars(ls->str,
+ ls->str + ls->length);
mysql_mutex_unlock(&LOCK_global_system_variables);
collation.set(system_charset_info, DERIVATION_SYSCONST);
max_length*= system_charset_info->mbmaxlen;
@@ -5758,13 +5891,13 @@ bool Item_func_get_system_var::fix_length_and_dec()
break;
case SHOW_BOOL:
case SHOW_MY_BOOL:
- collation.set_numeric();
+ collation= DTCollation_numeric();
fix_char_length(1);
decimals=0;
break;
case SHOW_DOUBLE:
decimals= 6;
- collation.set_numeric();
+ collation= DTCollation_numeric();
fix_char_length(DBL_DIG + 6);
break;
default:
@@ -5810,11 +5943,12 @@ const Type_handler *Item_func_get_system_var::type_handler() const
case SHOW_SINT:
case SHOW_SLONG:
case SHOW_SLONGLONG:
+ return &type_handler_slonglong;
case SHOW_UINT:
case SHOW_ULONG:
case SHOW_ULONGLONG:
case SHOW_HA_ROWS:
- return &type_handler_longlong;
+ return &type_handler_ulonglong;
case SHOW_CHAR:
case SHOW_CHAR_PTR:
case SHOW_LEX_STRING:
@@ -5981,7 +6115,7 @@ bool Item_func_match::init_search(THD *thd, bool no_order)
{
DBUG_ENTER("Item_func_match::init_search");
- if (!table->file->get_table()) // the handler isn't opened yet
+ if (!table->file->is_open())
DBUG_RETURN(0);
/* Check if init_search() has been called before */
@@ -6289,14 +6423,38 @@ void Item_func_match::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("))"));
}
-longlong Item_func_bit_xor::val_int()
+
+class Func_handler_bit_xor_int_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
{
- DBUG_ASSERT(fixed == 1);
- ulonglong arg1= (ulonglong) args[0]->val_int();
- ulonglong arg2= (ulonglong) args[1]->val_int();
- if ((null_value= (args[0]->null_value || args[1]->null_value)))
- return 0;
- return (longlong) (arg1 ^ arg2);
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return item->arguments()[0]->to_longlong_null() ^
+ item->arguments()[1]->to_longlong_null();
+ }
+};
+
+
+class Func_handler_bit_xor_dec_to_ulonglong:
+ public Item_handled_func::Handler_ulonglong
+{
+public:
+ Longlong_null to_longlong_null(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ return VDec(item->arguments()[0]).to_xlonglong_null() ^
+ VDec(item->arguments()[1]).to_xlonglong_null();
+ }
+};
+
+
+bool Item_func_bit_xor::fix_length_and_dec()
+{
+ static const Func_handler_bit_xor_int_to_ulonglong ha_int_to_ull;
+ static const Func_handler_bit_xor_dec_to_ulonglong ha_dec_to_ull;
+ return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
}
@@ -6863,7 +7021,7 @@ longlong Item_func_nextval::val_int()
if (!(entry= ((SEQUENCE_LAST_VALUE*)
my_hash_search(&thd->sequences, (uchar*) key, length))))
{
- if (!(key= (char*) my_memdup(key, length, MYF(MY_WME))) ||
+ if (!(key= (char*) my_memdup(PSI_INSTRUMENT_ME, key, length, MYF(MY_WME))) ||
!(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length)))
{
/* EOM, error given */
diff --git a/sql/item_func.h b/sql/item_func.h
index b368e5872fe..6a4a9fa5dae 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1,7 +1,7 @@
#ifndef ITEM_FUNC_INCLUDED
#define ITEM_FUNC_INCLUDED
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2009, 2016, MariaDB
+ Copyright (c) 2009, 2020, 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
@@ -76,7 +76,7 @@ public:
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
NEG_FUNC, GSYSVAR_FUNC, IN_OPTIMIZER_FUNC, DYNCOL_FUNC,
- JSON_EXTRACT_FUNC,
+ JSON_EXTRACT_FUNC, JSON_VALID_FUNC,
CASE_SEARCHED_FUNC, // Used by ColumnStore/Spider
CASE_SIMPLE_FUNC // Used by ColumnStore/spider
};
@@ -188,12 +188,10 @@ public:
void signal_divide_by_null();
friend class udf_handler;
- Field *create_field_for_create_select(TABLE *table)
- { return tmp_table_field_from_field_type(table); }
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
+ { return tmp_table_field_from_field_type(root, table); }
Item *get_tmp_table_item(THD *thd);
- my_decimal *val_decimal(my_decimal *);
-
void fix_char_length_ulonglong(ulonglong max_char_length_arg)
{
ulonglong max_result_length= max_char_length_arg *
@@ -212,6 +210,7 @@ public:
void traverse_cond(Cond_traverser traverser,
void * arg, traverse_order order);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
// bool is_expensive_processor(void *arg);
// virtual bool is_expensive() { return 0; }
inline void raise_numeric_overflow(const char *type_name)
@@ -406,13 +405,13 @@ public:
class Item_real_func :public Item_func
{
public:
- Item_real_func(THD *thd): Item_func(thd) { collation.set_numeric(); }
+ Item_real_func(THD *thd): Item_func(thd) { collation= DTCollation_numeric(); }
Item_real_func(THD *thd, Item *a): Item_func(thd, a)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
Item_real_func(THD *thd, Item *a, Item *b): Item_func(thd, a, b)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
Item_real_func(THD *thd, List<Item> &list): Item_func(thd, list)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *decimal_value);
longlong val_int()
@@ -436,8 +435,7 @@ public:
Functions whose returned field type is determined at fix_fields() time.
*/
class Item_hybrid_func: public Item_func,
- public Type_handler_hybrid_field_type,
- public Type_geometry_attributes
+ public Type_handler_hybrid_field_type
{
protected:
bool fix_attributes(Item **item, uint nitems);
@@ -452,34 +450,24 @@ public:
:Item_func(thd, item), Type_handler_hybrid_field_type(item) { }
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
- Field::geometry_type get_geometry_type() const
- { return Type_geometry_attributes::get_geometry_type(); };
- void set_geometry_type(uint type)
- {
- Type_geometry_attributes::set_geometry_type(type);
- }
void fix_length_and_dec_long_or_longlong(uint char_length, bool unsigned_arg)
{
- collation.set_numeric();
+ collation= DTCollation_numeric();
unsigned_flag= unsigned_arg;
max_length= char_length;
-#if MARIADB_VERSION_ID < 100500
- set_handler(Type_handler::type_handler_long_or_longlong(char_length));
-#else
set_handler(Type_handler::type_handler_long_or_longlong(char_length,
unsigned_arg));
-#endif
}
void fix_length_and_dec_ulong_or_ulonglong_by_nbits(uint nbits)
{
uint digits= Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(nbits);
- collation.set_numeric();
+ collation= DTCollation_numeric();
unsigned_flag= true;
max_length= digits;
if (nbits > 32)
- set_handler(&type_handler_longlong);
+ set_handler(&type_handler_ulonglong);
else
- set_handler(&type_handler_long);
+ set_handler(&type_handler_ulong);
}
};
@@ -503,10 +491,48 @@ public:
to->length(0);
return true;
}
- virtual const Type_handler *return_type_handler() const= 0;
+ virtual const Type_handler *
+ return_type_handler(const Item_handled_func *item) const= 0;
+ virtual const Type_handler *
+ type_handler_for_create_select(const Item_handled_func *item) const
+ {
+ return return_type_handler(item);
+ }
virtual bool fix_length_and_dec(Item_handled_func *) const= 0;
};
+ class Handler_str: public Handler
+ {
+ public:
+ String *val_str_ascii(Item_handled_func *item, String *str) const
+ {
+ return item->Item::val_str_ascii(str);
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ StringBuffer<64> tmp;
+ String *res= item->val_str(&tmp);
+ return res ? item->double_from_string_with_check(res) : 0.0;
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ DBUG_ASSERT(item->is_fixed());
+ StringBuffer<22> tmp;
+ String *res= item->val_str(&tmp);
+ return res ? item->longlong_from_string_with_check(res) : 0;
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ return item->val_decimal_from_string(to);
+ }
+ bool get_date(THD *thd, Item_handled_func *item, MYSQL_TIME *to,
+ date_mode_t fuzzydate) const
+ {
+ return item->get_date_from_string(thd, to, fuzzydate);
+ }
+ };
+
/**
Abstract class for functions returning TIME, DATE, DATETIME or string values,
whose data type depends on parameters and is set at fix_fields time.
@@ -529,10 +555,15 @@ public:
class Handler_temporal_string: public Handler_temporal
{
public:
- const Type_handler *return_type_handler() const
+ const Type_handler *return_type_handler(const Item_handled_func *) const
{
return &type_handler_string;
}
+ const Type_handler *
+ type_handler_for_create_select(const Item_handled_func *item) const
+ {
+ return return_type_handler(item)->type_handler_for_tmp_table(item);
+ }
double val_real(Item_handled_func *item) const
{
return Temporal_hybrid(item).to_double();
@@ -555,7 +586,7 @@ public:
class Handler_date: public Handler_temporal
{
public:
- const Type_handler *return_type_handler() const
+ const Type_handler *return_type_handler(const Item_handled_func *) const
{
return &type_handler_newdate;
}
@@ -586,7 +617,7 @@ public:
class Handler_time: public Handler_temporal
{
public:
- const Type_handler *return_type_handler() const
+ const Type_handler *return_type_handler(const Item_handled_func *) const
{
return &type_handler_time2;
}
@@ -616,7 +647,7 @@ public:
class Handler_datetime: public Handler_temporal
{
public:
- const Type_handler *return_type_handler() const
+ const Type_handler *return_type_handler(const Item_handled_func *) const
{
return &type_handler_datetime2;
}
@@ -639,6 +670,87 @@ public:
};
+ class Handler_int: public Handler
+ {
+ public:
+ String *val_str(Item_handled_func *item, String *to) const
+ {
+ longlong nr= val_int(item);
+ if (item->null_value)
+ return 0;
+ to->set_int(nr, item->unsigned_flag, item->collation.collation);
+ return to;
+ }
+ String *val_str_ascii(Item_handled_func *item, String *to) const
+ {
+ return item->Item::val_str_ascii(to);
+ }
+ double val_real(Item_handled_func *item) const
+ {
+ return item->unsigned_flag ? (double) ((ulonglong) val_int(item)) :
+ (double) val_int(item);
+ }
+ my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const
+ {
+ return item->val_decimal_from_int(to);
+ }
+ bool get_date(THD *thd, Item_handled_func *item,
+ MYSQL_TIME *to, date_mode_t fuzzydate) const
+ {
+ return item->get_date_from_int(thd, to, fuzzydate);
+ }
+ longlong val_int(Item_handled_func *item) const
+ {
+ Longlong_null tmp= to_longlong_null(item);
+ item->null_value= tmp.is_null();
+ return tmp.value();
+ }
+ virtual Longlong_null to_longlong_null(Item_handled_func *item) const= 0;
+ };
+
+ class Handler_slong: public Handler_int
+ {
+ public:
+ const Type_handler *return_type_handler(const Item_handled_func *item) const
+ {
+ return &type_handler_slong;
+ }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->unsigned_flag= false;
+ item->collation= DTCollation_numeric();
+ item->fix_char_length(11);
+ return false;
+ }
+ };
+
+ class Handler_slong2: public Handler_slong
+ {
+ public:
+ bool fix_length_and_dec(Item_handled_func *func) const
+ {
+ bool rc= Handler_slong::fix_length_and_dec(func);
+ func->max_length= 2;
+ return rc;
+ }
+ };
+
+ class Handler_ulonglong: public Handler_int
+ {
+ public:
+ const Type_handler *return_type_handler(const Item_handled_func *item) const
+ {
+ return &type_handler_ulonglong;
+ }
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ item->unsigned_flag= true;
+ item->collation= DTCollation_numeric();
+ item->fix_char_length(21);
+ return false;
+ }
+ };
+
protected:
const Handler *m_func_handler;
public:
@@ -652,7 +764,15 @@ public:
}
const Type_handler *type_handler() const
{
- return m_func_handler->return_type_handler();
+ return m_func_handler->return_type_handler(this);
+ }
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
+ {
+ DBUG_ASSERT(fixed);
+ const Type_handler *h= m_func_handler->type_handler_for_create_select(this);
+ return h->make_and_init_table_field(root, &name,
+ Record_addr(maybe_null),
+ *this, table);
}
String *val_str(String *to)
{
@@ -767,19 +887,19 @@ public:
public:
Item_func_hybrid_field_type(THD *thd):
Item_hybrid_func(thd)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
Item_func_hybrid_field_type(THD *thd, Item *a):
Item_hybrid_func(thd, a)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
Item_func_hybrid_field_type(THD *thd, Item *a, Item *b):
Item_hybrid_func(thd, a, b)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
Item_func_hybrid_field_type(THD *thd, Item *a, Item *b, Item *c):
Item_hybrid_func(thd, a, b, c)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
Item_func_hybrid_field_type(THD *thd, List<Item> &list):
Item_hybrid_func(thd, list)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
double val_real()
{
@@ -919,6 +1039,7 @@ public:
Item_func_case_expression(THD *thd, List<Item> &list):
Item_func_hybrid_field_type(thd, list)
{ }
+ bool find_not_null_fields(table_map allowed) { return false; }
};
@@ -980,7 +1101,12 @@ public:
/* Base class for operations like '+', '-', '*' */
class Item_num_op :public Item_func_numhybrid
{
- public:
+protected:
+ bool check_arguments() const
+ {
+ return false; // Checked by aggregate_for_num_op()
+ }
+public:
Item_num_op(THD *thd, Item *a, Item *b): Item_func_numhybrid(thd, a, b) {}
virtual void result_precision()= 0;
@@ -991,7 +1117,7 @@ class Item_num_op :public Item_func_numhybrid
bool fix_type_handler(const Type_aggregator *aggregator);
void fix_length_and_dec_double()
{
- count_real_length(args, arg_count);
+ aggregate_numeric_attributes_real(args, arg_count);
max_length= float_length(decimals);
}
void fix_length_and_dec_decimal()
@@ -1028,22 +1154,26 @@ public:
Min signed = -9,223,372,036,854,775,808 = 19 digits, 20 characters
*/
Item_int_func(THD *thd): Item_func(thd)
- { collation.set_numeric(); fix_char_length(21); }
+ { collation= DTCollation_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, Item *a): Item_func(thd, a)
- { collation.set_numeric(); fix_char_length(21); }
+ { collation= DTCollation_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, Item *a, Item *b): Item_func(thd, a, b)
- { collation.set_numeric(); fix_char_length(21); }
+ { collation= DTCollation_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b, c)
- { collation.set_numeric(); fix_char_length(21); }
+ { collation= DTCollation_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, Item *a, Item *b, Item *c, Item *d):
Item_func(thd, a, b, c, d)
- { collation.set_numeric(); fix_char_length(21); }
+ { collation= DTCollation_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, List<Item> &list): Item_func(thd, list)
- { collation.set_numeric(); fix_char_length(21); }
+ { collation= DTCollation_numeric(); fix_char_length(21); }
Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item)
- { collation.set_numeric(); }
+ { collation= DTCollation_numeric(); }
double val_real();
String *val_str(String*str);
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ return val_decimal_from_int(decimal_value);
+ }
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{ return get_date_from_int(thd, ltime, fuzzydate); }
const Type_handler *type_handler() const= 0;
@@ -1060,7 +1190,12 @@ public:
Item_long_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {}
Item_long_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { }
Item_long_func(THD *thd, Item_long_func *item) :Item_int_func(thd, item) {}
- const Type_handler *type_handler() const { return &type_handler_long; }
+ const Type_handler *type_handler() const
+ {
+ if (unsigned_flag)
+ return &type_handler_ulong;
+ return &type_handler_slong;
+ }
bool fix_length_and_dec() { max_length= 11; return FALSE; }
};
@@ -1072,7 +1207,7 @@ public:
{}
longlong val_int();
bool fix_length_and_dec();
- const Type_handler *type_handler() const { return &type_handler_long; }
+ const Type_handler *type_handler() const { return &type_handler_slong; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_hash>(thd, this); }
const char *func_name() const { return "<hash>"; }
@@ -1089,7 +1224,12 @@ public:
Item_int_func(thd, a, b, c, d) {}
Item_longlong_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { }
Item_longlong_func(THD *thd, Item_longlong_func *item) :Item_int_func(thd, item) {}
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const
+ {
+ if (unsigned_flag)
+ return &type_handler_ulonglong;
+ return &type_handler_slonglong;
+ }
};
@@ -1157,7 +1297,10 @@ public:
}
const char *func_name() const { return "cast_as_signed"; }
const Type_handler *type_handler() const
- { return type_handler_long_or_longlong(); }
+ {
+ return Type_handler::type_handler_long_or_longlong(max_char_length(),
+ false);
+ }
longlong val_int()
{
longlong value= args[0]->val_int_signed_typecast();
@@ -1216,8 +1359,8 @@ public:
const Type_handler *type_handler() const
{
if (max_char_length() <= MY_INT32_NUM_DECIMAL_DIGITS - 1)
- return &type_handler_long;
- return &type_handler_longlong;
+ return &type_handler_ulong;
+ return &type_handler_ulonglong;
}
longlong val_int()
{
@@ -1244,7 +1387,7 @@ public:
:Item_func(thd, a)
{
decimals= (uint8) dec;
- collation.set_numeric();
+ collation= DTCollation_numeric();
fix_char_length(my_decimal_precision_to_length_no_truncation(len, dec,
unsigned_flag));
}
@@ -1894,6 +2037,10 @@ class Item_func_min_max :public Item_hybrid_func
String tmp_value;
int cmp_sign;
protected:
+ bool check_arguments() const
+ {
+ return false; // Checked by aggregate_for_min_max()
+ }
bool fix_attributes(Item **item, uint nitems);
public:
Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg):
@@ -2011,9 +2158,7 @@ public:
const Type_handler *type_handler() const { return args[0]->type_handler(); }
bool fix_length_and_dec()
{
- collation= args[0]->collation;
- max_length= args[0]->max_length;
- decimals=args[0]->decimals;
+ Type_std_attributes::set(*args[0]);
return FALSE;
}
Item *get_copy(THD *thd)
@@ -2083,6 +2228,10 @@ public:
not_null_tables_cache= 0;
return false;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ return this; }
bool const_item() const { return true; }
@@ -2187,84 +2336,99 @@ public:
/* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */
-class Item_func_bit: public Item_longlong_func
+class Item_func_bit_operator: public Item_handled_func
{
bool check_arguments() const
{ return check_argument_types_can_return_int(0, arg_count); }
+protected:
+ bool fix_length_and_dec_op1_std(const Handler *ha_int, const Handler *ha_dec)
+ {
+ set_func_handler(args[0]->cmp_type() == INT_RESULT ? ha_int : ha_dec);
+ return m_func_handler->fix_length_and_dec(this);
+ }
+ bool fix_length_and_dec_op2_std(const Handler *ha_int, const Handler *ha_dec)
+ {
+ set_func_handler(args[0]->cmp_type() == INT_RESULT &&
+ args[1]->cmp_type() == INT_RESULT ? ha_int : ha_dec);
+ return m_func_handler->fix_length_and_dec(this);
+ }
public:
- Item_func_bit(THD *thd, Item *a, Item *b): Item_longlong_func(thd, a, b) {}
- Item_func_bit(THD *thd, Item *a): Item_longlong_func(thd, a) {}
- bool fix_length_and_dec() { unsigned_flag= 1; return FALSE; }
-
- virtual inline void print(String *str, enum_query_type query_type)
+ Item_func_bit_operator(THD *thd, Item *a)
+ :Item_handled_func(thd, a) {}
+ Item_func_bit_operator(THD *thd, Item *a, Item *b)
+ :Item_handled_func(thd, a, b) {}
+ void print(String *str, enum_query_type query_type)
{
print_op(str, query_type);
}
bool need_parentheses_in_default() { return true; }
};
-class Item_func_bit_or :public Item_func_bit
+class Item_func_bit_or :public Item_func_bit_operator
{
public:
- Item_func_bit_or(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
- longlong val_int();
+ Item_func_bit_or(THD *thd, Item *a, Item *b)
+ :Item_func_bit_operator(thd, a, b) {}
+ bool fix_length_and_dec();
const char *func_name() const { return "|"; }
enum precedence precedence() const { return BITOR_PRECEDENCE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_bit_or>(thd, this); }
};
-class Item_func_bit_and :public Item_func_bit
+class Item_func_bit_and :public Item_func_bit_operator
{
public:
- Item_func_bit_and(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
- longlong val_int();
+ Item_func_bit_and(THD *thd, Item *a, Item *b)
+ :Item_func_bit_operator(thd, a, b) {}
+ bool fix_length_and_dec();
const char *func_name() const { return "&"; }
enum precedence precedence() const { return BITAND_PRECEDENCE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_bit_and>(thd, this); }
};
-class Item_func_bit_count :public Item_long_func
+class Item_func_bit_count :public Item_handled_func
{
bool check_arguments() const
{ return args[0]->check_type_can_return_int(func_name()); }
public:
- Item_func_bit_count(THD *thd, Item *a): Item_long_func(thd, a) {}
- longlong val_int();
+ Item_func_bit_count(THD *thd, Item *a): Item_handled_func(thd, a) {}
const char *func_name() const { return "bit_count"; }
- bool fix_length_and_dec() { max_length=2; return FALSE; }
+ bool fix_length_and_dec();
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_bit_count>(thd, this); }
};
-class Item_func_shift_left :public Item_func_bit
+class Item_func_shift_left :public Item_func_bit_operator
{
public:
- Item_func_shift_left(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
- longlong val_int();
+ Item_func_shift_left(THD *thd, Item *a, Item *b)
+ :Item_func_bit_operator(thd, a, b) {}
+ bool fix_length_and_dec();
const char *func_name() const { return "<<"; }
enum precedence precedence() const { return SHIFT_PRECEDENCE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_shift_left>(thd, this); }
};
-class Item_func_shift_right :public Item_func_bit
+class Item_func_shift_right :public Item_func_bit_operator
{
public:
- Item_func_shift_right(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
- longlong val_int();
+ Item_func_shift_right(THD *thd, Item *a, Item *b)
+ :Item_func_bit_operator(thd, a, b) {}
+ bool fix_length_and_dec();
const char *func_name() const { return ">>"; }
enum precedence precedence() const { return SHIFT_PRECEDENCE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_shift_right>(thd, this); }
};
-class Item_func_bit_neg :public Item_func_bit
+class Item_func_bit_neg :public Item_func_bit_operator
{
public:
- Item_func_bit_neg(THD *thd, Item *a): Item_func_bit(thd, a) {}
- longlong val_int();
+ Item_func_bit_neg(THD *thd, Item *a): Item_func_bit_operator(thd, a) {}
+ bool fix_length_and_dec();
const char *func_name() const { return "~"; }
enum precedence precedence() const { return NEG_PRECEDENCE; }
void print(String *str, enum_query_type query_type)
@@ -2455,6 +2619,10 @@ public:
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
bool is_expensive() { return 1; }
virtual void print(String *str, enum_query_type query_type);
bool check_vcol_func_processor(void *arg)
@@ -2511,8 +2679,17 @@ public:
Item_udf_func(thd, udf_arg, list) {}
longlong val_int();
double val_real() { return (double) Item_func_udf_int::val_int(); }
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ return val_decimal_from_int(decimal_value);
+ }
String *val_str(String *str);
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const
+ {
+ if (unsigned_flag)
+ return &type_handler_ulonglong;
+ return &type_handler_slonglong;
+ }
bool fix_length_and_dec() { decimals= 0; max_length= 21; return FALSE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_udf_int>(thd, this); }
@@ -2560,15 +2737,15 @@ public:
char *end_not_used;
String *res;
res= val_str(&str_value);
- return res ? my_strntod(res->charset(),(char*) res->ptr(),
- res->length(), &end_not_used, &err_not_used) : 0.0;
+ return res ? res->charset()->strntod((char*) res->ptr(), res->length(),
+ &end_not_used, &err_not_used) : 0.0;
}
longlong val_int()
{
int err_not_used;
String *res; res=val_str(&str_value);
- return res ? my_strntoll(res->charset(),res->ptr(),res->length(),10,
- (char**) 0, &err_not_used) : (longlong) 0;
+ return res ? res->charset()->strntoll(res->ptr(),res->length(),10,
+ (char**) 0, &err_not_used) : (longlong) 0;
}
my_decimal *val_decimal(my_decimal *dec_buf)
{
@@ -2604,7 +2781,7 @@ public:
Item_int_func(thd) {}
Item_func_udf_int(THD *thd, udf_func *udf_arg, List<Item> &list):
Item_int_func(thd, list) {}
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; }
};
@@ -2616,7 +2793,7 @@ public:
Item_int_func(thd) {}
Item_func_udf_decimal(THD *thd, udf_func *udf_arg, List<Item> &list):
Item_int_func(thd, list) {}
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; }
};
@@ -2640,19 +2817,13 @@ public:
void mysql_ull_cleanup(THD *thd);
void mysql_ull_set_explicit_lock_duration(THD *thd);
-class Item_func_get_lock :public Item_long_func
+
+class Item_func_lock :public Item_long_func
{
- bool check_arguments() const
- {
- return args[0]->check_type_general_purpose_string(func_name()) ||
- args[1]->check_type_can_return_real(func_name());
- }
- String value;
public:
- Item_func_get_lock(THD *thd, Item *a, Item *b) :Item_long_func(thd, a, b) {}
- longlong val_int();
- const char *func_name() const { return "get_lock"; }
- bool fix_length_and_dec() { max_length=1; maybe_null=1; return FALSE; }
+ Item_func_lock(THD *thd): Item_long_func(thd) { }
+ Item_func_lock(THD *thd, Item *a): Item_long_func(thd, a) {}
+ Item_func_lock(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {}
table_map used_tables() const
{
return used_tables_cache | RAND_TABLE_BIT;
@@ -2663,34 +2834,54 @@ class Item_func_get_lock :public Item_long_func
{
return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
}
- Item *get_copy(THD *thd)
+};
+
+
+class Item_func_get_lock final :public Item_func_lock
+{
+ bool check_arguments() const
+ {
+ return args[0]->check_type_general_purpose_string(func_name()) ||
+ args[1]->check_type_can_return_real(func_name());
+ }
+ String value;
+ public:
+ Item_func_get_lock(THD *thd, Item *a, Item *b) :Item_func_lock(thd, a, b) {}
+ longlong val_int() final;
+ const char *func_name() const final { return "get_lock"; }
+ bool fix_length_and_dec() { max_length= 1; maybe_null= 1; return FALSE; }
+ Item *get_copy(THD *thd) final
{ return get_item_copy<Item_func_get_lock>(thd, this); }
};
-class Item_func_release_lock :public Item_long_func
+
+class Item_func_release_all_locks final :public Item_func_lock
+{
+public:
+ Item_func_release_all_locks(THD *thd): Item_func_lock(thd)
+ { unsigned_flag= 1; }
+ longlong val_int() final;
+ const char *func_name() const final { return "release_all_locks"; }
+ Item *get_copy(THD *thd) final
+ { return get_item_copy<Item_func_release_all_locks>(thd, this); }
+};
+
+
+class Item_func_release_lock final :public Item_func_lock
{
bool check_arguments() const
{ return args[0]->check_type_general_purpose_string(func_name()); }
String value;
public:
- Item_func_release_lock(THD *thd, Item *a): Item_long_func(thd, a) {}
- longlong val_int();
+ Item_func_release_lock(THD *thd, Item *a): Item_func_lock(thd, a) {}
+ longlong val_int() final;
const char *func_name() const { return "release_lock"; }
bool fix_length_and_dec() { max_length= 1; maybe_null= 1; return FALSE; }
- table_map used_tables() const
- {
- return used_tables_cache | RAND_TABLE_BIT;
- }
- bool const_item() const { return 0; }
- bool is_expensive() { return 1; }
- bool check_vcol_func_processor(void *arg)
- {
- return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE);
- }
- Item *get_copy(THD *thd)
+ Item *get_copy(THD *thd) final
{ return get_item_copy<Item_func_release_lock>(thd, this); }
};
+
/* replication functions */
class Item_master_pos_wait :public Item_longlong_func
@@ -2769,10 +2960,15 @@ public:
Item_func_user_var(THD *thd, Item_func_user_var *item)
:Item_hybrid_func(thd, item),
m_var_entry(item->m_var_entry), name(item->name) { }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
- const Tmp_field_param *param);
- Field *create_field_for_create_select(TABLE *table)
- { return create_table_field_from_handler(table); }
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
+ const Tmp_field_param *param)
+ {
+ DBUG_ASSERT(fixed);
+ return create_tmp_field_ex_from_handler(root, table, src, param,
+ type_handler());
+ }
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
+ { return create_table_field_from_handler(root, table); }
bool check_vcol_func_processor(void *arg);
bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
@@ -2947,7 +3143,7 @@ public:
{
return 0;
}
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(0);
@@ -3066,6 +3262,10 @@ public:
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
bool fix_fields(THD *thd, Item **ref);
bool eq(const Item *, bool binary_cmp) const;
/* The following should be safe, even if we compare doubles */
@@ -3119,11 +3319,12 @@ private:
};
-class Item_func_bit_xor : public Item_func_bit
+class Item_func_bit_xor : public Item_func_bit_operator
{
public:
- Item_func_bit_xor(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {}
- longlong val_int();
+ Item_func_bit_xor(THD *thd, Item *a, Item *b)
+ :Item_func_bit_operator(thd, a, b) {}
+ bool fix_length_and_dec();
const char *func_name() const { return "^"; }
enum precedence precedence() const { return BITXOR_PRECEDENCE; }
Item *get_copy(THD *thd)
@@ -3199,12 +3400,15 @@ public:
set(handler, 0, 0);
}
const Type_handler *type_handler() const { return m_type_handler; }
- Item *create_typecast_item(THD *thd, Item *item, CHARSET_INFO *cs= NULL)
+ Item *create_typecast_item(THD *thd, Item *item,
+ CHARSET_INFO *cs= NULL) const
{
return m_type_handler->
create_typecast_item(thd, item,
Type_cast_attributes(length(), dec(), cs));
}
+ Item *create_typecast_item_or_error(THD *thd, Item *item,
+ CHARSET_INFO *cs= NULL) const;
};
@@ -3266,13 +3470,13 @@ public:
const Type_handler *type_handler() const;
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param);
- Field *create_field_for_create_select(TABLE *table)
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
{
return result_type() != STRING_RESULT ?
sp_result_field :
- create_table_field_from_handler(table);
+ create_table_field_from_handler(root, table);
}
void make_send_field(THD *thd, Send_field *tmp_field);
@@ -3366,6 +3570,10 @@ public:
}
bool excl_dep_on_grouping_fields(st_select_lex *sel)
{ return false; }
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
};
@@ -3470,6 +3678,10 @@ public:
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
bool const_item() const { return 0; }
void evaluate_sideeffects();
void update_used_tables()
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 682051f2448..f1a4ac4ef8a 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -38,6 +38,8 @@
#ifdef HAVE_SPATIAL
#include <m_ctype.h>
#include "opt_range.h"
+#include "item_geofunc.h"
+#include "item_create.h"
bool Item_geometry_func::fix_length_and_dec()
@@ -324,12 +326,6 @@ String *Item_func_geometry_type::val_str_ascii(String *str)
}
-Field::geometry_type Item_func_envelope::get_geometry_type() const
-{
- return Field::GEOM_POLYGON;
-}
-
-
String *Item_func_envelope::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -480,12 +476,6 @@ mem_error:
}
-Field::geometry_type Item_func_centroid::get_geometry_type() const
-{
- return Field::GEOM_POINT;
-}
-
-
String *Item_func_centroid::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -903,12 +893,6 @@ err:
*/
-Field::geometry_type Item_func_point::get_geometry_type() const
-{
- return Field::GEOM_POINT;
-}
-
-
String *Item_func_point::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -1082,9 +1066,17 @@ Item_func_spatial_rel::get_mm_leaf(RANGE_OPT_PARAM *param,
if (param->using_real_indexes &&
!field->optimize_range(param->real_keynr[key_part->key],
key_part->part))
- DBUG_RETURN(0);
+ DBUG_RETURN(0);
- if (value->save_in_field_no_warnings(field, 1))
+ Field_geom *field_geom= dynamic_cast<Field_geom*>(field);
+ DBUG_ASSERT(field_geom);
+ const Type_handler_geometry *sav_geom_type= field_geom->type_handler_geom();
+ // We have to be able to store all sorts of spatial features here
+ field_geom->set_type_handler(&type_handler_geometry);
+ bool rc= value->save_in_field_no_warnings(field, 1);
+ field_geom->set_type_handler(sav_geom_type);
+
+ if (rc)
DBUG_RETURN(&sel_arg_impossible); // Bad GEOMETRY value
DBUG_ASSERT(!field->real_maybe_null()); // SPATIAL keys do not support NULL
@@ -2636,12 +2628,6 @@ mem_error:
}
-Field::geometry_type Item_func_pointonsurface::get_geometry_type() const
-{
- return Field::GEOM_POINT;
-}
-
-
#ifndef DBUG_OFF
longlong Item_func_gis_debug::val_int()
{
@@ -2650,4 +2636,1274 @@ longlong Item_func_gis_debug::val_int()
}
#endif
+
+/**********************************************************************/
+
+
+class Create_func_area : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_area(thd, arg1);
+ }
+
+ static Create_func_area s_singleton;
+
+protected:
+ Create_func_area() {}
+ virtual ~Create_func_area() {}
+};
+
+
+class Create_func_as_wkb : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_as_wkb(thd, arg1);
+ }
+
+ static Create_func_as_wkb s_singleton;
+
+protected:
+ Create_func_as_wkb() {}
+ virtual ~Create_func_as_wkb() {}
+};
+
+
+class Create_func_as_wkt : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_as_wkt(thd, arg1);
+ }
+
+ static Create_func_as_wkt s_singleton;
+
+protected:
+ Create_func_as_wkt() {}
+ virtual ~Create_func_as_wkt() {}
+};
+
+
+
+class Create_func_centroid : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_centroid(thd, arg1);
+ }
+
+ static Create_func_centroid s_singleton;
+
+protected:
+ Create_func_centroid() {}
+ virtual ~Create_func_centroid() {}
+};
+
+
+class Create_func_convexhull : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_convexhull(thd, arg1);
+ }
+
+ static Create_func_convexhull s_singleton;
+
+protected:
+ Create_func_convexhull() {}
+ virtual ~Create_func_convexhull() {}
+};
+
+
+class Create_func_pointonsurface : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_pointonsurface(thd, arg1);
+ }
+
+ static Create_func_pointonsurface s_singleton;
+
+protected:
+ Create_func_pointonsurface() {}
+ virtual ~Create_func_pointonsurface() {}
+};
+
+
+class Create_func_mbr_contains : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
+ Item_func::SP_CONTAINS_FUNC);
+ }
+
+ static Create_func_mbr_contains s_singleton;
+
+protected:
+ Create_func_mbr_contains() {}
+ virtual ~Create_func_mbr_contains() {}
+};
+
+
+class Create_func_contains : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_CONTAINS_FUNC);
+ }
+ static Create_func_contains s_singleton;
+
+protected:
+ Create_func_contains() {}
+ virtual ~Create_func_contains() {}
+};
+
+
+class Create_func_crosses : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_CROSSES_FUNC);
+ }
+ static Create_func_crosses s_singleton;
+
+protected:
+ Create_func_crosses() {}
+ virtual ~Create_func_crosses() {}
+};
+
+
+class Create_func_dimension : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_dimension(thd, arg1);
+ }
+
+ static Create_func_dimension s_singleton;
+
+protected:
+ Create_func_dimension() {}
+ virtual ~Create_func_dimension() {}
+};
+
+
+class Create_func_mbr_disjoint : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
+ Item_func::SP_DISJOINT_FUNC);
+ }
+
+ static Create_func_mbr_disjoint s_singleton;
+
+protected:
+ Create_func_mbr_disjoint() {}
+ virtual ~Create_func_mbr_disjoint() {}
+};
+
+
+class Create_func_disjoint : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_DISJOINT_FUNC);
+ }
+ static Create_func_disjoint s_singleton;
+
+protected:
+ Create_func_disjoint() {}
+ virtual ~Create_func_disjoint() {}
+};
+
+
+class Create_func_distance : public Create_func_arg2
+{
+public:
+ Item* create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_distance(thd, arg1, arg2);
+ }
+
+ static Create_func_distance s_singleton;
+
+protected:
+ Create_func_distance() {}
+ virtual ~Create_func_distance() {}
+};
+
+
+
+class Create_func_endpoint : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_spatial_decomp(thd, arg1,
+ Item_func::SP_ENDPOINT);
+ }
+
+ static Create_func_endpoint s_singleton;
+
+protected:
+ Create_func_endpoint() {}
+ virtual ~Create_func_endpoint() {}
+};
+
+
+class Create_func_envelope : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_envelope(thd, arg1);
+ }
+
+ static Create_func_envelope s_singleton;
+
+protected:
+ Create_func_envelope() {}
+ virtual ~Create_func_envelope() {}
+};
+
+class Create_func_boundary : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_boundary(thd, arg1);
+ }
+
+ static Create_func_boundary s_singleton;
+
+protected:
+ Create_func_boundary() {}
+ virtual ~Create_func_boundary() {}
+};
+
+
+class Create_func_mbr_equals : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
+ Item_func::SP_EQUALS_FUNC);
+ }
+
+ static Create_func_mbr_equals s_singleton;
+
+protected:
+ Create_func_mbr_equals() {}
+ virtual ~Create_func_mbr_equals() {}
+};
+
+
+class Create_func_equals : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_EQUALS_FUNC);
+ }
+
+ static Create_func_equals s_singleton;
+
+protected:
+ Create_func_equals() {}
+ virtual ~Create_func_equals() {}
+};
+
+
+class Create_func_exteriorring : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_spatial_decomp(thd, arg1,
+ Item_func::SP_EXTERIORRING);
+ }
+
+ static Create_func_exteriorring s_singleton;
+
+protected:
+ Create_func_exteriorring() {}
+ virtual ~Create_func_exteriorring() {}
+};
+
+
+
+class Create_func_geometry_from_text : public Create_native_func
+{
+public:
+ Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_geometry_from_text s_singleton;
+
+protected:
+ Create_func_geometry_from_text() {}
+ virtual ~Create_func_geometry_from_text() {}
+};
+
+
+Item*
+Create_func_geometry_from_text::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_text(thd, param_1);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_text(thd, param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+class Create_func_geometry_from_wkb : public Create_native_func
+{
+public:
+ Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_geometry_from_wkb s_singleton;
+
+protected:
+ Create_func_geometry_from_wkb() {}
+ virtual ~Create_func_geometry_from_wkb() {}
+};
+
+
+Item*
+Create_func_geometry_from_wkb::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *param_1= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_wkb(thd, param_1);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_wkb(thd, param_1, param_2);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+class Create_func_geometry_from_json : public Create_native_func
+{
+public:
+ Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_geometry_from_json s_singleton;
+
+protected:
+ Create_func_geometry_from_json() {}
+ virtual ~Create_func_geometry_from_json() {}
+};
+
+
+Item*
+Create_func_geometry_from_json::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *json= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_json(thd, json);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *json= item_list->pop();
+ Item *options= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_json(thd, json, options);
+ break;
+ }
+ case 3:
+ {
+ Item *json= item_list->pop();
+ Item *options= item_list->pop();
+ Item *srid= item_list->pop();
+ func= new (thd->mem_root) Item_func_geometry_from_json(thd, json, options,
+ srid);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+class Create_func_as_geojson : public Create_native_func
+{
+public:
+ Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list);
+
+ static Create_func_as_geojson s_singleton;
+
+protected:
+ Create_func_as_geojson() {}
+ virtual ~Create_func_as_geojson() {}
+};
+
+
+Item*
+Create_func_as_geojson::create_native(THD *thd, LEX_CSTRING *name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ switch (arg_count) {
+ case 1:
+ {
+ Item *geom= item_list->pop();
+ func= new (thd->mem_root) Item_func_as_geojson(thd, geom);
+ thd->lex->uncacheable(UNCACHEABLE_RAND);
+ break;
+ }
+ case 2:
+ {
+ Item *geom= item_list->pop();
+ Item *max_dec= item_list->pop();
+ func= new (thd->mem_root) Item_func_as_geojson(thd, geom, max_dec);
+ break;
+ }
+ case 3:
+ {
+ Item *geom= item_list->pop();
+ Item *max_dec= item_list->pop();
+ Item *options= item_list->pop();
+ func= new (thd->mem_root) Item_func_as_geojson(thd, geom, max_dec, options);
+ break;
+ }
+ default:
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+class Create_func_geometry_type : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_geometry_type(thd, arg1);
+ }
+
+ static Create_func_geometry_type s_singleton;
+
+protected:
+ Create_func_geometry_type() {}
+ virtual ~Create_func_geometry_type() {}
+};
+
+
+class Create_func_geometryn : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_decomp_n(thd, arg1, arg2,
+ Item_func::SP_GEOMETRYN);
+ }
+
+ static Create_func_geometryn s_singleton;
+
+protected:
+ Create_func_geometryn() {}
+ virtual ~Create_func_geometryn() {}
+};
+
+
+#if !defined(DBUG_OFF)
+class Create_func_gis_debug : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_gis_debug(thd, arg1);
+ }
+
+ static Create_func_gis_debug s_singleton;
+
+protected:
+ Create_func_gis_debug() {}
+ virtual ~Create_func_gis_debug() {}
+};
+#endif
+
+
+class Create_func_glength : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_glength(thd, arg1);
+ }
+
+ static Create_func_glength s_singleton;
+
+protected:
+ Create_func_glength() {}
+ virtual ~Create_func_glength() {}
+};
+
+
+class Create_func_interiorringn : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_decomp_n(thd, arg1, arg2,
+ Item_func::SP_INTERIORRINGN);
+ }
+
+ static Create_func_interiorringn s_singleton;
+
+protected:
+ Create_func_interiorringn() {}
+ virtual ~Create_func_interiorringn() {}
+};
+
+
+class Create_func_relate : public Create_func_arg3
+{
+public:
+ Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) override
+ {
+ return new (thd->mem_root) Item_func_spatial_relate(thd, arg1, arg2, arg3);
+ }
+
+ static Create_func_relate s_singleton;
+
+protected:
+ Create_func_relate() {}
+ virtual ~Create_func_relate() {}
+};
+
+
+class Create_func_mbr_intersects : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
+ Item_func::SP_INTERSECTS_FUNC);
+ }
+
+ static Create_func_mbr_intersects s_singleton;
+
+protected:
+ Create_func_mbr_intersects() {}
+ virtual ~Create_func_mbr_intersects() {}
+};
+
+
+class Create_func_intersects : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_INTERSECTS_FUNC);
+ }
+
+ static Create_func_intersects s_singleton;
+
+protected:
+ Create_func_intersects() {}
+ virtual ~Create_func_intersects() {}
+};
+
+
+class Create_func_intersection : public Create_func_arg2
+{
+public:
+ Item* create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
+ Gcalc_function::op_intersection);
+ }
+
+ static Create_func_intersection s_singleton;
+
+protected:
+ Create_func_intersection() {}
+ virtual ~Create_func_intersection() {}
+};
+
+
+class Create_func_difference : public Create_func_arg2
+{
+public:
+ Item* create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
+ Gcalc_function::op_difference);
+ }
+
+ static Create_func_difference s_singleton;
+
+protected:
+ Create_func_difference() {}
+ virtual ~Create_func_difference() {}
+};
+
+
+class Create_func_union : public Create_func_arg2
+{
+public:
+ Item* create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
+ Gcalc_function::op_union);
+ }
+
+ static Create_func_union s_singleton;
+
+protected:
+ Create_func_union() {}
+ virtual ~Create_func_union() {}
+};
+
+
+class Create_func_symdifference : public Create_func_arg2
+{
+public:
+ Item* create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_operation(thd, arg1, arg2,
+ Gcalc_function::op_symdifference);
+ }
+
+ static Create_func_symdifference s_singleton;
+
+protected:
+ Create_func_symdifference() {}
+ virtual ~Create_func_symdifference() {}
+};
+
+
+class Create_func_buffer : public Create_func_arg2
+{
+public:
+ Item* create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_buffer(thd, arg1, arg2);
+ }
+
+ static Create_func_buffer s_singleton;
+
+protected:
+ Create_func_buffer() {}
+ virtual ~Create_func_buffer() {}
+};
+
+
+class Create_func_isclosed : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_isclosed(thd, arg1);
+ }
+
+ static Create_func_isclosed s_singleton;
+
+protected:
+ Create_func_isclosed() {}
+ virtual ~Create_func_isclosed() {}
+};
+
+
+class Create_func_isring : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_isring(thd, arg1);
+ }
+
+ static Create_func_isring s_singleton;
+
+protected:
+ Create_func_isring() {}
+ virtual ~Create_func_isring() {}
+};
+
+
+class Create_func_isempty : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_isempty(thd, arg1);
+ }
+
+ static Create_func_isempty s_singleton;
+
+protected:
+ Create_func_isempty() {}
+ virtual ~Create_func_isempty() {}
+};
+
+
+class Create_func_issimple : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_issimple(thd, arg1);
+ }
+
+ static Create_func_issimple s_singleton;
+
+protected:
+ Create_func_issimple() {}
+ virtual ~Create_func_issimple() {}
+};
+
+
+
+class Create_func_numgeometries : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_numgeometries(thd, arg1);
+ }
+
+ static Create_func_numgeometries s_singleton;
+
+protected:
+ Create_func_numgeometries() {}
+ virtual ~Create_func_numgeometries() {}
+};
+
+
+class Create_func_numinteriorring : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_numinteriorring(thd, arg1);
+ }
+
+ static Create_func_numinteriorring s_singleton;
+
+protected:
+ Create_func_numinteriorring() {}
+ virtual ~Create_func_numinteriorring() {}
+};
+
+
+class Create_func_numpoints : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_numpoints(thd, arg1);
+ }
+
+ static Create_func_numpoints s_singleton;
+
+protected:
+ Create_func_numpoints() {}
+ virtual ~Create_func_numpoints() {}
+};
+
+
+class Create_func_mbr_overlaps : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
+ Item_func::SP_OVERLAPS_FUNC);
+ }
+
+ static Create_func_mbr_overlaps s_singleton;
+
+protected:
+ Create_func_mbr_overlaps() {}
+ virtual ~Create_func_mbr_overlaps() {}
+};
+
+
+class Create_func_overlaps : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_OVERLAPS_FUNC);
+ }
+
+ static Create_func_overlaps s_singleton;
+
+protected:
+ Create_func_overlaps() {}
+ virtual ~Create_func_overlaps() {}
+};
+
+
+
+
+
+class Create_func_pointn : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_decomp_n(thd, arg1, arg2,
+ Item_func::SP_POINTN);
+ }
+ static Create_func_pointn s_singleton;
+
+protected:
+ Create_func_pointn() {}
+ virtual ~Create_func_pointn() {}
+};
+
+
+
+
+class Create_func_srid : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_srid(thd, arg1);
+ }
+
+ static Create_func_srid s_singleton;
+
+protected:
+ Create_func_srid() {}
+ virtual ~Create_func_srid() {}
+};
+
+
+class Create_func_startpoint : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_spatial_decomp(thd, arg1,
+ Item_func::SP_STARTPOINT);
+ }
+
+ static Create_func_startpoint s_singleton;
+
+protected:
+ Create_func_startpoint() {}
+ virtual ~Create_func_startpoint() {}
+};
+
+
+
+class Create_func_touches : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_TOUCHES_FUNC);
+ }
+
+ static Create_func_touches s_singleton;
+
+protected:
+ Create_func_touches() {}
+ virtual ~Create_func_touches() {}
+};
+
+
+class Create_func_mbr_within : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_mbr_rel(thd, arg1, arg2,
+ Item_func::SP_WITHIN_FUNC);
+ }
+
+ static Create_func_mbr_within s_singleton;
+
+protected:
+ Create_func_mbr_within() {}
+ virtual ~Create_func_mbr_within() {}
+};
+
+
+class Create_func_within : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2) override
+ {
+ return new (thd->mem_root) Item_func_spatial_precise_rel(thd, arg1, arg2,
+ Item_func::SP_WITHIN_FUNC);
+ }
+
+ static Create_func_within s_singleton;
+
+protected:
+ Create_func_within() {}
+ virtual ~Create_func_within() {}
+};
+
+
+class Create_func_x : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_x(thd, arg1);
+ }
+
+ static Create_func_x s_singleton;
+
+protected:
+ Create_func_x() {}
+ virtual ~Create_func_x() {}
+};
+
+
+class Create_func_y : public Create_func_arg1
+{
+public:
+ Item *create_1_arg(THD *thd, Item *arg1) override
+ {
+ return new (thd->mem_root) Item_func_y(thd, arg1);
+ }
+
+ static Create_func_y s_singleton;
+
+protected:
+ Create_func_y() {}
+ virtual ~Create_func_y() {}
+};
+
+
+/*****************************************************************/
+
+
+
+
+
+
+
+/*************************************************************************/
+
+#if !defined(DBUG_OFF)
+Create_func_gis_debug Create_func_gis_debug::s_singleton;
+#endif
+
+Create_func_area Create_func_area::s_singleton;
+Create_func_as_geojson Create_func_as_geojson::s_singleton;
+Create_func_as_wkb Create_func_as_wkb::s_singleton;
+Create_func_as_wkt Create_func_as_wkt::s_singleton;
+Create_func_boundary Create_func_boundary::s_singleton;
+Create_func_buffer Create_func_buffer::s_singleton;
+Create_func_centroid Create_func_centroid::s_singleton;
+Create_func_contains Create_func_contains::s_singleton;
+Create_func_convexhull Create_func_convexhull::s_singleton;
+Create_func_crosses Create_func_crosses::s_singleton;
+Create_func_difference Create_func_difference::s_singleton;
+Create_func_dimension Create_func_dimension::s_singleton;
+Create_func_disjoint Create_func_disjoint::s_singleton;
+Create_func_distance Create_func_distance::s_singleton;
+Create_func_endpoint Create_func_endpoint::s_singleton;
+Create_func_envelope Create_func_envelope::s_singleton;
+Create_func_equals Create_func_equals::s_singleton;
+Create_func_exteriorring Create_func_exteriorring::s_singleton;
+Create_func_geometry_from_json Create_func_geometry_from_json::s_singleton;
+Create_func_geometry_from_text Create_func_geometry_from_text::s_singleton;
+Create_func_geometry_from_wkb Create_func_geometry_from_wkb::s_singleton;
+Create_func_geometryn Create_func_geometryn::s_singleton;
+Create_func_geometry_type Create_func_geometry_type::s_singleton;
+Create_func_glength Create_func_glength::s_singleton;
+Create_func_interiorringn Create_func_interiorringn::s_singleton;
+Create_func_intersection Create_func_intersection::s_singleton;
+Create_func_intersects Create_func_intersects::s_singleton;
+Create_func_isclosed Create_func_isclosed::s_singleton;
+Create_func_isempty Create_func_isempty::s_singleton;
+Create_func_isring Create_func_isring::s_singleton;
+Create_func_issimple Create_func_issimple::s_singleton;
+Create_func_mbr_contains Create_func_mbr_contains::s_singleton;
+Create_func_mbr_disjoint Create_func_mbr_disjoint::s_singleton;
+Create_func_mbr_equals Create_func_mbr_equals::s_singleton;
+Create_func_mbr_intersects Create_func_mbr_intersects::s_singleton;
+Create_func_mbr_overlaps Create_func_mbr_overlaps::s_singleton;
+Create_func_mbr_within Create_func_mbr_within::s_singleton;
+Create_func_numgeometries Create_func_numgeometries::s_singleton;
+Create_func_numinteriorring Create_func_numinteriorring::s_singleton;
+Create_func_numpoints Create_func_numpoints::s_singleton;
+Create_func_overlaps Create_func_overlaps::s_singleton;
+Create_func_pointn Create_func_pointn::s_singleton;
+Create_func_pointonsurface Create_func_pointonsurface::s_singleton;
+Create_func_relate Create_func_relate::s_singleton;
+Create_func_srid Create_func_srid::s_singleton;
+Create_func_startpoint Create_func_startpoint::s_singleton;
+Create_func_symdifference Create_func_symdifference::s_singleton;
+Create_func_touches Create_func_touches::s_singleton;
+Create_func_union Create_func_union::s_singleton;
+Create_func_within Create_func_within::s_singleton;
+Create_func_x Create_func_x::s_singleton;
+Create_func_y Create_func_y::s_singleton;
+
+/*************************************************************************/
+
+
+#define GEOM_BUILDER(F) & F::s_singleton
+
+
+static Native_func_registry func_array_geom[] =
+{
+#ifndef DBUG_OFF
+ { { STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)},
+#endif
+ { { STRING_WITH_LEN("AREA") }, GEOM_BUILDER(Create_func_area)},
+ { { STRING_WITH_LEN("ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { STRING_WITH_LEN("ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
+ { { STRING_WITH_LEN("ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { STRING_WITH_LEN("ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
+ { { STRING_WITH_LEN("BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
+ { { STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
+ { { STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
+ { { STRING_WITH_LEN("CONTAINS") }, GEOM_BUILDER(Create_func_contains)},
+ { { STRING_WITH_LEN("CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
+ { { STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
+ { { STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
+ { { STRING_WITH_LEN("DISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
+ { { STRING_WITH_LEN("ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
+ { { STRING_WITH_LEN("ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
+ { { STRING_WITH_LEN("EQUALS") }, GEOM_BUILDER(Create_func_equals)},
+ { { STRING_WITH_LEN("EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
+ { { STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
+ { { STRING_WITH_LEN("GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
+ { { STRING_WITH_LEN("GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("GLENGTH") }, GEOM_BUILDER(Create_func_glength)},
+ { { STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
+ { { STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
+ { { STRING_WITH_LEN("ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
+ { { STRING_WITH_LEN("ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
+ { { STRING_WITH_LEN("ISRING") }, GEOM_BUILDER(Create_func_isring)},
+ { { STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
+ { { STRING_WITH_LEN("LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)},
+ { { STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
+ { { STRING_WITH_LEN("MBREQUAL") }, GEOM_BUILDER(Create_func_mbr_equals)},
+ { { STRING_WITH_LEN("MBREQUALS") }, GEOM_BUILDER(Create_func_mbr_equals)},
+ { { STRING_WITH_LEN("MBRINTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
+ { { STRING_WITH_LEN("MBROVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)},
+ { { STRING_WITH_LEN("MBRTOUCHES") }, GEOM_BUILDER(Create_func_touches)},
+ { { STRING_WITH_LEN("MBRWITHIN") }, GEOM_BUILDER(Create_func_mbr_within)},
+ { { STRING_WITH_LEN("MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
+ { { STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
+ { { STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
+ { { STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)},
+ { { STRING_WITH_LEN("POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("POINTN") }, GEOM_BUILDER(Create_func_pointn)},
+ { { STRING_WITH_LEN("POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
+ { { STRING_WITH_LEN("POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("SRID") }, GEOM_BUILDER(Create_func_srid)},
+ { { STRING_WITH_LEN("ST_AREA") }, GEOM_BUILDER(Create_func_area)},
+ { { STRING_WITH_LEN("STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
+ { { STRING_WITH_LEN("ST_ASBINARY") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { STRING_WITH_LEN("ST_ASGEOJSON") }, GEOM_BUILDER(Create_func_as_geojson)},
+ { { STRING_WITH_LEN("ST_ASTEXT") }, GEOM_BUILDER(Create_func_as_wkt)},
+ { { STRING_WITH_LEN("ST_ASWKB") }, GEOM_BUILDER(Create_func_as_wkb)},
+ { { STRING_WITH_LEN("ST_ASWKT") }, GEOM_BUILDER(Create_func_as_wkt)},
+ { { STRING_WITH_LEN("ST_BOUNDARY") }, GEOM_BUILDER(Create_func_boundary)},
+ { { STRING_WITH_LEN("ST_BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
+ { { STRING_WITH_LEN("ST_CENTROID") }, GEOM_BUILDER(Create_func_centroid)},
+ { { STRING_WITH_LEN("ST_CONTAINS") }, GEOM_BUILDER(Create_func_contains)},
+ { { STRING_WITH_LEN("ST_CONVEXHULL") }, GEOM_BUILDER(Create_func_convexhull)},
+ { { STRING_WITH_LEN("ST_CROSSES") }, GEOM_BUILDER(Create_func_crosses)},
+ { { STRING_WITH_LEN("ST_DIFFERENCE") }, GEOM_BUILDER(Create_func_difference)},
+ { { STRING_WITH_LEN("ST_DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
+ { { STRING_WITH_LEN("ST_DISJOINT") }, GEOM_BUILDER(Create_func_disjoint)},
+ { { STRING_WITH_LEN("ST_DISTANCE") }, GEOM_BUILDER(Create_func_distance)},
+ { { STRING_WITH_LEN("ST_ENDPOINT") }, GEOM_BUILDER(Create_func_endpoint)},
+ { { STRING_WITH_LEN("ST_ENVELOPE") }, GEOM_BUILDER(Create_func_envelope)},
+ { { STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
+ { { STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
+ { { STRING_WITH_LEN("ST_EXTERIORRING") }, GEOM_BUILDER(Create_func_exteriorring)},
+ { { STRING_WITH_LEN("ST_GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_GEOMCOLLFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_GEOMETRYCOLLECTIONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_GEOMETRYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_GEOMETRYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_GEOMETRYN") }, GEOM_BUILDER(Create_func_geometryn)},
+ { { STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
+ { { STRING_WITH_LEN("ST_GEOMFROMGEOJSON") }, GEOM_BUILDER(Create_func_geometry_from_json)},
+ { { STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
+ { { STRING_WITH_LEN("ST_INTERSECTION") }, GEOM_BUILDER(Create_func_intersection)},
+ { { STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)},
+ { { STRING_WITH_LEN("ST_ISCLOSED") }, GEOM_BUILDER(Create_func_isclosed)},
+ { { STRING_WITH_LEN("ST_ISEMPTY") }, GEOM_BUILDER(Create_func_isempty)},
+ { { STRING_WITH_LEN("ST_ISRING") }, GEOM_BUILDER(Create_func_isring)},
+ { { STRING_WITH_LEN("ST_ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
+ { { STRING_WITH_LEN("ST_LENGTH") }, GEOM_BUILDER(Create_func_glength)},
+ { { STRING_WITH_LEN("ST_LINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_LINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_LINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_LINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_MLINEFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_MLINEFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_MPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_MPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_MPOLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_MPOLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_MULTILINESTRINGFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_MULTILINESTRINGFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_MULTIPOINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_MULTIPOINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)},
+ { { STRING_WITH_LEN("ST_NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)},
+ { { STRING_WITH_LEN("ST_NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)},
+ { { STRING_WITH_LEN("ST_OVERLAPS") }, GEOM_BUILDER(Create_func_overlaps)},
+ { { STRING_WITH_LEN("ST_POINTFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_POINTFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_POINTN") }, GEOM_BUILDER(Create_func_pointn)},
+ { { STRING_WITH_LEN("ST_POINTONSURFACE") }, GEOM_BUILDER(Create_func_pointonsurface)},
+ { { STRING_WITH_LEN("ST_POLYFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_POLYFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_POLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
+ { { STRING_WITH_LEN("ST_POLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+ { { STRING_WITH_LEN("ST_RELATE") }, GEOM_BUILDER(Create_func_relate)},
+ { { STRING_WITH_LEN("ST_SRID") }, GEOM_BUILDER(Create_func_srid)},
+ { { STRING_WITH_LEN("ST_STARTPOINT") }, GEOM_BUILDER(Create_func_startpoint)},
+ { { STRING_WITH_LEN("ST_SYMDIFFERENCE") }, GEOM_BUILDER(Create_func_symdifference)},
+ { { STRING_WITH_LEN("ST_TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
+ { { STRING_WITH_LEN("ST_UNION") }, GEOM_BUILDER(Create_func_union)},
+ { { STRING_WITH_LEN("ST_WITHIN") }, GEOM_BUILDER(Create_func_within)},
+ { { STRING_WITH_LEN("ST_X") }, GEOM_BUILDER(Create_func_x)},
+ { { STRING_WITH_LEN("ST_Y") }, GEOM_BUILDER(Create_func_y)},
+ { { STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
+ { { STRING_WITH_LEN("WITHIN") }, GEOM_BUILDER(Create_func_within)},
+ { { STRING_WITH_LEN("X") }, GEOM_BUILDER(Create_func_x)},
+ { { STRING_WITH_LEN("Y") }, GEOM_BUILDER(Create_func_y)},
+};
+
+
+Native_func_registry_array
+ native_func_registry_array_geom(func_array_geom,
+ array_elements(func_array_geom));
+
#endif /*HAVE_SPATIAL*/
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 4e7cda137c2..c38d0b986f1 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -26,6 +26,10 @@
#pragma interface /* gcc class implementation */
#endif
+#include "sql_type_geom.h"
+#include "item.h"
+#include "gstream.h"
+#include "spatial.h"
#include "gcalc_slicescan.h"
#include "gcalc_tools.h"
@@ -53,7 +57,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count == 1);
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ return Type_handler_geometry::check_type_geom_or_binary(func_name(),
+ args[0]);
}
public:
Item_real_func_args_geometry(THD *thd, Item *a)
@@ -69,7 +74,8 @@ class Item_long_func_args_geometry: public Item_long_func
bool check_arguments() const
{
DBUG_ASSERT(arg_count == 1);
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ return Type_handler_geometry::check_type_geom_or_binary(func_name(),
+ args[0]);
}
protected:
String value;
@@ -89,7 +95,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count == 1);
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ return Type_handler_geometry::check_type_geom_or_binary(func_name(),
+ args[0]);
}
public:
Item_bool_func_args_geometry(THD *thd, Item *a)
@@ -106,7 +113,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 1);
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ return Type_handler_geometry::check_type_geom_or_binary(func_name(),
+ args[0]);
}
public:
Item_str_ascii_func_args_geometry(THD *thd, Item *a)
@@ -127,7 +135,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 1);
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ return Type_handler_geometry::check_type_geom_or_binary(func_name(),
+ args[0]);
}
public:
Item_binary_func_args_geometry(THD *thd, Item *a)
@@ -144,7 +153,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 1);
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry);
+ return Type_handler_geometry::check_type_geom_or_binary(func_name(),
+ args[0]);
}
public:
Item_geometry_func_args_geometry(THD *thd, Item *a)
@@ -163,7 +173,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 2);
- return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ return Type_handler_geometry::check_types_geom_or_binary(func_name(),
+ args, 0, 2);
}
public:
Item_real_func_args_geometry_geometry(THD *thd, Item *a, Item *b)
@@ -181,7 +192,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 2);
- return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ return Type_handler_geometry::check_types_geom_or_binary(func_name(),
+ args, 0, 2);
}
public:
Item_bool_func_args_geometry_geometry(THD *thd, Item *a, Item *b, Item *c)
@@ -210,8 +222,9 @@ class Item_func_geometry_from_wkb: public Item_geometry_func
{
bool check_arguments() const
{
- return args[0]->check_type_or_binary(func_name(), &type_handler_geometry) ||
- check_argument_types_can_return_int(1, MY_MIN(2, arg_count));
+ return
+ Type_handler_geometry::check_type_geom_or_binary(func_name(), args[0]) ||
+ check_argument_types_can_return_int(1, MY_MIN(2, arg_count));
}
public:
Item_func_geometry_from_wkb(THD *thd, Item *a): Item_geometry_func(thd, a) {}
@@ -363,7 +376,10 @@ public:
:Item_geometry_func_args_geometry(thd, a) {}
const char *func_name() const { return "st_centroid"; }
String *val_str(String *);
- Field::geometry_type get_geometry_type() const;
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_point;
+ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_centroid>(thd, this); }
};
@@ -375,7 +391,10 @@ public:
:Item_geometry_func_args_geometry(thd, a) {}
const char *func_name() const { return "st_envelope"; }
String *val_str(String *);
- Field::geometry_type get_geometry_type() const;
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_polygon;
+ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_envelope>(thd, this); }
};
@@ -425,7 +444,10 @@ public:
Item_geometry_func(thd, a, b, srid) {}
const char *func_name() const { return "point"; }
String *val_str(String *);
- Field::geometry_type get_geometry_type() const;
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_point;
+ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_point>(thd, this); }
};
@@ -493,7 +515,8 @@ class Item_func_spatial_collection: public Item_geometry_func
{
bool check_arguments() const
{
- return check_argument_types_or_binary(&type_handler_geometry, 0, arg_count);
+ return Type_handler_geometry::check_types_geom_or_binary(func_name(), args,
+ 0, arg_count);
}
enum Geometry::wkbType coll_type;
enum Geometry::wkbType item_type;
@@ -524,13 +547,112 @@ public:
}
return FALSE;
}
-
+};
+
+
+class Item_func_geometrycollection: public Item_func_spatial_collection
+{
+public:
+ Item_func_geometrycollection(THD *thd, List<Item> &list)
+ :Item_func_spatial_collection(thd, list,
+ Geometry::wkb_geometrycollection,
+ Geometry::wkb_point)
+ { }
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_geometrycollection;
+ }
const char *func_name() const { return "geometrycollection"; }
Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_spatial_collection>(thd, this); }
+ { return get_item_copy<Item_func_geometrycollection>(thd, this); }
};
+class Item_func_linestring: public Item_func_spatial_collection
+{
+public:
+ Item_func_linestring(THD *thd, List<Item> &list)
+ :Item_func_spatial_collection(thd, list,
+ Geometry::wkb_linestring,
+ Geometry::wkb_point)
+ { }
+ const Type_handler *type_handler() const { return &type_handler_linestring; }
+ const char *func_name() const { return "linestring"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_linestring>(thd, this); }
+};
+
+
+class Item_func_polygon: public Item_func_spatial_collection
+{
+public:
+ Item_func_polygon(THD *thd, List<Item> &list)
+ :Item_func_spatial_collection(thd, list,
+ Geometry::wkb_polygon,
+ Geometry::wkb_linestring)
+ { }
+ const Type_handler *type_handler() const { return &type_handler_polygon; }
+ const char *func_name() const { return "polygon"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_polygon>(thd, this); }
+};
+
+
+class Item_func_multilinestring: public Item_func_spatial_collection
+{
+public:
+ Item_func_multilinestring(THD *thd, List<Item> &list)
+ :Item_func_spatial_collection(thd, list,
+ Geometry::wkb_multilinestring,
+ Geometry::wkb_linestring)
+ { }
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_multilinestring;
+ }
+ const char *func_name() const { return "multilinestring"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_multilinestring>(thd, this); }
+};
+
+
+class Item_func_multipoint: public Item_func_spatial_collection
+{
+public:
+ Item_func_multipoint(THD *thd, List<Item> &list)
+ :Item_func_spatial_collection(thd, list,
+ Geometry::wkb_multipoint,
+ Geometry::wkb_point)
+ { }
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_multipoint;
+ }
+ const char *func_name() const { return "multipoint"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_multipoint>(thd, this); }
+};
+
+
+class Item_func_multipolygon: public Item_func_spatial_collection
+{
+public:
+ Item_func_multipolygon(THD *thd, List<Item> &list)
+ :Item_func_spatial_collection(thd, list,
+ Geometry::wkb_multipolygon,
+ Geometry::wkb_polygon)
+ { }
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_multipolygon;
+ }
+ const char *func_name() const { return "multipolygon"; }
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_multipolygon>(thd, this); }
+};
+
+
+
/*
Spatial relations
*/
@@ -546,7 +668,8 @@ protected:
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 2);
- return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ return Type_handler_geometry::check_types_geom_or_binary(func_name(),
+ args, 0, 2);
}
public:
Item_func_spatial_rel(THD *thd, Item *a, Item *b, enum Functype sp_rel):
@@ -641,7 +764,8 @@ class Item_func_spatial_operation: public Item_geometry_func
bool check_arguments() const
{
DBUG_ASSERT(arg_count >= 2);
- return check_argument_types_or_binary(&type_handler_geometry, 0, 2);
+ return Type_handler_geometry::check_types_geom_or_binary(func_name(),
+ args, 0, 2);
}
public:
Gcalc_function::op_type spatial_op;
@@ -945,7 +1069,10 @@ public:
:Item_geometry_func_args_geometry(thd, a) {}
const char *func_name() const { return "st_pointonsurface"; }
String *val_str(String *);
- Field::geometry_type get_geometry_type() const;
+ const Type_handler *type_handler() const
+ {
+ return &type_handler_point;
+ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_pointonsurface>(thd, this); }
};
@@ -971,10 +1098,12 @@ class Item_func_gis_debug: public Item_long_func
#define GEOM_NEW(thd, obj_constructor) new (thd->mem_root) obj_constructor
+#define GEOM_TYPE(x) (x)
#else /*HAVE_SPATIAL*/
#define GEOM_NEW(thd, obj_constructor) NULL
+#define GEOM_TYPE(x) NULL
#endif /*HAVE_SPATIAL*/
#endif /* ITEM_GEOFUNC_INCLUDED */
diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc
deleted file mode 100644
index a2abbce6619..00000000000
--- a/sql/item_inetfunc.cc
+++ /dev/null
@@ -1,967 +0,0 @@
-/* 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-1335 USA */
-
-#include "mariadb.h"
-#include "item_inetfunc.h"
-
-#include "my_net.h"
-
-///////////////////////////////////////////////////////////////////////////
-
-static const size_t IN_ADDR_SIZE= 4;
-static const size_t IN_ADDR_MAX_CHAR_LENGTH= 15;
-
-static const size_t IN6_ADDR_SIZE= 16;
-static const size_t IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2;
-
-/**
- Non-abbreviated syntax is 8 groups, up to 4 digits each,
- plus 7 delimiters between the groups.
- Abbreviated syntax is even shorter.
-*/
-static const uint IN6_ADDR_MAX_CHAR_LENGTH= 8 * 4 + 7;
-
-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;
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-
-class Inet4
-{
- char m_buffer[IN_ADDR_SIZE];
-protected:
- bool ascii_to_ipv4(const char *str, size_t length);
- bool character_string_to_ipv4(const char *str, size_t str_length,
- CHARSET_INFO *cs)
- {
- if (cs->state & MY_CS_NONASCII)
- {
- char tmp[IN_ADDR_MAX_CHAR_LENGTH];
- String_copier copier;
- uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp),
- cs, str, str_length);
- return ascii_to_ipv4(tmp, length);
- }
- return ascii_to_ipv4(str, str_length);
- }
- bool binary_to_ipv4(const char *str, size_t length)
- {
- if (length != sizeof(m_buffer))
- return true;
- memcpy(m_buffer, str, length);
- return false;
- }
- // Non-initializing constructor
- Inet4() { }
-public:
- void to_binary(char *dst, size_t dstsize) const
- {
- DBUG_ASSERT(dstsize >= sizeof(m_buffer));
- memcpy(dst, m_buffer, sizeof(m_buffer));
- }
- bool to_binary(String *to) const
- {
- return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
- }
- size_t to_string(char *dst, size_t dstsize) const;
- bool to_string(String *to) const
- {
- to->set_charset(&my_charset_latin1);
- if (to->alloc(INET_ADDRSTRLEN))
- return true;
- to->length((uint32) to_string((char*) to->ptr(), INET_ADDRSTRLEN));
- return false;
- }
-};
-
-
-class Inet4_null: public Inet4, public Null_flag
-{
-public:
- // Initialize from a text representation
- Inet4_null(const char *str, size_t length, CHARSET_INFO *cs)
- :Null_flag(character_string_to_ipv4(str, length, cs))
- { }
- Inet4_null(const String &str)
- :Inet4_null(str.ptr(), str.length(), str.charset())
- { }
- // Initialize from a binary representation
- Inet4_null(const char *str, size_t length)
- :Null_flag(binary_to_ipv4(str, length))
- { }
- Inet4_null(const Binary_string &str)
- :Inet4_null(str.ptr(), str.length())
- { }
-public:
- const Inet4& to_inet4() const
- {
- DBUG_ASSERT(!is_null());
- return *this;
- }
- void to_binary(char *dst, size_t dstsize) const
- {
- to_inet4().to_binary(dst, dstsize);
- }
- bool to_binary(String *to) const
- {
- return to_inet4().to_binary(to);
- }
- size_t to_string(char *dst, size_t dstsize) const
- {
- return to_inet4().to_string(dst, dstsize);
- }
- bool to_string(String *to) const
- {
- return to_inet4().to_string(to);
- }
-};
-
-
-class Inet6
-{
- char m_buffer[IN6_ADDR_SIZE];
-protected:
- bool make_from_item(Item *item);
- bool ascii_to_ipv6(const char *str, size_t str_length);
- bool character_string_to_ipv6(const char *str, size_t str_length,
- CHARSET_INFO *cs)
- {
- if (cs->state & MY_CS_NONASCII)
- {
- char tmp[IN6_ADDR_MAX_CHAR_LENGTH];
- String_copier copier;
- uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp),
- cs, str, str_length);
- return ascii_to_ipv6(tmp, length);
- }
- return ascii_to_ipv6(str, str_length);
- }
- bool binary_to_ipv6(const char *str, size_t length)
- {
- if (length != sizeof(m_buffer))
- return true;
- memcpy(m_buffer, str, length);
- return false;
- }
- // Non-initializing constructor
- Inet6() { }
-public:
- bool to_binary(String *to) const
- {
- return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
- }
- size_t to_string(char *dst, size_t dstsize) const;
- bool to_string(String *to) const
- {
- to->set_charset(&my_charset_latin1);
- if (to->alloc(INET6_ADDRSTRLEN))
- return true;
- to->length((uint32) to_string((char*) to->ptr(), INET6_ADDRSTRLEN));
- return false;
- }
- bool is_v4compat() const
- {
- static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
- return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer);
- }
- bool is_v4mapped() const
- {
- static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
- return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer);
- }
-};
-
-
-class Inet6_null: public Inet6, public Null_flag
-{
-public:
- // Initialize from a text representation
- Inet6_null(const char *str, size_t length, CHARSET_INFO *cs)
- :Null_flag(character_string_to_ipv6(str, length, cs))
- { }
- Inet6_null(const String &str)
- :Inet6_null(str.ptr(), str.length(), str.charset())
- { }
- // Initialize from a binary representation
- Inet6_null(const char *str, size_t length)
- :Null_flag(binary_to_ipv6(str, length))
- { }
- Inet6_null(const Binary_string &str)
- :Inet6_null(str.ptr(), str.length())
- { }
- // Initialize from an Item
- Inet6_null(Item *item)
- :Null_flag(make_from_item(item))
- { }
-public:
- const Inet6& to_inet6() const
- {
- DBUG_ASSERT(!is_null());
- return *this;
- }
- bool to_binary(String *to) const
- {
- DBUG_ASSERT(!is_null());
- return to_inet6().to_binary(to);
- }
- size_t to_string(char *dst, size_t dstsize) const
- {
- return to_inet6().to_string(dst, dstsize);
- }
- bool to_string(String *to) const
- {
- return to_inet6().to_string(to);
- }
- bool is_v4compat() const
- {
- return to_inet6().is_v4compat();
- }
- bool is_v4mapped() const
- {
- return to_inet6().is_v4mapped();
- }
-};
-
-
-bool Inet6::make_from_item(Item *item)
-{
- String tmp(m_buffer, sizeof(m_buffer), &my_charset_bin);
- String *str= item->val_str(&tmp);
- /*
- Charset could be tested in item->collation.collation before the val_str()
- call, but traditionally Inet6 functions still call item->val_str()
- for non-binary arguments and therefore execute side effects.
- */
- if (!str || str->length() != sizeof(m_buffer) ||
- str->charset() != &my_charset_bin)
- return true;
- if (str->ptr() != m_buffer)
- memcpy(m_buffer, str->ptr(), sizeof(m_buffer));
- return false;
-};
-
-
-/**
- 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_length String length.
-
- @return Completion status.
- @retval true - error, the given string does not represent an IPv4-address.
- @retval false - ok, the string has been converted successfully.
-
- @note The problem with inet_pton() is that it treats leading zeros in
- IPv4-part differently on different platforms.
-*/
-
-bool Inet4::ascii_to_ipv4(const char *str, size_t str_length)
-{
- if (str_length < 7)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): "
- "invalid IPv4 address: too short.",
- (int) str_length, str));
- return true;
- }
-
- if (str_length > IN_ADDR_MAX_CHAR_LENGTH)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): "
- "invalid IPv4 address: too long.",
- (int) str_length, str));
- return true;
- }
-
- unsigned char *ipv4_bytes= (unsigned char *) &m_buffer;
- const char *str_end= str + str_length;
- const char *p= str;
- int byte_value= 0;
- int chars_in_group= 0;
- int dot_count= 0;
- char c= 0;
-
- while (p < str_end && *p)
- {
- c= *p++;
-
- if (my_isdigit(&my_charset_latin1, c))
- {
- ++chars_in_group;
-
- if (chars_in_group > 3)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "too many characters in a group.",
- (int) str_length, str));
- return true;
- }
-
- byte_value= byte_value * 10 + (c - '0');
-
- if (byte_value > 255)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "invalid byte value.",
- (int) str_length, str));
- return true;
- }
- }
- else if (c == '.')
- {
- if (chars_in_group == 0)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "too few characters in a group.",
- (int) str_length, str));
- return true;
- }
-
- ipv4_bytes[dot_count]= (unsigned char) byte_value;
-
- ++dot_count;
- byte_value= 0;
- chars_in_group= 0;
-
- if (dot_count > 3)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "too many dots.", (int) str_length, str));
- return true;
- }
- }
- else
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "invalid character at pos %d.",
- (int) str_length, str, (int) (p - str)));
- return true;
- }
- }
-
- if (c == '.')
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "ending at '.'.", (int) str_length, str));
- return true;
- }
-
- if (dot_count != 3)
- {
- DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: "
- "too few groups.",
- (int) str_length, str));
- return true;
- }
-
- ipv4_bytes[3]= (unsigned char) byte_value;
-
- DBUG_PRINT("info", ("ascii_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d",
- (int) str_length, str,
- ipv4_bytes[0], ipv4_bytes[1],
- ipv4_bytes[2], ipv4_bytes[3]));
- return false;
-}
-
-
-/**
- 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_length String length.
-
- @return Completion status.
- @retval true - error, the given string does not represent an IPv6-address.
- @retval false - ok, the string has been converted successfully.
-
- @note The problem with inet_pton() is that it treats leading zeros in
- IPv4-part differently on different platforms.
-*/
-
-bool Inet6::ascii_to_ipv6(const char *str, size_t str_length)
-{
- if (str_length < 2)
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: too short.",
- (int) str_length, str));
- return true;
- }
-
- if (str_length > IN6_ADDR_MAX_CHAR_LENGTH)
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: too long.",
- (int) str_length, str));
- return true;
- }
-
- memset(m_buffer, 0, sizeof(m_buffer));
-
- const char *p= str;
-
- if (*p == ':')
- {
- ++p;
-
- if (*p != ':')
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "can not start with ':x'.", (int) str_length, str));
- return true;
- }
- }
-
- const char *str_end= str + str_length;
- char *ipv6_bytes_end= m_buffer + sizeof(m_buffer);
- char *dst= m_buffer;
- char *gap_ptr= NULL;
- const char *group_start_ptr= p;
- int chars_in_group= 0;
- int group_value= 0;
-
- while (p < str_end && *p)
- {
- char c= *p++;
-
- if (c == ':')
- {
- group_start_ptr= p;
-
- if (!chars_in_group)
- {
- if (gap_ptr)
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "too many gaps(::).", (int) str_length, str));
- return true;
- }
-
- gap_ptr= dst;
- continue;
- }
-
- if (!*p || p >= str_end)
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "ending at ':'.", (int) str_length, str));
- return true;
- }
-
- if (dst + 2 > ipv6_bytes_end)
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "too many groups (1).", (int) str_length, str));
- return true;
- }
-
- 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", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "unexpected IPv4-part.", (int) str_length, str));
- return true;
- }
-
- Inet4_null tmp(group_start_ptr, (size_t) (str_end - group_start_ptr),
- &my_charset_latin1);
- if (tmp.is_null())
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "invalid IPv4-part.", (int) str_length, str));
- return true;
- }
-
- tmp.to_binary(dst, IN_ADDR_SIZE);
- 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", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "invalid character at pos %d.",
- (int) str_length, str, (int) (p - str)));
- return true;
- }
-
- if (chars_in_group >= 4)
- {
- DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "too many digits in group.",
- (int) str_length, str));
- return true;
- }
-
- 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", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "too many groups (2).", (int) str_length, str));
- return true;
- }
-
- 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", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "no room for a gap (::).", (int) str_length, str));
- return true;
- }
-
- int bytes_to_move= (int)(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", ("ascii_to_ipv6(%.*s): invalid IPv6 address: "
- "too few groups.", (int) str_length, str));
- return true;
- }
-
- return false;
-}
-
-
-/**
- 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] dst A buffer to store string representation of IPv4-address.
- @param[in] dstsize Number of bytes avaiable in "dst"
-
- @note The problem with inet_ntop() is that it is available starting from
- Windows Vista, but the minimum supported version is Windows 2000.
-*/
-
-size_t Inet4::to_string(char *dst, size_t dstsize) const
-{
- return (size_t) my_snprintf(dst, dstsize, "%d.%d.%d.%d",
- (uchar) m_buffer[0], (uchar) m_buffer[1],
- (uchar) m_buffer[2], (uchar) m_buffer[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] dst A buffer to store string representation of IPv6-address.
- It must be at least of INET6_ADDRSTRLEN.
- @param[in] dstsize Number of bytes available dst.
-
- @note The problem with inet_ntop() is that it is available starting from
- Windows Vista, but out the minimum supported version is Windows 2000.
-*/
-
-size_t Inet6::to_string(char *dst, size_t dstsize) const
-{
- struct Region
- {
- int pos;
- int length;
- };
-
- const char *ipv6= m_buffer;
- char *dstend= dst + dstsize;
- 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];
-
- DBUG_ASSERT(dstsize > 0); // Need a space at least for the trailing '\0'
- for (size_t 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 (size_t 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= (int) i;
- rg.length= 1;
- }
- }
- }
-
- if (rg.pos >= 0)
- {
- if (rg.length > gap.length)
- gap= rg;
- }
- }
-
- // 3. Convert binary data to string.
-
- char *p= dst;
-
- for (int i= 0; i < (int) IN6_ADDR_NUM_WORDS; ++i)
- {
- DBUG_ASSERT(dstend >= p);
- size_t dstsize_available= dstend - p;
- if (dstsize_available < 5)
- break;
- 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 (dst). Now it's time to dump IPv4-part.
-
- return (size_t) (p - dst) +
- Inet4_null((const char *) (ipv6_bytes + 12), 4).
- to_string(p, dstsize_available);
- }
- 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 + 1 != IN6_ADDR_NUM_WORDS)
- {
- *p= ':';
- ++p;
- }
- }
- }
-
- *p= 0;
- return (size_t) (p - dst);
-}
-
-///////////////////////////////////////////////////////////////////////////
-
-/**
- Converts IP-address-string to IP-address-data.
-
- ipv4-string -> varbinary(4)
- ipv6-string -> varbinary(16)
-
- @return Completion status.
- @retval NULL Given string does not represent an IP-address.
- @retval !NULL The string has been converted successfully.
-*/
-
-String *Item_func_inet6_aton::val_str(String *buffer)
-{
- DBUG_ASSERT(fixed);
-
- Ascii_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
- if ((null_value= tmp.is_null()))
- return NULL;
-
- Inet4_null ipv4(*tmp.string());
- if (!ipv4.is_null())
- {
- ipv4.to_binary(buffer);
- return buffer;
- }
-
- Inet6_null ipv6(*tmp.string());
- if (!ipv6.is_null())
- {
- ipv6.to_binary(buffer);
- return buffer;
- }
-
- null_value= true;
- return NULL;
-}
-
-
-/**
- Converts IP-address-data to IP-address-string.
-*/
-
-String *Item_func_inet6_ntoa::val_str_ascii(String *buffer)
-{
- DBUG_ASSERT(fixed);
-
- // Binary string argument expected
- if (unlikely(args[0]->result_type() != STRING_RESULT ||
- args[0]->collation.collation != &my_charset_bin))
- {
- null_value= true;
- return NULL;
- }
-
- String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
- if ((null_value= tmp.is_null()))
- return NULL;
-
- Inet4_null ipv4(static_cast<const Binary_string&>(*tmp.string()));
- if (!ipv4.is_null())
- {
- ipv4.to_string(buffer);
- return buffer;
- }
-
- Inet6_null ipv6(static_cast<const Binary_string&>(*tmp.string()));
- if (!ipv6.is_null())
- {
- ipv6.to_string(buffer);
- return buffer;
- }
-
- DBUG_PRINT("info", ("INET6_NTOA(): varbinary(4) or varbinary(16) expected."));
- null_value= true;
- return NULL;
-}
-
-
-/**
- Checks if the passed string represents an IPv4-address.
-*/
-
-longlong Item_func_is_ipv4::val_int()
-{
- DBUG_ASSERT(fixed);
- String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
- return !tmp.is_null() && !Inet4_null(*tmp.string()).is_null();
-}
-
-
-/**
- Checks if the passed string represents an IPv6-address.
-*/
-
-longlong Item_func_is_ipv6::val_int()
-{
- DBUG_ASSERT(fixed);
- String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
- return !tmp.is_null() && !Inet6_null(*tmp.string()).is_null();
-}
-
-
-/**
- Checks if the passed IPv6-address is an IPv4-compat IPv6-address.
-*/
-
-longlong Item_func_is_ipv4_compat::val_int()
-{
- Inet6_null ip6(args[0]);
- return !ip6.is_null() && ip6.is_v4compat();
-}
-
-
-/**
- Checks if the passed IPv6-address is an IPv4-mapped IPv6-address.
-*/
-
-longlong Item_func_is_ipv4_mapped::val_int()
-{
- Inet6_null ip6(args[0]);
- return !ip6.is_null() && ip6.is_v4mapped();
-}
diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h
deleted file mode 100644
index 8cfb4cd278c..00000000000
--- a/sql/item_inetfunc.h
+++ /dev/null
@@ -1,226 +0,0 @@
-#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-1335 USA */
-
-
-#include "item.h"
-
-/*************************************************************************
- Item_func_inet_aton implements INET_ATON() SQL-function.
-*************************************************************************/
-
-class Item_func_inet_aton : public Item_longlong_func
-{
- bool check_arguments() const
- { return check_argument_types_can_return_text(0, arg_count); }
-public:
- Item_func_inet_aton(THD *thd, Item *a): Item_longlong_func(thd, a) {}
- longlong val_int();
- const char *func_name() const { return "inet_aton"; }
- bool fix_length_and_dec()
- {
- decimals= 0;
- max_length= 21;
- maybe_null= 1;
- unsigned_flag= 1;
- return FALSE;
- }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_inet_aton>(thd, this); }
-};
-
-
-/*************************************************************************
- Item_func_inet_ntoa implements INET_NTOA() SQL-function.
-*************************************************************************/
-
-class Item_func_inet_ntoa : public Item_str_func
-{
-public:
- Item_func_inet_ntoa(THD *thd, Item *a): Item_str_func(thd, a)
- { }
- String* val_str(String* str);
- const char *func_name() const { return "inet_ntoa"; }
- bool fix_length_and_dec()
- {
- decimals= 0;
- fix_length_and_charset(3 * 8 + 7, default_charset());
- maybe_null= 1;
- return FALSE;
- }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_inet_ntoa>(thd, this); }
-};
-
-
-/*************************************************************************
- 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(THD *thd, Item *ip_addr):
- Item_bool_func(thd, ip_addr)
- {
- null_value= false;
- }
- bool need_parentheses_in_default() { return false; }
-};
-
-
-/*************************************************************************
- Item_func_inet6_aton implements INET6_ATON() SQL-function.
-*************************************************************************/
-
-class Item_func_inet6_aton : public Item_str_func
-{
-public:
- inline Item_func_inet6_aton(THD *thd, Item *ip_addr):
- Item_str_func(thd, ip_addr)
- { }
-
-public:
- virtual const char *func_name() const
- { return "inet6_aton"; }
-
- virtual bool fix_length_and_dec()
- {
- decimals= 0;
- fix_length_and_charset(16, &my_charset_bin);
- maybe_null= 1;
- return FALSE;
- }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_inet6_aton>(thd, this); }
-
- String *val_str(String *to);
-};
-
-
-/*************************************************************************
- Item_func_inet6_ntoa implements INET6_NTOA() SQL-function.
-*************************************************************************/
-
-class Item_func_inet6_ntoa : public Item_str_ascii_func
-{
-public:
- inline Item_func_inet6_ntoa(THD *thd, Item *ip_addr):
- Item_str_ascii_func(thd, ip_addr)
- { }
-
-public:
- virtual const char *func_name() const
- { return "inet6_ntoa"; }
-
- virtual bool 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;
- return FALSE;
- }
- String *val_str_ascii(String *to);
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_inet6_ntoa>(thd, this); }
-};
-
-
-/*************************************************************************
- 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(THD *thd, Item *ip_addr):
- Item_func_inet_bool_base(thd, ip_addr)
- { }
-
-public:
- virtual const char *func_name() const
- { return "is_ipv4"; }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_is_ipv4>(thd, this); }
-
- longlong val_int();
-};
-
-
-/*************************************************************************
- 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(THD *thd, Item *ip_addr):
- Item_func_inet_bool_base(thd, ip_addr)
- { }
-
- virtual const char *func_name() const
- { return "is_ipv6"; }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_is_ipv6>(thd, this); }
-
- longlong val_int();
-};
-
-
-/*************************************************************************
- 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(THD *thd, Item *ip_addr):
- Item_func_inet_bool_base(thd, ip_addr)
- { }
- virtual const char *func_name() const
- { return "is_ipv4_compat"; }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_is_ipv4_compat>(thd, this); }
- longlong val_int();
-};
-
-
-/*************************************************************************
- 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(THD *thd, Item *ip_addr):
- Item_func_inet_bool_base(thd, ip_addr)
- { }
- virtual const char *func_name() const
- { return "is_ipv4_mapped"; }
- Item *get_copy(THD *thd)
- { return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); }
- longlong val_int();
-};
-
-#endif // ITEM_INETFUNC_INCLUDED
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index 0d15c5e9ad0..032ecb1bb91 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, Monty Program Ab.
+/* Copyright (c) 2016, 2020, MariaDB Corporation.
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,7 +36,7 @@ static bool eq_ascii_string(const CHARSET_INFO *cs,
my_wc_t wc;
int wc_len;
- wc_len= cs->cset->mb_wc(cs, &wc, (uchar *) s, (uchar *) s_end);
+ wc_len= cs->mb_wc(&wc, (uchar *) s, (uchar *) s_end);
if (wc_len <= 0 || (wc | 0x20) != (my_wc_t) *ascii)
return 0;
@@ -439,7 +439,17 @@ bool Item_func_json_value::fix_length_and_dec()
{
collation.set(args[0]->collation);
max_length= args[0]->max_length;
- path.set_constant_flag(args[1]->const_item());
+ set_constant_flag(args[1]->const_item());
+ maybe_null= 1;
+ return FALSE;
+}
+
+
+bool Item_func_json_query::fix_length_and_dec()
+{
+ collation.set(args[0]->collation);
+ max_length= args[0]->max_length;
+ set_constant_flag(args[1]->const_item());
maybe_null= 1;
return FALSE;
}
@@ -449,115 +459,100 @@ bool Item_func_json_value::fix_length_and_dec()
Returns NULL, not an error if the found value
is not a scalar.
*/
-String *Item_func_json_value::val_str(String *str)
+bool Json_path_extractor::extract(String *str, Item *item_js, Item *item_jp,
+ CHARSET_INFO *cs)
{
- json_engine_t je;
- String *js= args[0]->val_json(&tmp_js);
+ String *js= item_js->val_json(&tmp_js);
int error= 0;
uint array_counters[JSON_DEPTH_LIMIT];
- if (!path.parsed)
+ if (!parsed)
{
- String *s_p= args[1]->val_str(&tmp_path);
+ String *s_p= item_jp->val_str(&tmp_path);
if (s_p &&
- json_path_setup(&path.p, s_p->charset(), (const uchar *) s_p->ptr(),
+ json_path_setup(&p, s_p->charset(), (const uchar *) s_p->ptr(),
(const uchar *) s_p->ptr() + s_p->length()))
- goto err_return;
- path.parsed= path.constant;
+ return true;
+ parsed= constant;
}
- if ((null_value= args[0]->null_value || args[1]->null_value))
- return NULL;
-
- json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
- (const uchar *) js->ptr() + js->length());
+ if (item_js->null_value || item_jp->null_value)
+ return true;
+ Json_engine_scan je(*js);
str->length(0);
- str->set_charset(collation.collation);
+ str->set_charset(cs);
- path.cur_step= path.p.steps;
+ cur_step= p.steps;
continue_search:
- if (json_find_path(&je, &path.p, &path.cur_step, array_counters))
- {
- if (je.s.error)
- goto err_return;
-
- null_value= 1;
- return 0;
- }
+ if (json_find_path(&je, &p, &cur_step, array_counters))
+ return true;
if (json_read_value(&je))
- goto err_return;
+ return true;
if (unlikely(check_and_get_value(&je, str, &error)))
{
if (error)
- goto err_return;
+ return true;
goto continue_search;
}
- return str;
-
-err_return:
- null_value= 1;
- return 0;
+ return false;
}
-bool Item_func_json_value::check_and_get_value(json_engine_t *je, String *res,
- int *error)
+bool Json_engine_scan::check_and_get_value_scalar(String *res, int *error)
{
CHARSET_INFO *json_cs;
const uchar *js;
uint js_len;
- if (!json_value_scalar(je))
+ if (!json_value_scalar(this))
{
/* We only look for scalar values! */
- if (json_skip_level(je) || json_scan_next(je))
+ if (json_skip_level(this) || json_scan_next(this))
*error= 1;
return true;
}
- if (je->value_type == JSON_VALUE_TRUE ||
- je->value_type == JSON_VALUE_FALSE)
+ if (value_type == JSON_VALUE_TRUE ||
+ value_type == JSON_VALUE_FALSE)
{
json_cs= &my_charset_utf8mb4_bin;
- js= (const uchar *) ((je->value_type == JSON_VALUE_TRUE) ? "1" : "0");
+ js= (const uchar *) ((value_type == JSON_VALUE_TRUE) ? "1" : "0");
js_len= 1;
}
else
{
- json_cs= je->s.cs;
- js= je->value;
- js_len= je->value_len;
+ json_cs= s.cs;
+ js= value;
+ js_len= value_len;
}
- return st_append_json(res, json_cs, js, js_len);
+ return st_append_json(res, json_cs, js, js_len);
}
-bool Item_func_json_query::check_and_get_value(json_engine_t *je, String *res,
- int *error)
+bool Json_engine_scan::check_and_get_value_complex(String *res, int *error)
{
- const uchar *value;
- if (json_value_scalar(je))
+ if (json_value_scalar(this))
{
/* We skip scalar values. */
- if (json_scan_next(je))
+ if (json_scan_next(this))
*error= 1;
return true;
}
- value= je->value;
- if (json_skip_level(je))
+ const uchar *tmp_value= value;
+ if (json_skip_level(this))
{
*error= 1;
return true;
}
- res->set((const char *) je->value, (uint32)(je->s.c_str - value), je->s.cs);
+ res->set((const char *) value, (uint32)(s.c_str - tmp_value), s.cs);
return false;
}
@@ -600,7 +595,7 @@ String *Item_func_json_quote::val_str(String *str)
bool Item_func_json_unquote::fix_length_and_dec()
{
- collation.set(&my_charset_utf8_general_ci,
+ collation.set(&my_charset_utf8mb3_general_ci,
DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
max_length= args[0]->max_length;
maybe_null= 1;
@@ -645,12 +640,12 @@ String *Item_func_json_unquote::val_str(String *str)
return js;
str->length(0);
- str->set_charset(&my_charset_utf8_general_ci);
+ str->set_charset(&my_charset_utf8mb3_general_ci);
if (str->realloc_with_extra_if_needed(je.value_len) ||
(c_len= json_unescape(js->charset(),
je.value, je.value + je.value_len,
- &my_charset_utf8_general_ci,
+ &my_charset_utf8mb3_general_ci,
(uchar *) str->ptr(), (uchar *) (str->ptr() + je.value_len))) < 0)
goto error;
@@ -680,7 +675,7 @@ static int alloc_tmp_paths(THD *thd, uint n_paths,
return 1;
for (uint c_path=0; c_path < n_paths; c_path++)
- (*tmp_paths)[c_path].set_charset(&my_charset_utf8_general_ci);
+ (*tmp_paths)[c_path].set_charset(&my_charset_utf8mb3_general_ci);
}
return 0;
@@ -910,7 +905,7 @@ longlong Item_func_json_extract::val_int()
{
char *end;
int err;
- i= my_strntoll(collation.collation, value, value_len, 10, &end, &err);
+ i= collation.collation->strntoll(value, value_len, 10, &end, &err);
break;
}
case JSON_VALUE_TRUE:
@@ -941,7 +936,7 @@ double Item_func_json_extract::val_real()
{
char *end;
int err;
- d= my_strntod(collation.collation, value, value_len, &end, &err);
+ d= collation.collation->strntod(value, value_len, &end, &err);
break;
}
case JSON_VALUE_TRUE:
@@ -1132,10 +1127,8 @@ static int check_contains(json_engine_t *js, json_engine_t *value)
char *end;
int err;
- d_j= my_strntod(js->s.cs, (char *) js->value, js->value_len,
- &end, &err);;
- d_v= my_strntod(value->s.cs, (char *) value->value, value->value_len,
- &end, &err);;
+ d_j= js->s.cs->strntod((char *) js->value, js->value_len, &end, &err);;
+ d_v= value->s.cs->strntod((char *) value->value, value->value_len, &end, &err);;
return (fabs(d_j - d_v) < 1e-12);
}
@@ -1494,6 +1487,52 @@ append_null:
}
+static int append_json_value_from_field(String *str,
+ Item *i, Field *f, const uchar *key, size_t offset, String *tmp_val)
+{
+ if (i->type_handler()->is_bool_type())
+ {
+ longlong v_int= f->val_int(key + offset);
+ const char *t_f;
+ int t_f_len;
+
+ if (f->is_null_in_record(key))
+ goto append_null;
+
+ if (v_int)
+ {
+ t_f= "true";
+ t_f_len= 4;
+ }
+ else
+ {
+ t_f= "false";
+ t_f_len= 5;
+ }
+
+ return str->append(t_f, t_f_len);
+ }
+ {
+ String *sv= f->val_str(tmp_val, key + offset);
+ if (f->is_null_in_record(key))
+ goto append_null;
+ if (i->is_json_type())
+ return str->append(sv->ptr(), sv->length());
+
+ if (i->result_type() == STRING_RESULT)
+ {
+ return str->append("\"", 1) ||
+ st_append_escaped(str, sv) ||
+ str->append("\"", 1);
+ }
+ return st_append_escaped(str, sv);
+ }
+
+append_null:
+ return str->append("null", 4);
+}
+
+
static int append_json_keyname(String *str, Item *item, String *tmp_val)
{
String *sv= item->val_str(tmp_val);
@@ -2656,7 +2695,7 @@ longlong Item_func_json_depth::val_int()
bool Item_func_json_type::fix_length_and_dec()
{
- collation.set(&my_charset_utf8_general_ci);
+ collation.set(&my_charset_utf8mb3_general_ci);
max_length= 12;
maybe_null= 1;
return FALSE;
@@ -2702,7 +2741,7 @@ String *Item_func_json_type::val_str(String *str)
break;
}
- str->set(type, strlen(type), &my_charset_utf8_general_ci);
+ str->set(type, strlen(type), &my_charset_utf8mb3_general_ci);
return str;
error:
@@ -3358,7 +3397,7 @@ int Item_func_json_search::compare_json_value_wild(json_engine_t *je,
const String *cmp_str)
{
if (je->value_type != JSON_VALUE_STRING || !je->value_escaped)
- return my_wildcmp(collation.collation,
+ return collation.collation->wildcmp(
(const char *) je->value, (const char *) (je->value + je->value_len),
cmp_str->ptr(), cmp_str->end(), escape, wild_one, wild_many) ? 0 : 1;
@@ -3375,7 +3414,7 @@ int Item_func_json_search::compare_json_value_wild(json_engine_t *je,
if (esc_len <= 0)
return 0;
- return my_wildcmp(collation.collation,
+ return collation.collation->wildcmp(
esc_value.ptr(), esc_value.ptr() + esc_len,
cmp_str->ptr(), cmp_str->end(), escape, wild_one, wild_many) ? 0 : 1;
}
@@ -3604,7 +3643,7 @@ int Arg_comparator::compare_json_str_basic(Item *j, Item *s)
if (value2.realloc_with_extra_if_needed(je.value_len) ||
(c_len= json_unescape(js->charset(), je.value,
je.value + je.value_len,
- &my_charset_utf8_general_ci,
+ &my_charset_utf8mb3_general_ci,
(uchar *) value2.ptr(),
(uchar *) (value2.ptr() + je.value_len))) < 0)
goto error;
@@ -3653,7 +3692,7 @@ int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s)
if (value1.realloc_with_extra_if_needed(value_len) ||
(c_len= json_unescape(value1.charset(), (uchar *) value,
(uchar *) value+value_len,
- &my_charset_utf8_general_ci,
+ &my_charset_utf8mb3_general_ci,
(uchar *) value1.ptr(),
(uchar *) (value1.ptr() + value_len))) < 0)
return 1;
@@ -3663,3 +3702,172 @@ int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s)
return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0);
}
+
+
+String *Item_func_json_arrayagg::get_str_from_item(Item *i, String *tmp)
+{
+ m_tmp_json.length(0);
+ if (append_json_value(&m_tmp_json, i, tmp))
+ return NULL;
+ return &m_tmp_json;
+}
+
+
+String *Item_func_json_arrayagg::get_str_from_field(Item *i,Field *f,
+ String *tmp, const uchar *key, size_t offset)
+{
+ m_tmp_json.length(0);
+
+ if (append_json_value_from_field(&m_tmp_json, i, f, key, offset, tmp))
+ return NULL;
+
+ return &m_tmp_json;
+
+}
+
+
+void Item_func_json_arrayagg::cut_max_length(String *result,
+ uint old_length, uint max_length) const
+{
+ if (result->length() == 0)
+ return;
+
+ if (result->ptr()[result->length() - 1] != '"' ||
+ max_length == 0)
+ {
+ Item_func_group_concat::cut_max_length(result, old_length, max_length);
+ return;
+ }
+
+ Item_func_group_concat::cut_max_length(result, old_length, max_length-1);
+ result->append('"');
+}
+
+
+String* Item_func_json_arrayagg::val_str(String *str)
+{
+ if ((str= Item_func_group_concat::val_str(str)))
+ {
+ String s;
+ s.append('[');
+ s.swap(*str);
+ str->append(s);
+ str->append(']');
+ }
+ return str;
+}
+
+
+Item_func_json_objectagg::
+Item_func_json_objectagg(THD *thd, Item_func_json_objectagg *item)
+ :Item_sum(thd, item)
+{
+ quick_group= FALSE;
+ result.set_charset(collation.collation);
+ result.append("{");
+}
+
+
+bool
+Item_func_json_objectagg::fix_fields(THD *thd, Item **ref)
+{
+ uint i; /* for loop variable */
+ DBUG_ASSERT(fixed == 0);
+
+ memcpy(orig_args, args, sizeof(Item*) * arg_count);
+
+ if (init_sum_func_check(thd))
+ return TRUE;
+
+ maybe_null= 1;
+
+ /*
+ Fix fields for select list and ORDER clause
+ */
+
+ for (i=0 ; i < arg_count ; i++)
+ {
+ if (args[i]->fix_fields_if_needed_for_scalar(thd, &args[i]))
+ return TRUE;
+ m_with_subquery|= args[i]->with_subquery();
+ with_param|= args[i]->with_param;
+ with_window_func|= args[i]->with_window_func;
+ }
+
+ /* skip charset aggregation for order columns */
+ if (agg_arg_charsets_for_string_result(collation, args, arg_count))
+ return 1;
+
+ result.set_charset(collation.collation);
+ result_field= 0;
+ null_value= 1;
+ max_length= (uint32)(thd->variables.group_concat_max_len
+ / collation.collation->mbminlen
+ * collation.collation->mbmaxlen);
+
+ if (check_sum_func(thd, ref))
+ return TRUE;
+
+ fixed= 1;
+ return FALSE;
+}
+
+
+void Item_func_json_objectagg::cleanup()
+{
+ DBUG_ENTER("Item_func_json_objectagg::cleanup");
+ Item_sum::cleanup();
+
+ result.length(1);
+ DBUG_VOID_RETURN;
+}
+
+
+Item *Item_func_json_objectagg::copy_or_same(THD* thd)
+{
+ return new (thd->mem_root) Item_func_json_objectagg(thd, this);
+}
+
+
+void Item_func_json_objectagg::clear()
+{
+ result.length(1);
+ null_value= 1;
+}
+
+
+bool Item_func_json_objectagg::add()
+{
+ StringBuffer<MAX_FIELD_WIDTH> buf;
+ String *key;
+
+ key= args[0]->val_str(&buf);
+ if (args[0]->is_null())
+ return 0;
+
+ null_value= 0;
+ if (result.length() > 1)
+ result.append(", ");
+
+ result.append("\"");
+ result.append(*key);
+ result.append("\":");
+
+ buf.length(0);
+ append_json_value(&result, args[1], &buf);
+
+ return 0;
+}
+
+
+String* Item_func_json_objectagg::val_str(String* str)
+{
+ DBUG_ASSERT(fixed == 1);
+ if (null_value)
+ return 0;
+
+ result.append("}");
+ return &result;
+}
+
+
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
index c703533f799..ec6c6696001 100644
--- a/sql/item_jsonfunc.h
+++ b/sql/item_jsonfunc.h
@@ -23,6 +23,7 @@
#include <json_lib.h>
#include "item_cmpfunc.h" // Item_bool_func
#include "item_strfunc.h" // Item_str_func
+#include "item_sum.h"
class json_path_with_flags
@@ -40,6 +41,33 @@ public:
};
+class Json_engine_scan: public json_engine_t
+{
+public:
+ Json_engine_scan(CHARSET_INFO *i_cs, const uchar *str, const uchar *end)
+ {
+ json_scan_start(this, i_cs, str, end);
+ }
+ Json_engine_scan(const String &str)
+ :Json_engine_scan(str.charset(), (const uchar *) str.ptr(),
+ (const uchar *) str.end())
+ { }
+ bool check_and_get_value_scalar(String *res, int *error);
+ bool check_and_get_value_complex(String *res, int *error);
+};
+
+
+class Json_path_extractor: public json_path_with_flags
+{
+protected:
+ String tmp_js, tmp_path;
+ virtual ~Json_path_extractor() { }
+ virtual bool check_and_get_value(Json_engine_scan *je,
+ String *to, int *error)=0;
+ bool extract(String *to, Item *js, Item *jp, CHARSET_INFO *cs);
+};
+
+
class Item_func_json_valid: public Item_bool_func
{
protected:
@@ -56,8 +84,14 @@ public:
maybe_null= 1;
return FALSE;
}
+ bool set_format_by_check_constraint(Send_field_extended_metadata *to) const
+ {
+ static const Lex_cstring fmt(STRING_WITH_LEN("json"));
+ return to->set_format_name(fmt);
+ }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_json_valid>(thd, this); }
+ enum Functype functype() const { return JSON_VALID_FUNC; }
};
@@ -78,33 +112,72 @@ public:
};
-class Item_func_json_value: public Item_str_func
+class Item_json_func: public Item_str_func
+{
+public:
+ Item_json_func(THD *thd)
+ :Item_str_func(thd) { }
+ Item_json_func(THD *thd, Item *a)
+ :Item_str_func(thd, a) { }
+ Item_json_func(THD *thd, Item *a, Item *b)
+ :Item_str_func(thd, a, b) { }
+ Item_json_func(THD *thd, List<Item> &list)
+ :Item_str_func(thd, list) { }
+ bool is_json_type() { return true; }
+ void make_send_field(THD *thd, Send_field *tmp_field)
+ {
+ Item_str_func::make_send_field(thd, tmp_field);
+ static const Lex_cstring fmt(STRING_WITH_LEN("json"));
+ tmp_field->set_format_name(fmt);
+ }
+};
+
+
+class Item_func_json_value: public Item_str_func,
+ public Json_path_extractor
{
-protected:
- json_path_with_flags path;
- String tmp_js, tmp_path;
public:
Item_func_json_value(THD *thd, Item *js, Item *i_path):
Item_str_func(thd, js, i_path) {}
- const char *func_name() const { return "json_value"; }
- bool fix_length_and_dec();
- String *val_str(String *);
- virtual bool check_and_get_value(json_engine_t *je, String *res, int *error);
- Item *get_copy(THD *thd)
+ const char *func_name() const override { return "json_value"; }
+ bool fix_length_and_dec() override ;
+ String *val_str(String *to) override
+ {
+ null_value= Json_path_extractor::extract(to, args[0], args[1],
+ collation.collation);
+ return null_value ? NULL : to;
+ }
+ bool check_and_get_value(Json_engine_scan *je,
+ String *res, int *error) override
+ {
+ return je->check_and_get_value_scalar(res, error);
+ }
+ Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_json_value>(thd, this); }
};
-class Item_func_json_query: public Item_func_json_value
+class Item_func_json_query: public Item_json_func,
+ public Json_path_extractor
{
public:
Item_func_json_query(THD *thd, Item *js, Item *i_path):
- Item_func_json_value(thd, js, i_path) {}
- bool is_json_type() { return true; }
- const char *func_name() const { return "json_query"; }
- bool check_and_get_value(json_engine_t *je, String *res, int *error);
- Item *get_copy(THD *thd)
+ Item_json_func(thd, js, i_path) {}
+ const char *func_name() const override { return "json_query"; }
+ bool fix_length_and_dec() override;
+ String *val_str(String *to) override
+ {
+ null_value= Json_path_extractor::extract(to, args[0], args[1],
+ collation.collation);
+ return null_value ? NULL : to;
+ }
+ bool check_and_get_value(Json_engine_scan *je,
+ String *res, int *error) override
+ {
+ return je->check_and_get_value_complex(res, error);
+ }
+ Item *get_copy(THD *thd) override
{ return get_item_copy<Item_func_json_query>(thd, this); }
};
@@ -139,18 +212,17 @@ public:
};
-class Item_json_str_multipath: public Item_str_func
+class Item_json_str_multipath: public Item_json_func
{
protected:
json_path_with_flags *paths;
String *tmp_paths;
public:
Item_json_str_multipath(THD *thd, List<Item> &list):
- Item_str_func(thd, list), tmp_paths(0) {}
+ Item_json_func(thd, list), tmp_paths(0) {}
bool fix_fields(THD *thd, Item **ref);
void cleanup();
virtual uint get_n_paths() const = 0;
- bool is_json_type() { return true; }
};
@@ -218,18 +290,17 @@ public:
};
-class Item_func_json_array: public Item_str_func
+class Item_func_json_array: public Item_json_func
{
protected:
String tmp_val;
ulong result_limit;
public:
Item_func_json_array(THD *thd):
- Item_str_func(thd) {}
+ Item_json_func(thd) {}
Item_func_json_array(THD *thd, List<Item> &list):
- Item_str_func(thd, list) {}
+ Item_json_func(thd, list) {}
String *val_str(String *);
- bool is_json_type() { return true; }
bool fix_length_and_dec();
const char *func_name() const { return "json_array"; }
Item *get_copy(THD *thd)
@@ -274,7 +345,6 @@ public:
Item_func_json_object(THD *thd, List<Item> &list):
Item_func_json_array(thd, list) {}
String *val_str(String *);
- bool is_json_type() { return true; }
const char *func_name() const { return "json_object"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_json_object>(thd, this); }
@@ -289,7 +359,6 @@ public:
Item_func_json_merge(THD *thd, List<Item> &list):
Item_func_json_array(thd, list) {}
String *val_str(String *);
- bool is_json_type() { return true; }
const char *func_name() const { return "json_merge_preserve"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_json_merge>(thd, this); }
@@ -440,7 +509,7 @@ public:
};
-class Item_func_json_format: public Item_str_func
+class Item_func_json_format: public Item_json_func
{
public:
enum formats
@@ -455,18 +524,103 @@ protected:
String tmp_js;
public:
Item_func_json_format(THD *thd, Item *js, formats format):
- Item_str_func(thd, js), fmt(format) {}
+ Item_json_func(thd, js), fmt(format) {}
Item_func_json_format(THD *thd, List<Item> &list):
- Item_str_func(thd, list), fmt(DETAILED) {}
+ Item_json_func(thd, list), fmt(DETAILED) {}
const char *func_name() const;
bool fix_length_and_dec();
String *val_str(String *str);
String *val_json(String *str);
- bool is_json_type() { return true; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_json_format>(thd, this); }
};
+class Item_func_json_arrayagg : public Item_func_group_concat
+{
+protected:
+ /*
+ Overrides Item_func_group_concat::skip_nulls()
+ NULL-s should be added to the result as JSON null value.
+ */
+ bool skip_nulls() const { return false; }
+ String *get_str_from_item(Item *i, String *tmp);
+ String *get_str_from_field(Item *i, Field *f, String *tmp,
+ const uchar *key, size_t offset);
+ void cut_max_length(String *result,
+ uint old_length, uint max_length) const;
+public:
+ String m_tmp_json; /* Used in get_str_from_*.. */
+ Item_func_json_arrayagg(THD *thd, Name_resolution_context *context_arg,
+ bool is_distinct, List<Item> *is_select,
+ const SQL_I_List<ORDER> &is_order, String *is_separator,
+ bool limit_clause, Item *row_limit, Item *offset_limit):
+ Item_func_group_concat(thd, context_arg, is_distinct, is_select, is_order,
+ is_separator, limit_clause, row_limit, offset_limit)
+ {
+ }
+ Item_func_json_arrayagg(THD *thd, Item_func_json_arrayagg *item);
+ bool is_json_type() { return true; }
+
+ const char *func_name() const { return "json_arrayagg("; }
+ enum Sumfunctype sum_func() const {return JSON_ARRAYAGG_FUNC;}
+
+ String* val_str(String *str);
+
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_arrayagg>(thd, this); }
+};
+
+
+class Item_func_json_objectagg : public Item_sum
+{
+ String result;
+public:
+ Item_func_json_objectagg(THD *thd, Item *key, Item *value) :
+ Item_sum(thd, key, value)
+ {
+ quick_group= FALSE;
+ result.append("{");
+ }
+
+ Item_func_json_objectagg(THD *thd, Item_func_json_objectagg *item);
+ bool is_json_type() { return true; }
+ void cleanup();
+
+ enum Sumfunctype sum_func () const {return JSON_OBJECTAGG_FUNC;}
+ const char *func_name() const { return "json_objectagg"; }
+ const Type_handler *type_handler() const
+ {
+ if (too_big_for_varchar())
+ return &type_handler_blob;
+ return &type_handler_varchar;
+ }
+ void clear();
+ bool add();
+ void reset_field() { DBUG_ASSERT(0); } // not used
+ void update_field() { DBUG_ASSERT(0); } // not used
+ bool fix_fields(THD *,Item **);
+
+ double val_real()
+ { return 0.0; }
+ longlong val_int()
+ { return 0; }
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ {
+ my_decimal_set_zero(decimal_value);
+ return decimal_value;
+ }
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ {
+ return get_date_from_string(thd, ltime, fuzzydate);
+ }
+ String* val_str(String* str);
+ Item *copy_or_same(THD* thd);
+ void no_rows_in_result() {}
+ Item *get_copy(THD *thd)
+ { return get_item_copy<Item_func_json_objectagg>(thd, this); }
+};
+
+
#endif /* ITEM_JSONFUNC_INCLUDED */
diff --git a/sql/item_row.cc b/sql/item_row.cc
index a363f9b5576..767787497ce 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -88,6 +88,25 @@ Item_row::eval_not_null_tables(void *opt_arg)
}
+bool
+Item_row::find_not_null_fields(table_map allowed)
+{
+ if (~allowed & used_tables())
+ return false;
+
+ Item **arg,**arg_end;
+ if (arg_count)
+ {
+ for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++)
+ {
+ if (!(*arg)->find_not_null_fields(allowed))
+ continue;
+ }
+ }
+ return false;
+}
+
+
void Item_row::cleanup()
{
DBUG_ENTER("Item_row::cleanup");
diff --git a/sql/item_row.h b/sql/item_row.h
index ea5a0f21d8b..2872a498d55 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -60,7 +60,7 @@ public:
bool with_subquery() const { DBUG_ASSERT(fixed); return m_with_subquery; }
enum Type type() const { return ROW_ITEM; };
const Type_handler *type_handler() const { return &type_handler_row; }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
return NULL; // Check with Vicentiu why it's called for Item_row
@@ -121,6 +121,7 @@ public:
}
Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
uint cols() const { return arg_count; }
Item* element_index(uint i) { return args[i]; }
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index ea14e9d44f8..c2de296a109 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -43,7 +43,6 @@
#include "set_var.h"
#include "sql_base.h"
#include "sql_time.h"
-#include "sql_acl.h" // SUPER_ACL
#include "des_key_file.h" // st_des_keyschedule, st_des_keyblock
#include "password.h" // my_make_scrambled_password,
// my_make_scrambled_password_323
@@ -58,25 +57,21 @@ C_MODE_END
size_t username_char_length= 80;
+/*
+ Calculate max length of string from length argument to LEFT and RIGHT
+*/
-class Repeat_count
+static uint32 max_length_for_string(Item *item)
{
- ulonglong m_count;
-public:
- Repeat_count(Item *item)
- :m_count(0)
+ ulonglong length= item->val_int();
+ /* Note that if value is NULL, val_int() returned 0 */
+ if (length > (ulonglong) INT_MAX32)
{
- Longlong_hybrid nr= item->to_longlong_hybrid();
- if (!item->null_value && !nr.neg())
- {
- // Assume that the maximum length of a String is < INT_MAX32
- m_count= (ulonglong) nr.value();
- if (m_count > (ulonglong) INT_MAX32)
- m_count= (ulonglong) INT_MAX32;
- }
+ /* Limit string length to maxium string length in MariaDB (2G) */
+ length= item->unsigned_flag ? (ulonglong) INT_MAX32 : 0;
}
- ulonglong count() const { return m_count; }
-};
+ return (uint32) length;
+}
/*
@@ -842,7 +837,7 @@ String *Item_func_des_decrypt::val_str(String *str)
{
uint key_number=(uint) (*res)[0] & 127;
// Check if automatic key and that we have privilege to uncompress using it
- if (!(current_thd->security_ctx->master_access & SUPER_ACL) ||
+ if (!(current_thd->security_ctx->master_access & PRIV_DES_DECRYPT_ONE_ARG) ||
key_number > 9)
goto error;
@@ -1103,7 +1098,7 @@ String *Item_func_reverse::val_str(String *str)
end= res->end();
tmp= (char *) str->end();
#ifdef USE_MB
- if (use_mb(res->charset()))
+ if (res->use_mb())
{
uint32 l;
while (ptr < end)
@@ -1176,7 +1171,7 @@ String *Item_func_replace::val_str_internal(String *str,
res->set_charset(collation.collation);
#ifdef USE_MB
- binary_cmp = ((res->charset()->state & MY_CS_BINSORT) || !use_mb(res->charset()));
+ binary_cmp = ((res->charset()->state & MY_CS_BINSORT) || !res->use_mb());
#endif
if (res2->length() == 0)
@@ -1306,13 +1301,6 @@ bool Item_func_replace::fix_length_and_dec()
/*********************************************************************/
-bool Item_func_regexp_replace::fix_fields(THD *thd, Item **ref)
-{
- re.set_recursion_limit(thd);
- return Item_str_func::fix_fields(thd, ref);
-}
-
-
bool Item_func_regexp_replace::fix_length_and_dec()
{
if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
@@ -1342,8 +1330,8 @@ bool Item_func_regexp_replace::append_replacement(String *str,
my_wc_t wc;
int cnv, n;
- if ((cnv= cs->cset->mb_wc(cs, &wc, (const uchar *) beg,
- (const uchar *) end)) < 1)
+ if ((cnv= cs->mb_wc(&wc, (const uchar *) beg,
+ (const uchar *) end)) < 1)
break; /* End of line */
beg+= cnv;
@@ -1354,8 +1342,8 @@ bool Item_func_regexp_replace::append_replacement(String *str,
continue;
}
- if ((cnv= cs->cset->mb_wc(cs, &wc, (const uchar *) beg,
- (const uchar *) end)) < 1)
+ if ((cnv= cs->mb_wc(&wc, (const uchar *) beg,
+ (const uchar *) end)) < 1)
break; /* End of line */
beg+= cnv;
@@ -1364,7 +1352,7 @@ bool Item_func_regexp_replace::append_replacement(String *str,
if (n < re.nsubpatterns())
{
/* A valid sub-pattern reference found */
- int pbeg= re.subpattern_start(n), plength= re.subpattern_end(n) - pbeg;
+ size_t pbeg= re.subpattern_start(n), plength= re.subpattern_end(n) - pbeg;
if (str->append(source->str + pbeg, plength, cs))
return true;
}
@@ -1393,7 +1381,7 @@ String *Item_func_regexp_replace::val_str(String *str)
String *source= args[0]->val_str(&tmp0);
String *replace= args[2]->val_str(&tmp2);
LEX_CSTRING src, rpl;
- int startoffset= 0;
+ size_t startoffset= 0;
if ((null_value= (args[0]->null_value || args[2]->null_value ||
re.recompile(args[1]))))
@@ -1403,8 +1391,8 @@ String *Item_func_regexp_replace::val_str(String *str)
!(replace= re.convert_if_needed(replace, &re.replace_converter)))
goto err;
- src= source->lex_cstring();
- rpl= replace->lex_cstring();
+ source->get_value(&src);
+ replace->get_value(&rpl);
str->length(0);
str->set_charset(collation.collation);
@@ -1422,7 +1410,8 @@ String *Item_func_regexp_replace::val_str(String *str)
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()))
+ if (str->append(src.str + startoffset, src.length - startoffset,
+ re.library_charset()))
goto err;
return str;
}
@@ -1431,7 +1420,8 @@ String *Item_func_regexp_replace::val_str(String *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()))
+ if (str->append(src.str + startoffset,
+ re.subpattern_start(0) - startoffset, re.library_charset()))
goto err;
// Append replacement
@@ -1449,13 +1439,6 @@ err:
}
-bool Item_func_regexp_substr::fix_fields(THD *thd, Item **ref)
-{
- re.set_recursion_limit(thd);
- return Item_str_func::fix_fields(thd, ref);
-}
-
-
bool Item_func_regexp_substr::fix_length_and_dec()
{
if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))
@@ -1490,8 +1473,7 @@ String *Item_func_regexp_substr::val_str(String *str)
return str;
if (str->append(source->ptr() + re.subpattern_start(0),
- re.subpattern_end(0) - re.subpattern_start(0),
- re.library_charset()))
+ re.subpattern_length(0), re.library_charset()))
goto err;
return str;
@@ -1656,8 +1638,8 @@ void Item_str_func::left_right_max_length()
uint32 char_length= args[0]->max_char_length();
if (args[1]->const_item() && !args[1]->is_expensive())
{
- Repeat_count tmp(args[1]);
- set_if_smaller(char_length, (uint) tmp.count());
+ uint32 length= max_length_for_string(args[1]);
+ set_if_smaller(char_length, length);
}
fix_char_length(char_length);
}
@@ -1818,7 +1800,7 @@ String *Item_func_substr_index::val_str(String *str)
res->set_charset(collation.collation);
#ifdef USE_MB
- if (use_mb(res->charset()))
+ if (res->use_mb())
{
const char *ptr= res->ptr();
const char *strend= ptr+res->length();
@@ -2023,7 +2005,7 @@ String *Item_func_rtrim::val_str(String *str)
{
char chr=(*remove_str)[0];
#ifdef USE_MB
- if (use_mb(collation.collation))
+ if (collation.collation->use_mb())
{
while (ptr < end)
{
@@ -2040,7 +2022,7 @@ String *Item_func_rtrim::val_str(String *str)
{
const char *r_ptr=remove_str->ptr();
#ifdef USE_MB
- if (use_mb(collation.collation))
+ if (collation.collation->use_mb())
{
loop:
while (ptr + remove_length < end)
@@ -2099,7 +2081,7 @@ String *Item_func_trim::val_str(String *str)
while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length))
ptr+=remove_length;
#ifdef USE_MB
- if (use_mb(collation.collation))
+ if (collation.collation->use_mb())
{
char *p=ptr;
uint32 l;
@@ -2570,10 +2552,10 @@ String *Item_func_soundex::val_str(String *str)
for ( ; ; ) /* Skip pre-space */
{
- if ((rc= cs->cset->mb_wc(cs, &wc, (uchar*) from, (uchar*) end)) <= 0)
+ if ((rc= cs->mb_wc(&wc, (uchar*) from, (uchar*) end)) <= 0)
return make_empty_result(); /* EOL or invalid byte sequence */
- if (rc == 1 && cs->ctype)
+ if (rc == 1 && cs->m_ctype)
{
/* Single byte letter found */
if (my_isalpha(cs, *from))
@@ -2592,7 +2574,7 @@ String *Item_func_soundex::val_str(String *str)
/* Multibyte letter found */
wc= soundex_toupper(wc);
last_ch= get_scode(wc); // Code of the first letter
- if ((rc= cs->cset->wc_mb(cs, wc, (uchar*) to, (uchar*) to_end)) <= 0)
+ if ((rc= cs->wc_mb(wc, (uchar*) to, (uchar*) to_end)) <= 0)
{
/* Extra safety - should not really happen */
DBUG_ASSERT(false);
@@ -2610,10 +2592,10 @@ String *Item_func_soundex::val_str(String *str)
*/
for (nchars= 1 ; ; )
{
- if ((rc= cs->cset->mb_wc(cs, &wc, (uchar*) from, (uchar*) end)) <= 0)
+ if ((rc= cs->mb_wc(&wc, (uchar*) from, (uchar*) end)) <= 0)
break; /* EOL or invalid byte sequence */
- if (rc == 1 && cs->ctype)
+ if (rc == 1 && cs->m_ctype)
{
if (!my_isalpha(cs, *from++))
continue;
@@ -2629,8 +2611,7 @@ String *Item_func_soundex::val_str(String *str)
if ((ch != '0') && (ch != last_ch)) // if not skipped or double
{
// letter, copy to output
- if ((rc= cs->cset->wc_mb(cs, (my_wc_t) ch,
- (uchar*) to, (uchar*) to_end)) <= 0)
+ if ((rc= cs->wc_mb((my_wc_t) ch, (uchar*) to, (uchar*) to_end)) <= 0)
{
// Extra safety - should not really happen
DBUG_ASSERT(false);
@@ -2646,7 +2627,7 @@ String *Item_func_soundex::val_str(String *str)
if (nchars < 4)
{
uint nbytes= (4 - nchars) * cs->mbminlen;
- cs->cset->fill(cs, to, nbytes, '0');
+ cs->fill(to, nbytes, '0');
to+= nbytes;
}
@@ -3048,8 +3029,8 @@ bool Item_func_repeat::fix_length_and_dec()
DBUG_ASSERT(collation.collation != NULL);
if (args[1]->const_item() && !args[1]->is_expensive())
{
- Repeat_count tmp(args[1]);
- ulonglong char_length= (ulonglong) args[0]->max_char_length() * tmp.count();
+ uint32 length= max_length_for_string(args[1]);
+ ulonglong char_length= (ulonglong) args[0]->max_char_length() * length;
fix_char_length_ulonglong(char_length);
return false;
}
@@ -3122,7 +3103,7 @@ bool Item_func_space::fix_length_and_dec()
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
if (args[0]->const_item() && !args[0]->is_expensive())
{
- fix_char_length_ulonglong(Repeat_count(args[0]).count());
+ fix_char_length_ulonglong(max_length_for_string(args[0]));
return false;
}
max_length= MAX_BLOB_WIDTH;
@@ -3168,7 +3149,7 @@ String *Item_func_space::val_str(String *str)
goto err;
str->length(tot_length);
str->set_charset(cs);
- cs->cset->fill(cs, (char*) str->ptr(), tot_length, ' ');
+ cs->fill((char*) str->ptr(), tot_length, ' ');
return str;
err:
@@ -3241,7 +3222,7 @@ bool Item_func_pad::fix_length_and_dec()
DBUG_ASSERT(collation.collation->mbmaxlen > 0);
if (args[1]->const_item() && !args[1]->is_expensive())
{
- fix_char_length_ulonglong(Repeat_count(args[1]).count());
+ fix_char_length_ulonglong(max_length_for_string(args[1]));
return false;
}
max_length= MAX_BLOB_WIDTH;
@@ -3502,11 +3483,11 @@ String *Item_func_conv::val_str(String *str)
else
{
if (from_base < 0)
- dec= my_strntoll(res->charset(), res->ptr(), res->length(),
- -from_base, &endptr, &err);
+ dec= res->charset()->strntoll(res->ptr(), res->length(),
+ -from_base, &endptr, &err);
else
- dec= (longlong) my_strntoull(res->charset(), res->ptr(), res->length(),
- from_base, &endptr, &err);
+ dec= (longlong) res->charset()->strntoull(res->ptr(), res->length(),
+ from_base, &endptr, &err);
}
if (!(ptr= longlong2str(dec, ans, to_base)) ||
@@ -3628,7 +3609,7 @@ bool Item_func_weight_string::fix_length_and_dec()
size_t char_length;
char_length= ((cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS) || !nweights) ?
args[0]->max_char_length() : nweights * cs->levels_for_order;
- max_length= (uint32)cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen);
+ max_length= (uint32) cs->strnxfrmlen(char_length * cs->mbmaxlen);
}
maybe_null= 1;
return FALSE;
@@ -3679,7 +3660,7 @@ String *Item_func_weight_string::val_str(String *str)
char_length= (flags & MY_STRXFRM_PAD_WITH_SPACE) ?
res->numchars() : (res->length() / cs->mbminlen);
}
- tmp_length= cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen);
+ tmp_length= cs->strnxfrmlen(char_length * cs->mbmaxlen);
}
{
@@ -3698,11 +3679,10 @@ String *Item_func_weight_string::val_str(String *str)
if (str->alloc(tmp_length))
goto nl;
- frm_length= cs->coll->strnxfrm(cs,
- (uchar *) str->ptr(), tmp_length,
- nweights ? nweights : (uint)tmp_length,
- (const uchar *) res->ptr(), res->length(),
- flags);
+ frm_length= cs->strnxfrm((char*) str->ptr(), tmp_length,
+ nweights ? nweights : (uint) tmp_length,
+ res->ptr(), res->length(),
+ flags);
DBUG_ASSERT(frm_length <= tmp_length);
str->length(frm_length);
@@ -3822,10 +3802,10 @@ String *Item_func_like_range::val_str(String *str)
goto err;
null_value=0;
- if (cs->coll->like_range(cs, res->ptr(), res->length(),
- '\\', '_', '%', (size_t)nbytes,
- (char*) min_str.ptr(), (char*) max_str.ptr(),
- &min_len, &max_len))
+ if (cs->like_range(res->ptr(), res->length(),
+ '\\', '_', '%', (size_t)nbytes,
+ (char*) min_str.ptr(), (char*) max_str.ptr(),
+ &min_len, &max_len))
goto err;
min_str.set_charset(collation.collation);
@@ -4094,7 +4074,7 @@ String *Item_func_quote::val_str(String *str)
to_end= (uchar*) to + new_length;
/* Put leading quote */
- if ((mblen= cs->cset->wc_mb(cs, '\'', (uchar *) to, to_end)) <= 0)
+ if ((mblen= cs->wc_mb('\'', (uchar *) to, to_end)) <= 0)
goto toolong;
to+= mblen;
@@ -4102,7 +4082,7 @@ String *Item_func_quote::val_str(String *str)
{
my_wc_t wc;
bool escape;
- if ((mblen= cs->cset->mb_wc(cs, &wc, (uchar*) start, (uchar*) end)) <= 0)
+ if ((mblen= cs->mb_wc(&wc, (uchar*) start, (uchar*) end)) <= 0)
goto null;
start+= mblen;
switch (wc) {
@@ -4114,17 +4094,17 @@ String *Item_func_quote::val_str(String *str)
}
if (escape)
{
- if ((mblen= cs->cset->wc_mb(cs, '\\', (uchar*) to, to_end)) <= 0)
+ if ((mblen= cs->wc_mb('\\', (uchar*) to, to_end)) <= 0)
goto toolong;
to+= mblen;
}
- if ((mblen= cs->cset->wc_mb(cs, wc, (uchar*) to, to_end)) <= 0)
+ if ((mblen= cs->wc_mb(wc, (uchar*) to, to_end)) <= 0)
goto toolong;
to+= mblen;
}
/* Put trailing quote */
- if ((mblen= cs->cset->wc_mb(cs, '\'', (uchar *) to, to_end)) <= 0)
+ if ((mblen= cs->wc_mb('\'', (uchar *) to, to_end)) <= 0)
goto toolong;
to+= mblen;
new_length= (uint)(to - str->ptr());
@@ -4437,24 +4417,7 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg)
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;
- }
-
+ type= args[valpos]->type_handler()->dyncol_type(args[valpos]);
if (type == DYN_COL_STRING &&
args[valpos]->type() == Item::FUNC_ITEM &&
((Item_func *)args[valpos])->functype() == DYNCOL_FUNC)
@@ -4471,63 +4434,7 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg)
uint valpos= i * 2 + 1;
DYNAMIC_COLUMN_TYPE type= defs[i].type;
if (type == DYN_COL_NULL) // auto detect
- {
- /*
- We don't have a default here to ensure we get a warning if
- one adds a new not handled MYSQL_TYPE_...
- */
- switch (args[valpos]->field_type()) {
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- type= DYN_COL_DECIMAL;
- break;
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_BIT:
- type= args[valpos]->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT;
- break;
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- type= DYN_COL_DOUBLE;
- break;
- case MYSQL_TYPE_NULL:
- 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:
- case MYSQL_TYPE_NEWDATE:
- type= DYN_COL_DATE;
- break;
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_TIME2:
- type= DYN_COL_TIME;
- break;
- 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;
- case MYSQL_TYPE_VARCHAR_COMPRESSED:
- case MYSQL_TYPE_BLOB_COMPRESSED:
- DBUG_ASSERT(0);
- }
- }
+ type= args[valpos]->type_handler()->dyncol_type(args[valpos]);
if (type == DYN_COL_STRING &&
args[valpos]->type() == Item::FUNC_ITEM &&
((Item_func *)args[valpos])->functype() == DYNCOL_FUNC)
@@ -5105,8 +5012,8 @@ double Item_dyncol_get::val_real()
{
int error;
char *end;
- double res= my_strntod(val.x.string.charset, (char*) val.x.string.value.str,
- val.x.string.value.length, &end, &error);
+ double res= val.x.string.charset->strntod((char*) val.x.string.value.str,
+ val.x.string.value.length, &end, &error);
if (end != (char*) val.x.string.value.str + val.x.string.value.length ||
error)
@@ -5350,28 +5257,33 @@ String *Item_temptable_rowid::val_str(String *str)
str_value.set((char*)(table->file->ref), max_length, &my_charset_bin);
return &str_value;
}
+
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
+/* Format is %d-%d-%llu */
+#define WSREP_MAX_WSREP_SERVER_GTID_STR_LEN 10+1+10+1+20
String *Item_func_wsrep_last_written_gtid::val_str_ascii(String *str)
{
- wsrep::gtid gtid= current_thd->wsrep_cs().last_written_gtid();
- if (gtid_str.alloc(wsrep::gtid_c_str_len()))
+ if (gtid_str.alloc(WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1))
{
- my_error(ER_OUTOFMEMORY, wsrep::gtid_c_str_len());
- null_value= true;
- return NULL;
+ my_error(ER_OUTOFMEMORY, WSREP_MAX_WSREP_SERVER_GTID_STR_LEN);
+ null_value= TRUE;
+ return 0;
}
- ssize_t gtid_len= gtid_print_to_c_str(gtid, (char*) gtid_str.ptr(),
- wsrep::gtid_c_str_len());
+ ssize_t gtid_len= my_snprintf((char*)gtid_str.ptr(),
+ WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1,
+ "%u-%u-%llu", wsrep_gtid_server.domain_id,
+ wsrep_gtid_server.server_id,
+ current_thd->wsrep_last_written_gtid_seqno);
if (gtid_len < 0)
{
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
- "wsrep_gtid_print failed");
- null_value= true;
- return NULL;
+ "wsrep_gtid_print failed");
+ null_value= TRUE;
+ return 0;
}
gtid_str.length(gtid_len);
return &gtid_str;
@@ -5379,27 +5291,23 @@ String *Item_func_wsrep_last_written_gtid::val_str_ascii(String *str)
String *Item_func_wsrep_last_seen_gtid::val_str_ascii(String *str)
{
- wsrep::gtid gtid= wsrep::gtid::undefined();
- if (Wsrep_server_state::instance().is_provider_loaded())
- {
- /* TODO: Should call Wsrep_server_state.instance().last_committed_gtid()
- instead. */
- gtid= Wsrep_server_state::instance().provider().last_committed_gtid();
- }
- if (gtid_str.alloc(wsrep::gtid_c_str_len()))
+ if (gtid_str.alloc(WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1))
{
- my_error(ER_OUTOFMEMORY, wsrep::gtid_c_str_len());
- null_value= true;
- return NULL;
+ my_error(ER_OUTOFMEMORY, WSREP_MAX_WSREP_SERVER_GTID_STR_LEN);
+ null_value= TRUE;
+ return 0;
}
- ssize_t gtid_len= wsrep::gtid_print_to_c_str(gtid, (char*) gtid_str.ptr(),
- wsrep::gtid_c_str_len());
+ ssize_t gtid_len= my_snprintf((char*)gtid_str.ptr(),
+ WSREP_MAX_WSREP_SERVER_GTID_STR_LEN+1,
+ "%u-%u-%llu", wsrep_gtid_server.domain_id,
+ wsrep_gtid_server.server_id,
+ wsrep_gtid_server.seqno());
if (gtid_len < 0)
{
my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), func_name(),
"wsrep_gtid_print failed");
- null_value= true;
- return NULL;
+ null_value= TRUE;
+ return 0;
}
gtid_str.length(gtid_len);
return &gtid_str;
@@ -5407,49 +5315,59 @@ String *Item_func_wsrep_last_seen_gtid::val_str_ascii(String *str)
longlong Item_func_wsrep_sync_wait_upto::val_int()
{
- int timeout= -1;
- String* gtid_str= args[0]->val_str(&value);
- if (gtid_str == NULL)
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
- return 0LL;
- }
-
- if (arg_count == 2)
- {
- timeout= args[1]->val_int();
- }
+ String *gtid_str __attribute__((unused)) = args[0]->val_str(&value);
+ null_value=0;
+ uint timeout;
+ rpl_gtid *gtid_list;
+ uint32 count;
+ int wait_gtid_ret= 0;
+ int ret= 1;
- wsrep_gtid_t gtid;
- int gtid_len= wsrep_gtid_scan(gtid_str->ptr(), gtid_str->length(), &gtid);
- if (gtid_len < 0)
+ if (args[0]->null_value)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
- return 0LL;
+ null_value= TRUE;
+ return 0;
}
- if (gtid.seqno == WSREP_SEQNO_UNDEFINED &&
- wsrep_uuid_compare(&gtid.uuid, &WSREP_UUID_UNDEFINED) == 0)
+ if (arg_count==2 && !args[1]->null_value)
+ timeout= (uint)(args[1]->val_real());
+ else
+ timeout= (uint)-1;
+
+ if (!(gtid_list= gtid_parse_string_to_list(gtid_str->ptr(), gtid_str->length(),
+ &count)))
{
- return 1LL;
+ my_error(ER_INCORRECT_GTID_STATE, MYF(0), func_name());
+ null_value= TRUE;
+ return 0;
}
-
- enum wsrep::provider::status status=
- wsrep_sync_wait_upto(current_thd, &gtid, timeout);
-
- if (status)
+ if (count == 1)
{
- int err;
- switch (status) {
- case wsrep::provider::error_transaction_missing:
- err= ER_WRONG_ARGUMENTS;
- break;
- default:
- err= ER_LOCK_WAIT_TIMEOUT;
+ if (wsrep_check_gtid_seqno(gtid_list[0].domain_id, gtid_list[0].server_id,
+ gtid_list[0].seq_no))
+ {
+ wait_gtid_ret= wsrep_gtid_server.wait_gtid_upto(gtid_list[0].seq_no, timeout);
+ if ((wait_gtid_ret == ETIMEDOUT) || (wait_gtid_ret == ETIME))
+ {
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0), func_name());
+ ret= 0;
+ }
+ else if (wait_gtid_ret == ENOMEM)
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), func_name());
+ ret= 0;
+ }
}
- my_error(err, MYF(0), func_name());
- return 0LL;
}
- return 1LL;
+ else
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), func_name());
+ null_value= TRUE;
+ ret= 0;
+ }
+ my_free(gtid_list);
+ return ret;
}
+
#endif /* WITH_WSREP */
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index c13c49b8363..1ae62a4a6e8 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -368,13 +368,12 @@ public:
{}
void cleanup()
{
- DBUG_ENTER("Item_func_regex::cleanup");
+ DBUG_ENTER("Item_func_regexp_replace::cleanup");
Item_str_func::cleanup();
re.cleanup();
DBUG_VOID_RETURN;
}
String *val_str(String *str);
- bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
const char *func_name() const { return "regexp_replace"; }
Item *get_copy(THD *thd) { return 0;}
@@ -390,13 +389,12 @@ public:
{}
void cleanup()
{
- DBUG_ENTER("Item_func_regex::cleanup");
+ DBUG_ENTER("Item_func_regexp_substr::cleanup");
Item_str_func::cleanup();
re.cleanup();
DBUG_VOID_RETURN;
}
String *val_str(String *str);
- bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
const char *func_name() const { return "regexp_substr"; }
Item *get_copy(THD *thd) { return 0; }
@@ -1671,8 +1669,7 @@ public:
Item_func_uuid(THD *thd): Item_str_func(thd) {}
bool fix_length_and_dec()
{
- collation.set(system_charset_info,
- DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ collation.set(DTCollation_numeric());
fix_char_length(MY_UUID_STRING_LENGTH);
return FALSE;
}
@@ -1810,8 +1807,8 @@ public:
TABLE *table;
Item_temptable_rowid(TABLE *table_arg);
const Type_handler *type_handler() const { return &type_handler_string; }
- Field *create_tmp_field(bool group, TABLE *table)
- { return create_table_field_from_handler(table); }
+ Field *create_tmp_field(MEM_ROOT *root, bool group, TABLE *table)
+ { return create_table_field_from_handler(root, table); }
String *val_str(String *str);
enum Functype functype() const { return TEMPTABLE_ROWID; }
const char *func_name() const { return "<rowid>"; }
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 7887724358d..cbde9599073 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -47,6 +47,8 @@ double get_post_group_estimate(JOIN* join, double join_op_rows);
LEX_CSTRING exists_outer_expr_name= { STRING_WITH_LEN("<exists outer expr>") };
+LEX_CSTRING no_matter_name= {STRING_WITH_LEN("<no matter>") };
+
int check_and_do_in_subquery_rewrites(JOIN *join);
Item_subselect::Item_subselect(THD *thd_arg):
@@ -659,6 +661,40 @@ bool Item_subselect::is_expensive()
}
+bool Item_subselect::unknown_splocal_processor(void *argument)
+{
+ SELECT_LEX *sl= unit->first_select();
+ if (sl->top_join_list.elements)
+ return 0;
+ if (sl->tvc && sl->tvc->walk_values(&Item::unknown_splocal_processor,
+ false, argument))
+ return true;
+ for (SELECT_LEX *lex= unit->first_select(); lex; lex= lex->next_select())
+ {
+ /*
+ TODO: walk through GROUP BY and ORDER yet eventually.
+ This will require checking aliases in SELECT list:
+ SELECT 1 AS a GROUP BY a;
+ SELECT 1 AS a ORDER BY a;
+ */
+ List_iterator<Item> li(lex->item_list);
+ Item *item;
+ if (lex->where && (lex->where)->walk(&Item::unknown_splocal_processor,
+ false, argument))
+ return true;
+ if (lex->having && (lex->having)->walk(&Item::unknown_splocal_processor,
+ false, argument))
+ return true;
+ while ((item=li++))
+ {
+ if (item->walk(&Item::unknown_splocal_processor, false, argument))
+ return true;
+ }
+ }
+ return false;
+}
+
+
bool Item_subselect::walk(Item_processor processor, bool walk_subquery,
void *argument)
{
@@ -726,8 +762,8 @@ bool Item_subselect::exec()
QT_WITHOUT_INTRODUCERS));
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_UNKNOWN_ERROR, "DBUG: Item_subselect::exec %.*s",
- print.length(),print.ptr());
+ ER_UNKNOWN_ERROR, "DBUG: Item_subselect::exec %.*b",
+ print.length(),print.ptr());
);
/*
Do not execute subselect in case of a fatal error
@@ -1938,8 +1974,8 @@ Item_in_subselect::single_value_transformer(JOIN *join)
*/
expr= new (thd->mem_root) Item_direct_ref(thd, &select_lex->context,
(Item**)optimizer->get_cache(),
- "<no matter>",
- &in_left_expr_name);
+ no_matter_name,
+ in_left_expr_name);
}
DBUG_RETURN(false);
@@ -2170,8 +2206,8 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
this,
&select_lex->
ref_pointer_array[0],
- (char *)"<ref>",
- &field_name));
+ {STRING_WITH_LEN("<ref>")},
+ field_name));
if (!abort_on_null && left_expr->maybe_null)
{
/*
@@ -2255,8 +2291,8 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
&select_lex->context,
this,
&select_lex->ref_pointer_array[0],
- (char *)"<no matter>",
- &field_name));
+ no_matter_name,
+ field_name));
if (!abort_on_null && left_expr->maybe_null)
{
disable_cond_guard_for_const_null_left_expr(0);
@@ -2441,21 +2477,21 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_direct_ref(thd, &select_lex->context,
(*optimizer->get_cache())->
addr(i),
- (char *)"<no matter>",
- &in_left_expr_name),
+ no_matter_name,
+ in_left_expr_name),
new (thd->mem_root)
Item_ref(thd, &select_lex->context,
&select_lex->ref_pointer_array[i],
- (char *)"<no matter>",
- &list_ref));
+ no_matter_name,
+ list_ref));
Item *item_isnull=
new (thd->mem_root)
Item_func_isnull(thd,
new (thd->mem_root)
Item_ref(thd, &select_lex->context,
&select_lex->ref_pointer_array[i],
- (char *)"<no matter>",
- &list_ref));
+ no_matter_name,
+ list_ref));
Item *col_item= new (thd->mem_root)
Item_cond_or(thd, item_eq, item_isnull);
if (!abort_on_null && left_expr->element_index(i)->maybe_null &&
@@ -2475,8 +2511,8 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_ref(thd, &select_lex->context,
&select_lex->
ref_pointer_array[i],
- (char *)"<no matter>",
- &list_ref));
+ no_matter_name,
+ list_ref));
if (!abort_on_null && left_expr->element_index(i)->maybe_null &&
get_cond_guard(i) )
{
@@ -2510,14 +2546,14 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_direct_ref(thd, &select_lex->context,
(*optimizer->get_cache())->
addr(i),
- (char *)"<no matter>",
- &in_left_expr_name),
+ no_matter_name,
+ in_left_expr_name),
new (thd->mem_root)
Item_direct_ref(thd, &select_lex->context,
&select_lex->
ref_pointer_array[i],
- (char *)"<no matter>",
- &list_ref));
+ no_matter_name,
+ list_ref));
if (!abort_on_null && select_lex->ref_pointer_array[i]->maybe_null)
{
Item *having_col_item=
@@ -2526,8 +2562,8 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
new (thd->mem_root)
Item_ref(thd, &select_lex->context,
&select_lex->ref_pointer_array[i],
- (char *)"<no matter>",
- &list_ref));
+ no_matter_name,
+ list_ref));
item_isnull= new (thd->mem_root)
Item_func_isnull(thd,
@@ -2535,8 +2571,8 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_direct_ref(thd, &select_lex->context,
&select_lex->
ref_pointer_array[i],
- (char *)"<no matter>",
- &list_ref));
+ no_matter_name,
+ list_ref));
item= new (thd->mem_root) Item_cond_or(thd, item, item_isnull);
if (left_expr->element_index(i)->maybe_null && get_cond_guard(i))
{
@@ -2745,7 +2781,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
join_arg->thd->change_item_tree(&unit->global_parameters()->select_limit,
new (thd->mem_root)
Item_int(thd, (int32) 1));
- unit->select_limit_cnt= 1;
+ unit->lim.set_single_row();
DBUG_RETURN(false);
}
@@ -3102,8 +3138,8 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
in_subs->expr= new (thd->mem_root)
Item_direct_ref(thd, &first_select->context,
(Item**)optimizer->get_cache(),
- (char *)"<no matter>",
- &in_left_expr_name);
+ no_matter_name,
+ in_left_expr_name);
if (in_subs->fix_fields(thd, optimizer->arguments() + 1))
{
res= TRUE;
@@ -3173,8 +3209,8 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
Item_direct_ref(thd,
&unit->outer_select()->context,
optimizer->arguments(),
- (char *)"<no matter>",
- &exists_outer_expr_name)),
+ no_matter_name,
+ exists_outer_expr_name)),
optimizer) :
(Item *)optimizer);
}
@@ -3197,8 +3233,8 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg)
Item_direct_ref(thd,
&unit->outer_select()->context,
optimizer->arguments()[0]->addr((int)i),
- (char *)"<no matter>",
- &exists_outer_expr_name)),
+ no_matter_name,
+ exists_outer_expr_name)),
thd->mem_root);
}
}
@@ -3737,7 +3773,6 @@ int subselect_single_select_engine::prepare(THD *thd)
SELECT_LEX *save_select= thd->lex->current_select;
thd->lex->current_select= select_lex;
if (join->prepare(select_lex->table_list.first,
- select_lex->with_wild,
select_lex->where,
select_lex->order_list.elements +
select_lex->group_list.elements,
@@ -4122,7 +4157,8 @@ int subselect_uniquesubquery_engine::exec()
TABLE *table= tab->table;
empty_result_set= TRUE;
table->status= 0;
- Item_in_subselect *in_subs= (Item_in_subselect *) item;
+ Item_in_subselect *in_subs= item->get_IN_subquery();
+ DBUG_ASSERT(in_subs);
if (!tab->preread_init_done && tab->preread_init())
DBUG_RETURN(1);
@@ -4167,11 +4203,11 @@ int subselect_uniquesubquery_engine::exec()
table->null_row= 0;
if (!table->status && (!cond || cond->val_int()))
{
- ((Item_in_subselect *) item)->value= 1;
+ in_subs->value= 1;
empty_result_set= FALSE;
}
else
- ((Item_in_subselect *) item)->value= 0;
+ in_subs->value= 0;
}
DBUG_RETURN(error != 0);
@@ -4210,9 +4246,9 @@ int subselect_uniquesubquery_engine::index_lookup()
table->null_row= 0;
if (!error && (!cond || cond->val_int()))
- ((Item_in_subselect *) item)->value= 1;
+ item->get_IN_subquery()->value= 1;
else
- ((Item_in_subselect *) item)->value= 0;
+ item->get_IN_subquery()->value= 0;
DBUG_RETURN(0);
}
@@ -4282,9 +4318,9 @@ int subselect_indexsubquery_engine::exec()
int error;
bool null_finding= 0;
TABLE *table= tab->table;
- Item_in_subselect *in_subs= (Item_in_subselect *) item;
+ Item_in_subselect *in_subs= item->get_IN_subquery();
- ((Item_in_subselect *) item)->value= 0;
+ in_subs->value= 0;
empty_result_set= TRUE;
table->status= 0;
@@ -4292,7 +4328,7 @@ int subselect_indexsubquery_engine::exec()
{
/* We need to check for NULL if there wasn't a matching value */
*tab->ref.null_ref_key= 0; // Search first for not null
- ((Item_in_subselect *) item)->was_null= 0;
+ in_subs->was_null= 0;
}
if (!tab->preread_init_done && tab->preread_init())
@@ -4344,9 +4380,9 @@ int subselect_indexsubquery_engine::exec()
{
empty_result_set= FALSE;
if (null_finding)
- ((Item_in_subselect *) item)->was_null= 1;
+ in_subs->was_null= 1;
else
- ((Item_in_subselect *) item)->value= 1;
+ in_subs->value= 1;
break;
}
error= table->file->ha_index_next_same(table->record[0],
@@ -4452,9 +4488,10 @@ void subselect_single_select_engine::print(String *str,
enum_query_type query_type)
{
With_clause* with_clause= select_lex->get_with_clause();
+ THD *thd= get_thd();
if (with_clause)
- with_clause->print(str, query_type);
- select_lex->print(get_thd(), str, query_type);
+ with_clause->print(thd, str, query_type);
+ select_lex->print(thd, str, query_type);
}
@@ -4731,7 +4768,7 @@ bool subselect_uniquesubquery_engine::no_tables()
subselect_hash_sj_engine::exec_strategy
subselect_hash_sj_engine::get_strategy_using_schema()
{
- Item_in_subselect *item_in= (Item_in_subselect *) item;
+ Item_in_subselect *item_in= item->get_IN_subquery();
if (item_in->is_top_level_item())
return COMPLETE_MATCH;
@@ -4778,7 +4815,7 @@ subselect_hash_sj_engine::get_strategy_using_schema()
subselect_hash_sj_engine::exec_strategy
subselect_hash_sj_engine::get_strategy_using_data()
{
- Item_in_subselect *item_in= (Item_in_subselect *) item;
+ Item_in_subselect *item_in= item->get_IN_subquery();
select_materialize_with_stats *result_sink=
(select_materialize_with_stats *) result;
Item *outer_col;
@@ -5029,8 +5066,9 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
DBUG_RETURN(TRUE);
result_sink->get_tmp_table_param()->materialized_subquery= true;
- if (item->substype() == Item_subselect::IN_SUBS &&
- ((Item_in_subselect*)item)->is_jtbm_merged)
+
+ if (item->substype() == Item_subselect::IN_SUBS &&
+ (item->get_IN_subquery()->is_jtbm_merged))
{
result_sink->get_tmp_table_param()->force_not_null_cols= true;
}
@@ -5070,9 +5108,12 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
/*
Make sure there is only one index on the temp table, and it doesn't have
the extra key part created when s->uniques > 0.
+
+ NOTE: item have to be Item_in_subselect, because class constructor
+ accept Item_in_subselect as the parmeter.
*/
DBUG_ASSERT(tmp_table->s->keys == 1 &&
- ((Item_in_subselect *) item)->left_expr->cols() ==
+ item->get_IN_subquery()->left_expr->cols() ==
tmp_table->key_info->user_defined_key_parts);
if (make_semi_join_conds() ||
@@ -5121,7 +5162,7 @@ bool subselect_hash_sj_engine::make_semi_join_conds()
TABLE_LIST *tmp_table_ref;
/* Name resolution context for all tmp_table columns created below. */
Name_resolution_context *context;
- Item_in_subselect *item_in= (Item_in_subselect *) item;
+ Item_in_subselect *item_in= item->get_IN_subquery();
LEX_CSTRING table_name;
DBUG_ENTER("subselect_hash_sj_engine::make_semi_join_conds");
DBUG_ASSERT(semi_join_conds == NULL);
@@ -5183,7 +5224,7 @@ bool subselect_hash_sj_engine::make_semi_join_conds()
subselect_uniquesubquery_engine*
subselect_hash_sj_engine::make_unique_engine()
{
- Item_in_subselect *item_in= (Item_in_subselect *) item;
+ Item_in_subselect *item_in= item->get_IN_subquery();
Item_iterator_row it(item_in->left_expr);
/* The only index on the temporary table. */
KEY *tmp_key= tmp_table->key_info;
@@ -5205,7 +5246,7 @@ subselect_hash_sj_engine::make_unique_engine()
tab->preread_init_done= FALSE;
tab->ref.tmp_table_index_lookup_init(thd, tmp_key, it, FALSE);
- DBUG_RETURN(new subselect_uniquesubquery_engine(thd, tab, item,
+ DBUG_RETURN(new subselect_uniquesubquery_engine(thd, tab, item_in,
semi_join_conds));
}
@@ -5252,7 +5293,7 @@ void subselect_hash_sj_engine::cleanup()
at parse time and stored across executions, while all other materialization
related engines are created and chosen for each execution.
*/
- ((Item_in_subselect *) item)->engine= materialize_engine;
+ item->get_IN_subquery()->engine= materialize_engine;
if (lookup_engine_type == TABLE_SCAN_ENGINE ||
lookup_engine_type == ROWID_MERGE_ENGINE)
{
@@ -5492,7 +5533,7 @@ double get_post_group_estimate(JOIN* join, double join_op_rows)
int subselect_hash_sj_engine::exec()
{
- Item_in_subselect *item_in= (Item_in_subselect *) item;
+ Item_in_subselect *item_in= item->get_IN_subquery();
SELECT_LEX *save_select= thd->lex->current_select;
subselect_partial_match_engine *pm_engine= NULL;
int res= 0;
@@ -5825,8 +5866,9 @@ 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 | MY_THREAD_SPECIFIC))))
+ if (!(key_buff= (rownum_t*) my_malloc(PSI_INSTRUMENT_ME,
+ static_cast<size_t>(key_buff_elements * sizeof(rownum_t)),
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
return TRUE;
/*
@@ -6108,7 +6150,7 @@ subselect_partial_match_engine::subselect_partial_match_engine(
int subselect_partial_match_engine::exec()
{
- Item_in_subselect *item_in= (Item_in_subselect *) item;
+ Item_in_subselect *item_in= item->get_IN_subquery();
int lookup_res;
DBUG_ASSERT(!(item_in->left_expr_has_null() &&
@@ -6230,7 +6272,7 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
select_materialize_with_stats *result_sink=
(select_materialize_with_stats *) result;
uint cur_keyid= 0;
- Item_in_subselect *item_in= (Item_in_subselect*) item;
+ Item *left= item->get_IN_subquery()->left_exp();
int error;
if (merge_keys_count == 0)
@@ -6257,14 +6299,15 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
sizeof(Ordered_key*))) ||
!(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 | MY_THREAD_SPECIFIC))))
+ !(row_num_to_rowid= (uchar*) my_malloc(PSI_INSTRUMENT_ME,
+ static_cast<size_t>(row_count * rowid_length),
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
return TRUE;
/* Create the only non-NULL key if there is any. */
if (non_null_key_parts)
{
- non_null_key= new Ordered_key(cur_keyid, tmp_table, item_in->left_expr,
+ non_null_key= new Ordered_key(cur_keyid, tmp_table, left,
0, 0, 0, row_num_to_rowid);
if (non_null_key->init(non_null_key_parts))
return TRUE;
@@ -6296,7 +6339,7 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
merge_keys[cur_keyid]= new Ordered_key(
cur_keyid, tmp_table,
- item_in->left_expr->element_index(i),
+ left->element_index(i),
result_sink->get_null_count_of_col(i),
result_sink->get_min_null_of_col(i),
result_sink->get_max_null_of_col(i),
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 99bb5b190c2..ec4398b9a76 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -155,9 +155,7 @@ public:
}
bool is_in_predicate()
{
- return (substype() == Item_subselect::IN_SUBS ||
- substype() == Item_subselect::ALL_SUBS ||
- substype() == Item_subselect::ANY_SUBS);
+ return get_IN_subquery() != NULL;
}
/*
@@ -169,7 +167,7 @@ public:
select_result_interceptor *result);
~Item_subselect();
- void cleanup();
+ void cleanup() override;
virtual void reset()
{
eliminated= FALSE;
@@ -179,22 +177,23 @@ public:
Set the subquery result to a default value consistent with the semantics of
the result row produced for queries with implicit grouping.
*/
- void no_rows_in_result()= 0;
+ void no_rows_in_result() override= 0;
virtual bool select_transformer(JOIN *join);
bool assigned() { return value_assigned; }
void assigned(bool a) { value_assigned= a; }
- enum Type type() const;
- bool is_null()
+ enum Type type() const override;
+ bool is_null() override
{
update_null_value();
return null_value;
}
- bool fix_fields(THD *thd, Item **ref);
- bool with_subquery() const { DBUG_ASSERT(fixed); return true; }
- bool with_sum_func() const { return m_with_sum_func; }
- With_sum_func_cache* get_with_sum_func_cache() { return this; }
+ bool fix_fields(THD *thd, Item **ref) override;
+ bool with_subquery() const override { DBUG_ASSERT(fixed); return true; }
+ bool with_sum_func() const override { return m_with_sum_func; }
+ With_sum_func_cache* get_with_sum_func_cache() override { return this; }
bool mark_as_dependent(THD *thd, st_select_lex *select, Item *item);
- void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
+ void fix_after_pullout(st_select_lex *new_parent, Item **ref,
+ bool merge) override;
void recalc_used_tables(st_select_lex *new_parent, bool after_pullout);
virtual bool exec();
/*
@@ -208,13 +207,13 @@ public:
forced_const= TRUE;
}
virtual bool fix_length_and_dec();
- table_map used_tables() const;
- table_map not_null_tables() const { return 0; }
- bool const_item() const;
+ table_map used_tables() const override;
+ table_map not_null_tables() const override { return 0; }
+ bool const_item() const override;
inline table_map get_used_tables_cache() { return used_tables_cache; }
- Item *get_tmp_table_item(THD *thd);
- void update_used_tables();
- virtual void print(String *str, enum_query_type query_type);
+ Item *get_tmp_table_item(THD *thd) override;
+ void update_used_tables() override;
+ void print(String *str, enum_query_type query_type) override;
virtual bool have_guarded_conds() { return FALSE; }
bool change_engine(subselect_engine *eng)
{
@@ -229,7 +228,7 @@ public:
*/
bool is_evaluated() const;
bool is_uncacheable() const;
- bool is_expensive();
+ bool is_expensive() override;
/*
Used by max/min subquery to initialize value presence registration
@@ -237,12 +236,13 @@ public:
*/
virtual void reset_value_registration() {}
enum_parsing_place place() { return parsing_place; }
- bool walk(Item_processor processor, bool walk_subquery, void *arg);
- bool mark_as_eliminated_processor(void *arg);
- bool eliminate_subselect_processor(void *arg);
- bool set_fake_select_as_master_processor(void *arg);
- bool enumerate_field_refs_processor(void *arg);
- bool check_vcol_func_processor(void *arg)
+ bool walk(Item_processor processor, bool walk_subquery, void *arg) override;
+ bool unknown_splocal_processor(void *arg) override;
+ bool mark_as_eliminated_processor(void *arg) override;
+ bool eliminate_subselect_processor(void *arg) override;
+ bool set_fake_select_as_master_processor(void *arg) override;
+ bool enumerate_field_refs_processor(void *arg) override;
+ bool check_vcol_func_processor(void *arg) override
{
return mark_unsupported_function("select ...", arg, VCOL_IMPOSSIBLE);
}
@@ -255,27 +255,27 @@ public:
@retval TRUE if the predicate is expensive
@retval FALSE otherwise
*/
- bool is_expensive_processor(void *arg) { return is_expensive(); }
+ bool is_expensive_processor(void *arg) override { return is_expensive(); }
/**
Get the SELECT_LEX structure associated with this Item.
@return the SELECT_LEX structure associated with this Item
*/
st_select_lex* get_select_lex();
- virtual bool expr_cache_is_needed(THD *);
- virtual void get_cache_parameters(List<Item> &parameters);
- virtual bool is_subquery_processor (void *opt_arg) { return 1; }
- bool exists2in_processor(void *opt_arg) { return 0; }
- bool limit_index_condition_pushdown_processor(void *opt_arg)
+ bool expr_cache_is_needed(THD *) override;
+ void get_cache_parameters(List<Item> &parameters) override;
+ bool is_subquery_processor (void *opt_arg) override { return 1; }
+ bool exists2in_processor(void *opt_arg) override { return 0; }
+ bool limit_index_condition_pushdown_processor(void *opt_arg) override
{
return TRUE;
}
void register_as_with_rec_ref(With_element *with_elem);
void init_expr_cache_tracker(THD *thd);
-
- Item* build_clone(THD *thd) { return 0; }
- Item* get_copy(THD *thd) { return 0; }
+
+ Item* build_clone(THD *thd) override { return 0; }
+ Item* get_copy(THD *thd) override { return 0; }
bool wrap_tvc_into_select(THD *thd, st_select_lex *tvc_sl);
@@ -398,37 +398,40 @@ public:
emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0)
{}
- subs_type substype() { return EXISTS_SUBS; }
- void reset()
+ subs_type substype() override { return EXISTS_SUBS; }
+ void reset() override
{
eliminated= FALSE;
value= 0;
}
- void no_rows_in_result();
+ void no_rows_in_result() override;
- const Type_handler *type_handler() const { return &type_handler_bool; }
- longlong val_int();
- double val_real();
- String *val_str(String*);
- my_decimal *val_decimal(my_decimal *);
- bool val_bool();
- bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
+ const Type_handler *type_handler() const override
+ {
+ return &type_handler_bool;
+ }
+ longlong val_int() override;
+ double val_real() override;
+ String *val_str(String*) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ bool val_bool() override;
+ bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override
{ return get_date_from_int(thd, ltime, fuzzydate); }
- bool fix_fields(THD *thd, Item **ref);
- bool fix_length_and_dec();
- 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(void *opt_arg);
+ bool fix_fields(THD *thd, Item **ref) override;
+ bool fix_length_and_dec() override;
+ void print(String *str, enum_query_type query_type) override;
+ bool select_transformer(JOIN *join) override;
+ void top_level_item() override { abort_on_null=1; }
+ bool is_top_level_item() const override { return abort_on_null; }
+ bool exists2in_processor(void *opt_arg) override;
- Item* expr_cache_insert_transformer(THD *thd, uchar *unused);
+ Item* expr_cache_insert_transformer(THD *thd, uchar *unused) override;
- void mark_as_condition_AND_part(TABLE_LIST *embedding)
+ void mark_as_condition_AND_part(TABLE_LIST *embedding) override
{
emb_on_expr_nest= embedding;
}
- virtual void under_not(Item_func_not *upper) { upper_not= upper; };
+ void under_not(Item_func_not *upper) override { upper_not= upper; };
void set_exists_transformed() { exists_transformed= TRUE; }
@@ -510,7 +513,6 @@ protected:
bool create_row_in_to_exists_cond(JOIN * join,
Item **where_item,
Item **having_item);
-public:
Item *left_expr;
/*
Important for PS/SP: left_expr_orig is the item that left_expr originally
@@ -518,6 +520,7 @@ public:
left_expr could later be changed to something on the execution arena.
*/
Item *left_expr_orig;
+public:
/* Priority of this predicate in the convert-to-semi-join-nest process. */
int sj_convert_priority;
/* May be TRUE only for the candidates to semi-join conversion */
@@ -606,7 +609,7 @@ public:
if ( pushed_cond_guards)
pushed_cond_guards[i]= v;
}
- bool have_guarded_conds() { return MY_TEST(pushed_cond_guards); }
+ bool have_guarded_conds() override { return MY_TEST(pushed_cond_guards); }
Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
@@ -616,41 +619,42 @@ public:
in_strategy(SUBS_NOT_TRANSFORMED),
pushed_cond_guards(NULL), func(NULL), do_not_convert_to_sj(FALSE),
is_jtbm_merged(FALSE), is_jtbm_const_tab(FALSE), upper_item(0) {}
- void cleanup();
- subs_type substype() { return IN_SUBS; }
- void reset()
+ void cleanup() override;
+ subs_type substype() override { return IN_SUBS; }
+ void reset() override
{
eliminated= FALSE;
value= 0;
null_value= 0;
was_null= 0;
}
- bool select_transformer(JOIN *join);
+ bool select_transformer(JOIN *join) override;
bool create_in_to_exists_cond(JOIN *join_arg);
bool inject_in_to_exists_cond(JOIN *join_arg);
- virtual bool exec();
- longlong val_int();
- double val_real();
- String *val_str(String*);
- my_decimal *val_decimal(my_decimal *);
- bool val_bool();
+ bool exec() override;
+ longlong val_int() override;
+ double val_real() override;
+ String *val_str(String*) override;
+ my_decimal *val_decimal(my_decimal *) override;
+ bool val_bool() override;
bool test_limit(st_select_lex_unit *unit);
- void print(String *str, enum_query_type query_type);
- enum precedence precedence() const { return IN_PRECEDENCE; }
- bool fix_fields(THD *thd, Item **ref);
- bool fix_length_and_dec();
- void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
- bool const_item() const
+ void print(String *str, enum_query_type query_type) override;
+ enum precedence precedence() const override { return IN_PRECEDENCE; }
+ bool fix_fields(THD *thd, Item **ref) override;
+ bool fix_length_and_dec() override;
+ void fix_after_pullout(st_select_lex *new_parent, Item **ref,
+ bool merge) override;
+ bool const_item() const override
{
return Item_subselect::const_item() && left_expr->const_item();
}
- void update_used_tables();
+ void update_used_tables() override;
bool setup_mat_engine();
bool init_left_expr_cache();
/* Inform 'this' that it was computed, and contains a valid result. */
void set_first_execution() { if (first_execution) first_execution= FALSE; }
- bool expr_cache_is_needed(THD *thd);
+ bool expr_cache_is_needed(THD *thd) override;
inline bool left_expr_has_null();
void disable_cond_guard_for_const_null_left_expr(int i)
@@ -742,19 +746,28 @@ public:
DBUG_VOID_RETURN;
}
- bool walk(Item_processor processor, bool walk_subquery, void *arg)
+ bool walk(Item_processor processor, bool walk_subquery, void *arg) override
{
return left_expr->walk(processor, walk_subquery, arg) ||
Item_subselect::walk(processor, walk_subquery, arg);
}
- bool exists2in_processor(void *opt_arg __attribute__((unused)))
+ bool exists2in_processor(void *opt_arg __attribute__((unused))) override
{
return 0;
};
bool pushdown_cond_for_in_subquery(THD *thd, Item *cond);
+ Item_in_subselect *get_IN_subquery() override
+ { return this; }
+ inline Item** left_exp_ptr()
+ { return &left_expr; }
+ inline Item* left_exp() const
+ { return left_expr; }
+ inline Item* left_exp_orig() const
+ { return left_expr_orig; }
+
friend class Item_ref_null_helper;
friend class Item_is_not_null_test;
friend class Item_in_optimizer;
@@ -967,9 +980,9 @@ public:
// constructor can assign THD because it will be called after JOIN::prepare
subselect_uniquesubquery_engine(THD *thd_arg, st_join_table *tab_arg,
- Item_subselect *subs, Item *where)
+ Item_in_subselect *subs, Item *where)
:subselect_engine(subs, 0), tab(tab_arg), cond(where)
- {}
+ { DBUG_ASSERT(subs); }
~subselect_uniquesubquery_engine();
void cleanup();
int prepare(THD *);
@@ -1030,12 +1043,12 @@ public:
// constructor can assign THD because it will be called after JOIN::prepare
subselect_indexsubquery_engine(THD *thd_arg, st_join_table *tab_arg,
- Item_subselect *subs, Item *where,
+ Item_in_subselect *subs, Item *where,
Item *having_arg, bool chk_null)
:subselect_uniquesubquery_engine(thd_arg, tab_arg, subs, where),
check_null(chk_null),
having(having_arg)
- {}
+ { DBUG_ASSERT(subs); }
int exec();
void print (String *str, enum_query_type query_type);
virtual enum_engine_type engine_type() { return INDEXSUBQUERY_ENGINE; }
@@ -1098,14 +1111,14 @@ public:
Name_resolution_context *semi_join_conds_context;
- subselect_hash_sj_engine(THD *thd_arg, Item_subselect *in_predicate,
+ subselect_hash_sj_engine(THD *thd_arg, Item_in_subselect *in_predicate,
subselect_single_select_engine *old_engine)
: subselect_engine(in_predicate, NULL),
tmp_table(NULL), is_materialized(FALSE), materialize_engine(old_engine),
materialize_join(NULL), semi_join_conds(NULL), lookup_engine(NULL),
count_partial_match_columns(0), count_null_only_columns(0),
count_columns_with_nulls(0), strategy(UNDEFINED)
- {}
+ { DBUG_ASSERT(in_predicate); }
~subselect_hash_sj_engine();
bool init(List<Item> *tmp_columns, uint subquery_id);
@@ -1413,7 +1426,7 @@ public:
from Item_in_optimizer::val_int() sets Item_in_optimizer::null_value
correctly.
*/
- return !(((Item_in_subselect *) item)->null_value);
+ return !(item->get_IN_subquery()->null_value);
}
void print(String*, enum_query_type);
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index b980923d03d..4a94169c6f8 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -42,8 +42,9 @@
size_t Item_sum::ram_limitation(THD *thd)
{
- return (size_t)MY_MIN(thd->variables.tmp_memory_table_size,
- thd->variables.max_heap_table_size);
+ return MY_MAX(1024,
+ (size_t)MY_MIN(thd->variables.tmp_memory_table_size,
+ thd->variables.max_heap_table_size));
}
@@ -1280,21 +1281,22 @@ void Item_sum_min_max::setup_hybrid(THD *thd, Item *item, Item *value_arg)
}
-Field *Item_sum_min_max::create_tmp_field(bool group, TABLE *table)
+Field *Item_sum_min_max::create_tmp_field(MEM_ROOT *root,
+ bool group, TABLE *table)
{
DBUG_ENTER("Item_sum_min_max::create_tmp_field");
if (args[0]->type() == Item::FIELD_ITEM)
{
Field *field= ((Item_field*) args[0])->field;
- if ((field= field->create_tmp_field(table->in_use->mem_root, table, true)))
+ if ((field= field->create_tmp_field(root, table, true)))
{
DBUG_ASSERT((field->flags & NOT_NULL_FLAG) == 0);
field->field_name= name;
}
DBUG_RETURN(field);
}
- DBUG_RETURN(tmp_table_field_from_field_type(table));
+ DBUG_RETURN(tmp_table_field_from_field_type(root, table));
}
/***********************************************************************
@@ -1986,7 +1988,7 @@ Item *Item_sum_avg::copy_or_same(THD* thd)
}
-Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table)
+Field *Item_sum_avg::create_tmp_field(MEM_ROOT *root, bool group, TABLE *table)
{
if (group)
@@ -1996,7 +1998,7 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table)
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
- Field *field= new (table->in_use->mem_root)
+ Field *field= new (root)
Field_string(((result_type() == DECIMAL_RESULT) ?
dec_bin_size : sizeof(double)) + sizeof(longlong),
0, &name, &my_charset_bin);
@@ -2004,7 +2006,7 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table)
field->init(table);
return field;
}
- return tmp_table_field_from_field_type(table);
+ return tmp_table_field_from_field_type(root, table);
}
@@ -2228,7 +2230,8 @@ Item *Item_sum_variance::copy_or_same(THD* thd)
If we're grouping, then we need some space to serialize variables into, to
pass around.
*/
-Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table)
+Field *Item_sum_variance::create_tmp_field(MEM_ROOT *root,
+ bool group, TABLE *table)
{
Field *field;
if (group)
@@ -2238,11 +2241,12 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table)
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
- field= new Field_string(Stddev::binary_size(), 0, &name, &my_charset_bin);
+ field= new (root) Field_string(Stddev::binary_size(), 0,
+ &name, &my_charset_bin);
}
else
- field= new Field_double(max_length, maybe_null, &name, decimals,
- TRUE);
+ field= new (root) Field_double(max_length, maybe_null, &name, decimals,
+ TRUE);
if (field != NULL)
field->init(table);
@@ -3516,7 +3520,7 @@ String *Item_sum_udf_str::val_str(String *str)
*/
extern "C"
-int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
+int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
const void* key2)
{
Item_func_group_concat *item_func= (Item_func_group_concat*)arg;
@@ -3550,6 +3554,63 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
}
+/*
+ @brief
+ Comparator function for DISTINCT clause taking into account NULL values.
+
+ @note
+ Used for JSON_ARRAYAGG function
+*/
+
+int group_concat_key_cmp_with_distinct_with_nulls(void* arg,
+ const void* key1_arg,
+ const void* key2_arg)
+{
+ Item_func_group_concat *item_func= (Item_func_group_concat*)arg;
+
+ uchar *key1= (uchar*)key1_arg + item_func->table->s->null_bytes;
+ uchar *key2= (uchar*)key2_arg + item_func->table->s->null_bytes;
+
+ /*
+ JSON_ARRAYAGG function only accepts one argument.
+ */
+
+ Item *item= item_func->args[0];
+ /*
+ If item is a const item then either get_tmp_table_field returns 0
+ or it is an item over a const table.
+ */
+ if (item->const_item())
+ return 0;
+ /*
+ We have to use get_tmp_table_field() instead of
+ real_item()->get_tmp_table_field() because we want the field in
+ the temporary table, not the original field
+ */
+ Field *field= item->get_tmp_table_field();
+
+ if (!field)
+ return 0;
+
+ if (field->is_null_in_record((uchar*)key1_arg) &&
+ field->is_null_in_record((uchar*)key2_arg))
+ return 0;
+
+ if (field->is_null_in_record((uchar*)key1_arg))
+ return -1;
+
+ if (field->is_null_in_record((uchar*)key2_arg))
+ return 1;
+
+ uint offset= (field->offset(field->table->record[0]) -
+ field->table->s->null_bytes);
+ int res= field->cmp(key1 + offset, key2 + offset);
+ if (res)
+ return res;
+ return 0;
+}
+
+
/**
function of sort for syntax: GROUP_CONCAT(expr,... ORDER BY col,... )
*/
@@ -3606,6 +3667,106 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1,
}
+/*
+ @brief
+ Comparator function for ORDER BY clause taking into account NULL values.
+
+ @note
+ Used for JSON_ARRAYAGG function
+*/
+
+int group_concat_key_cmp_with_order_with_nulls(void *arg, const void *key1_arg,
+ const void *key2_arg)
+{
+ Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
+ ORDER **order_item, **end;
+
+ uchar *key1= (uchar*)key1_arg + grp_item->table->s->null_bytes;
+ uchar *key2= (uchar*)key2_arg + grp_item->table->s->null_bytes;
+
+ for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
+ order_item < end;
+ order_item++)
+ {
+ Item *item= *(*order_item)->item;
+ /*
+ If field_item is a const item then either get_tmp_table_field returns 0
+ or it is an item over a const table.
+ */
+ if (item->const_item())
+ continue;
+ /*
+ We have to use get_tmp_table_field() instead of
+ real_item()->get_tmp_table_field() because we want the field in
+ the temporary table, not the original field
+
+ Note that for the case of ROLLUP, field may point to another table
+ tham grp_item->table. This is however ok as the table definitions are
+ the same.
+ */
+ Field *field= item->get_tmp_table_field();
+ if (!field)
+ continue;
+
+ if (field->is_null_in_record((uchar*)key1_arg) &&
+ field->is_null_in_record((uchar*)key2_arg))
+ continue;
+
+ if (field->is_null_in_record((uchar*)key1_arg))
+ return ((*order_item)->direction == ORDER::ORDER_ASC) ? -1 : 1;
+
+ if (field->is_null_in_record((uchar*)key2_arg))
+ return ((*order_item)->direction == ORDER::ORDER_ASC) ? 1 : -1;
+
+ uint offset= (field->offset(field->table->record[0]) -
+ field->table->s->null_bytes);
+ int res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset);
+ if (res)
+ return ((*order_item)->direction == ORDER::ORDER_ASC) ? res : -res;
+ }
+ /*
+ We can't return 0 because in that case the tree class would remove this
+ item as double value. This would cause problems for case-changes and
+ if the returned values are not the same we do the sort on.
+ */
+ return 1;
+}
+
+
+static void report_cut_value_error(THD *thd, uint row_count, const char *fname)
+{
+ size_t fn_len= strlen(fname);
+ char *fname_upper= (char *) my_alloca(fn_len + 1);
+ if (!fname_upper)
+ fname_upper= (char*) fname; // Out of memory
+ else
+ memcpy(fname_upper, fname, fn_len+1);
+ my_caseup_str(&my_charset_latin1, fname_upper);
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_CUT_VALUE_GROUP_CONCAT,
+ ER_THD(thd, ER_CUT_VALUE_GROUP_CONCAT),
+ row_count, fname_upper);
+ my_afree(fname_upper);
+}
+
+
+void Item_func_group_concat::cut_max_length(String *result,
+ uint old_length, uint max_length) const
+{
+ const char *ptr= result->ptr();
+ /*
+ It's ok to use item->result.length() as the fourth argument
+ as this is never used to limit the length of the data.
+ Cut is done with the third argument.
+ */
+ size_t add_length= Well_formed_prefix(collation.collation,
+ ptr + old_length,
+ ptr + max_length,
+ result->length()).length();
+ result->length(old_length + add_length);
+}
+
+
/**
Append data from current leaf to item->result.
*/
@@ -3658,7 +3819,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
because it contains both order and arg list fields.
*/
if ((*arg)->const_item())
- res= (*arg)->val_str(&tmp);
+ res= item->get_str_from_item(*arg, &tmp);
else
{
Field *field= (*arg)->get_tmp_table_field();
@@ -3667,11 +3828,13 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
uint offset= (field->offset(field->table->record[0]) -
table->s->null_bytes);
DBUG_ASSERT(offset < table->s->reclength);
- res= field->val_str(&tmp, key + offset);
+ res= item->get_str_from_field(*arg, field, &tmp, key,
+ offset + item->get_null_bytes());
}
else
- res= (*arg)->val_str(&tmp);
+ res= item->get_str_from_item(*arg, &tmp);
}
+
if (res)
result->append(*res);
}
@@ -3683,24 +3846,10 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
/* stop if length of result more than max_length */
if (result->length() > max_length)
{
- CHARSET_INFO *cs= item->collation.collation;
- const char *ptr= result->ptr();
THD *thd= current_thd;
- /*
- It's ok to use item->result.length() as the fourth argument
- as this is never used to limit the length of the data.
- Cut is done with the third argument.
- */
- size_t add_length= Well_formed_prefix(cs,
- ptr + old_length,
- ptr + max_length,
- result->length()).length();
- result->length(old_length + add_length);
+ item->cut_max_length(result, old_length, max_length);
item->warning_for_row= TRUE;
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_CUT_VALUE_GROUP_CONCAT,
- ER_THD(thd, ER_CUT_VALUE_GROUP_CONCAT),
- item->row_count);
+ report_cut_value_error(thd, item->row_count, item->func_name());
/**
To avoid duplicated warnings in Item_func_group_concat::val_str()
@@ -3901,7 +4050,7 @@ void Item_func_group_concat::clear()
result.copy();
null_value= TRUE;
warning_for_row= FALSE;
- result_finalized= FALSE;
+ result_finalized= false;
if (offset_limit)
copy_offset_limit= offset_limit->val_int();
if (row_limit)
@@ -3951,7 +4100,7 @@ bool Item_func_group_concat::repack_tree(THD *thd)
init_tree(&st.tree, (size_t) MY_MIN(thd->variables.max_heap_table_size,
thd->variables.sortbuff_size/16), 0,
- size, group_concat_key_cmp_with_order, NULL,
+ size, get_comparator_function_for_order_by(), NULL,
(void*) this, MYF(MY_THREAD_SPECIFIC));
DBUG_ASSERT(tree->size_of_element == st.tree.size_of_element);
st.table= table;
@@ -3969,6 +4118,7 @@ bool Item_func_group_concat::repack_tree(THD *thd)
return 0;
}
+
/*
Repacking the tree is expensive. But it keeps the tree small, and
inserting into an unnecessary large tree is also waste of time.
@@ -3979,9 +4129,9 @@ bool Item_func_group_concat::repack_tree(THD *thd)
*/
#define GCONCAT_REPACK_FACTOR (1 << 10)
-bool Item_func_group_concat::add()
+bool Item_func_group_concat::add(bool exclude_nulls)
{
- if (always_null)
+ if (always_null && exclude_nulls)
return 0;
copy_fields(tmp_table_param);
if (copy_funcs(tmp_table_param->items_to_copy, table->in_use))
@@ -3999,11 +4149,20 @@ bool Item_func_group_concat::add()
Field *field= show_item->get_tmp_table_field();
if (field)
{
- if (field->is_null_in_record((const uchar*) table->record[0]))
+ if (field->is_null_in_record((const uchar*) table->record[0]) &&
+ exclude_nulls)
return 0; // Skip row if it contains null
if (tree && (res= field->val_str(&buf)))
row_str_len+= res->length();
}
+ else
+ {
+ /*
+ should not reach here, we create temp table for all the arguments of
+ the group_concat function
+ */
+ DBUG_ASSERT(0);
+ }
}
null_value= FALSE;
@@ -4013,7 +4172,7 @@ bool Item_func_group_concat::add()
{
/* Filter out duplicate rows. */
uint count= unique_filter->elements_in_tree();
- unique_filter->unique_add(table->record[0] + table->s->null_bytes);
+ unique_filter->unique_add(get_record_pointer());
if (count == unique_filter->elements_in_tree())
row_eligible= FALSE;
}
@@ -4027,8 +4186,7 @@ bool Item_func_group_concat::add()
&& tree->elements_in_tree > 1)
if (repack_tree(thd))
return 1;
- el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0,
- tree->custom_arg);
+ el= tree_insert(tree, get_record_pointer(), 0, tree->custom_arg);
/* check if there was enough memory to insert the row */
if (!el)
return 1;
@@ -4040,7 +4198,7 @@ bool Item_func_group_concat::add()
row to the output buffer here. That will be done in val_str.
*/
if (row_eligible && !warning_for_row && (!tree && !distinct))
- dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this);
+ dump_leaf_key(get_record_pointer(), 1, this);
return 0;
}
@@ -4135,13 +4293,10 @@ bool Item_func_group_concat::setup(THD *thd)
Item *item= args[i];
if (list.push_back(item, thd->mem_root))
DBUG_RETURN(TRUE);
- if (item->const_item())
+ if (item->const_item() && item->is_null() && skip_nulls())
{
- if (item->is_null())
- {
- always_null= 1;
- DBUG_RETURN(FALSE);
- }
+ always_null= 1;
+ DBUG_RETURN(FALSE);
}
}
@@ -4236,16 +4391,16 @@ bool Item_func_group_concat::setup(THD *thd)
*/
init_tree(tree, (size_t)MY_MIN(thd->variables.max_heap_table_size,
thd->variables.sortbuff_size/16), 0,
- tree_key_length,
- group_concat_key_cmp_with_order, NULL, (void*) this,
+ tree_key_length + get_null_bytes(),
+ get_comparator_function_for_order_by(), NULL, (void*) this,
MYF(MY_THREAD_SPECIFIC));
tree_len= 0;
}
if (distinct)
- unique_filter= new Unique(group_concat_key_cmp_with_distinct,
+ unique_filter= new Unique(get_comparator_function_for_distinct(),
(void*)this,
- tree_key_length,
+ tree_key_length + get_null_bytes(),
ram_limitation(thd));
if ((row_limit && row_limit->cmp_type() != INT_RESULT) ||
(offset_limit && offset_limit->cmp_type() != INT_RESULT))
@@ -4292,18 +4447,76 @@ String* Item_func_group_concat::val_str(String* str)
table->blob_storage->is_truncated_value())
{
warning_for_row= true;
- push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
- ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT),
- row_count);
+ report_cut_value_error(current_thd, row_count, func_name());
}
return &result;
}
+/*
+ @brief
+ Get the comparator function for DISTINT clause
+*/
+
+qsort_cmp2 Item_func_group_concat::get_comparator_function_for_distinct()
+{
+ return skip_nulls() ?
+ group_concat_key_cmp_with_distinct :
+ group_concat_key_cmp_with_distinct_with_nulls;
+}
+
+
+/*
+ @brief
+ Get the comparator function for ORDER BY clause
+*/
+
+qsort_cmp2 Item_func_group_concat::get_comparator_function_for_order_by()
+{
+ return skip_nulls() ?
+ group_concat_key_cmp_with_order :
+ group_concat_key_cmp_with_order_with_nulls;
+}
+
+
+/*
+
+ @brief
+ Get the record pointer of the current row of the table
+
+ @details
+ look at the comments for Item_func_group_concat::get_null_bytes
+*/
+
+uchar* Item_func_group_concat::get_record_pointer()
+{
+ return skip_nulls() ?
+ table->record[0] + table->s->null_bytes :
+ table->record[0];
+}
+
+
+/*
+ @brief
+ Get the null bytes for the table if required.
+
+ @details
+ This function is used for GROUP_CONCAT (or JSON_ARRAYAGG) implementation
+ where the Unique tree or the ORDER BY tree may store the null values,
+ in such case we also store the null bytes inside each node of the tree.
+
+*/
+
+uint Item_func_group_concat::get_null_bytes()
+{
+ return skip_nulls() ? 0 : table->s->null_bytes;
+}
+
+
void Item_func_group_concat::print(String *str, enum_query_type query_type)
{
- str->append(STRING_WITH_LEN("group_concat("));
+ str->append(func_name());
if (distinct)
str->append(STRING_WITH_LEN("distinct "));
for (uint i= 0; i < arg_count_field; i++)
@@ -4326,9 +4539,13 @@ void Item_func_group_concat::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN(" DESC"));
}
}
- str->append(STRING_WITH_LEN(" separator \'"));
- str->append_for_single_quote(separator->ptr(), separator->length());
- str->append(STRING_WITH_LEN("\'"));
+
+ if (sum_func() == GROUP_CONCAT_FUNC)
+ {
+ str->append(STRING_WITH_LEN(" separator \'"));
+ str->append_for_single_quote(separator->ptr(), separator->length());
+ str->append(STRING_WITH_LEN("\'"));
+ }
if (limit_clause)
{
diff --git a/sql/item_sum.h b/sql/item_sum.h
index f715c80ffaf..118f78ec5c1 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1,7 +1,7 @@
#ifndef ITEM_SUM_INCLUDED
#define ITEM_SUM_INCLUDED
/* Copyright (c) 2000, 2013 Oracle and/or its affiliates.
- Copyright (c) 2008, 2013 Monty Program Ab.
+ Copyright (c) 2008, 2020, MariaDB Corporation.
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
@@ -355,7 +355,8 @@ public:
ROW_NUMBER_FUNC, RANK_FUNC, DENSE_RANK_FUNC, PERCENT_RANK_FUNC,
CUME_DIST_FUNC, NTILE_FUNC, FIRST_VALUE_FUNC, LAST_VALUE_FUNC,
NTH_VALUE_FUNC, LEAD_FUNC, LAG_FUNC, PERCENTILE_CONT_FUNC,
- PERCENTILE_DISC_FUNC, SP_AGGREGATE_FUNC
+ PERCENTILE_DISC_FUNC, SP_AGGREGATE_FUNC, JSON_ARRAYAGG_FUNC,
+ JSON_OBJECTAGG_FUNC
};
Item **ref_by; /* pointer to a ref to the object used to register it */
@@ -428,6 +429,7 @@ public:
case SUM_BIT_FUNC:
case UDF_SUM_FUNC:
case GROUP_CONCAT_FUNC:
+ case JSON_ARRAYAGG_FUNC:
return true;
default:
return false;
@@ -513,11 +515,11 @@ public:
}
virtual void make_unique() { force_copy_fields= TRUE; }
Item *get_tmp_table_item(THD *thd);
- virtual Field *create_tmp_field(bool group, TABLE *table);
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ virtual Field *create_tmp_field(MEM_ROOT *root, bool group, TABLE *table);
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
- return create_tmp_field(param->group(), table);
+ return create_tmp_field(root, param->group(), table);
}
virtual bool collect_outer_ref_processor(void *param);
bool init_sum_func_check(THD *thd);
@@ -778,7 +780,6 @@ public:
{
return get_date_from_int(thd, ltime, fuzzydate);
}
- const Type_handler *type_handler() const { return &type_handler_longlong; }
bool fix_length_and_dec()
{ decimals=0; max_length=21; maybe_null=null_value=0; return FALSE; }
};
@@ -898,6 +899,7 @@ public:
count=count_arg;
Item_sum::make_const();
}
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
longlong val_int();
void reset_field();
void update_field();
@@ -957,7 +959,7 @@ public:
return has_with_distinct() ? "avg(distinct " : "avg(";
}
Item *copy_or_same(THD* thd);
- Field *create_tmp_field(bool group, TABLE *table);
+ Field *create_tmp_field(MEM_ROOT *root, bool group, TABLE *table);
void cleanup()
{
count= 0;
@@ -1014,10 +1016,10 @@ public:
-class Item_sum_variance : public Item_sum_double
+class Item_sum_variance :public Item_sum_double
{
Stddev m_stddev;
- bool fix_length_and_dec();
+ bool fix_length_and_dec() override;
public:
uint sample;
@@ -1028,26 +1030,27 @@ public:
sample(sample_arg)
{}
Item_sum_variance(THD *thd, Item_sum_variance *item);
- enum Sumfunctype sum_func () const { return VARIANCE_FUNC; }
+ Sumfunctype sum_func () const override { return VARIANCE_FUNC; }
void fix_length_and_dec_double();
void fix_length_and_dec_decimal();
- void clear();
- bool add();
- double val_real();
- void reset_field();
- void update_field();
- Item *result_item(THD *thd, Field *field);
- void no_rows_in_result() {}
- const char *func_name() const
+ void clear() override final;
+ bool add() override final;
+ double val_real() override;
+ void reset_field() override final;
+ void update_field() override final;
+ Item *result_item(THD *thd, Field *field) override;
+ void no_rows_in_result() override final {}
+ const char *func_name() const override
{ return sample ? "var_samp(" : "variance("; }
- Item *copy_or_same(THD* thd);
- Field *create_tmp_field(bool group, TABLE *table);
- void cleanup()
+ Item *copy_or_same(THD* thd) override;
+ Field *create_tmp_field(MEM_ROOT *root, bool group, TABLE *table) override
+ final;
+ void cleanup() override final
{
m_stddev= Stddev();
Item_sum_double::cleanup();
}
- Item *get_copy(THD *thd)
+ Item *get_copy(THD *thd) override
{ return get_item_copy<Item_sum_variance>(thd, this); }
};
@@ -1055,7 +1058,7 @@ public:
standard_deviation(a) = sqrt(variance(a))
*/
-class Item_sum_std :public Item_sum_variance
+class Item_sum_std final :public Item_sum_variance
{
public:
Item_sum_std(THD *thd, Item *item_par, uint sample_arg):
@@ -1063,27 +1066,27 @@ class Item_sum_std :public Item_sum_variance
Item_sum_std(THD *thd, Item_sum_std *item)
:Item_sum_variance(thd, item)
{}
- enum Sumfunctype sum_func () const { return STD_FUNC; }
- double val_real();
- Item *result_item(THD *thd, Field *field);
- const char *func_name() const { return "std("; }
- Item *copy_or_same(THD* thd);
- Item *get_copy(THD *thd)
+ enum Sumfunctype sum_func () const override final { return STD_FUNC; }
+ double val_real() override final;
+ Item *result_item(THD *thd, Field *field) override final;
+ const char *func_name() const override final { return "std("; }
+ Item *copy_or_same(THD* thd) override final;
+ Item *get_copy(THD *thd) override final
{ return get_item_copy<Item_sum_std>(thd, this); }
};
-class Item_sum_hybrid: public Item_sum,
+class Item_sum_hybrid : public Item_sum,
public Type_handler_hybrid_field_type
{
public:
Item_sum_hybrid(THD *thd, Item *item_par):
Item_sum(thd, item_par),
- Type_handler_hybrid_field_type(&type_handler_longlong)
+ Type_handler_hybrid_field_type(&type_handler_slonglong)
{ collation.set(&my_charset_bin); }
Item_sum_hybrid(THD *thd, Item *a, Item *b):
Item_sum(thd, a, b),
- Type_handler_hybrid_field_type(&type_handler_longlong)
+ Type_handler_hybrid_field_type(&type_handler_slonglong)
{ collation.set(&my_charset_bin); }
Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
:Item_sum(thd, item),
@@ -1138,7 +1141,7 @@ public:
{
return get_arg(0)->real_type_handler();
}
- TYPELIB *get_typelib() const { return args[0]->get_typelib(); }
+ const TYPELIB *get_typelib() const { return args[0]->get_typelib(); }
void update_field();
void min_max_update_str_field();
void min_max_update_real_field();
@@ -1149,12 +1152,12 @@ public:
bool any_value() { return was_values; }
void no_rows_in_result();
void restore_to_before_no_rows_in_result();
- Field *create_tmp_field(bool group, TABLE *table);
+ Field *create_tmp_field(MEM_ROOT *root, bool group, TABLE *table);
void setup_caches(THD *thd) { setup_hybrid(thd, arguments()[0], NULL); }
};
-class Item_sum_min :public Item_sum_min_max
+class Item_sum_min final :public Item_sum_min_max
{
public:
Item_sum_min(THD *thd, Item *item_par): Item_sum_min_max(thd, item_par, 1) {}
@@ -1169,7 +1172,7 @@ public:
};
-class Item_sum_max :public Item_sum_min_max
+class Item_sum_max final :public Item_sum_min_max
{
public:
Item_sum_max(THD *thd, Item *item_par): Item_sum_min_max(thd, item_par, -1) {}
@@ -1203,8 +1206,11 @@ public:
longlong val_int();
void reset_field();
void update_field();
+ const Type_handler *type_handler() const { return &type_handler_ulonglong; }
bool fix_length_and_dec()
{
+ if (args[0]->check_type_can_return_int(func_name()))
+ return true;
decimals= 0; max_length=21; unsigned_flag= 1; maybe_null= null_value= 0;
return FALSE;
}
@@ -1255,7 +1261,7 @@ protected:
};
-class Item_sum_or :public Item_sum_bit
+class Item_sum_or final :public Item_sum_bit
{
public:
Item_sum_or(THD *thd, Item *item_par): Item_sum_bit(thd, item_par, 0) {}
@@ -1271,7 +1277,7 @@ private:
};
-class Item_sum_and :public Item_sum_bit
+class Item_sum_and final :public Item_sum_bit
{
public:
Item_sum_and(THD *thd, Item *item_par):
@@ -1287,7 +1293,7 @@ private:
void set_bits_from_counters();
};
-class Item_sum_xor :public Item_sum_bit
+class Item_sum_xor final :public Item_sum_bit
{
public:
Item_sum_xor(THD *thd, Item *item_par): Item_sum_bit(thd, item_par, 0) {}
@@ -1311,7 +1317,7 @@ struct st_sp_security_context;
Item_sum_sp handles STORED AGGREGATE FUNCTIONS
Each Item_sum_sp represents a custom aggregate function. Inside the
- function's body, we require at least one occurence of FETCH GROUP NEXT ROW
+ function's body, we require at least one occurrence of FETCH GROUP NEXT ROW
instruction. This cursor is what makes custom stored aggregates possible.
During computation the function's add method is called. This in turn performs
@@ -1339,7 +1345,7 @@ struct st_sp_security_context;
group is already set in the argument x. This behaviour is done so when
a user writes a function, he should "logically" include FETCH GROUP NEXT ROW
before any "add" instructions in the stored function. This means however that
- internally, the first occurence doesn't stop the function. See the
+ internally, the first occurrence doesn't stop the function. See the
implementation of FETCH GROUP NEXT ROW for details as to how it happens.
Either way, one should assume that after calling "Item_sum_sp::add()" that
@@ -1354,7 +1360,7 @@ struct st_sp_security_context;
Example:
DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN ret_val;
*/
-class Item_sum_sp :public Item_sum,
+class Item_sum_sp final :public Item_sum,
public Item_sp
{
private:
@@ -1372,9 +1378,9 @@ public:
{
return SP_AGGREGATE_FUNC;
}
- Field *create_field_for_create_select(TABLE *table)
+ Field *create_field_for_create_select(MEM_ROOT *root, TABLE *table)
{
- return create_table_field_from_handler(table);
+ return create_table_field_from_handler(root, table);
}
bool fix_length_and_dec();
bool fix_fields(THD *thd, Item **ref);
@@ -1404,6 +1410,11 @@ public:
return sp_result_field->val_decimal(dec_buf);
}
+ bool val_native(THD *thd, Native *to)
+ {
+ return null_value= execute() || sp_result_field->val_native(to);
+ }
+
String *val_str(String *str)
{
String buf;
@@ -1456,10 +1467,10 @@ public:
unsigned_flag= item->unsigned_flag;
}
table_map used_tables() const { return (table_map) 1L; }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
- return create_tmp_field_ex_simple(table, src, param);
+ return create_tmp_field_ex_simple(root, table, src, param);
}
void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); }
bool check_vcol_func_processor(void *arg)
@@ -1672,7 +1683,12 @@ public:
{ DBUG_ASSERT(fixed == 1); return (double) Item_sum_udf_int::val_int(); }
String *val_str(String*str);
my_decimal *val_decimal(my_decimal *);
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const
+ {
+ if (unsigned_flag)
+ return &type_handler_ulonglong;
+ return &type_handler_slonglong;
+ }
bool fix_length_and_dec() { decimals=0; max_length=21; return FALSE; }
Item *copy_or_same(THD* thd);
Item *get_copy(THD *thd)
@@ -1696,8 +1712,8 @@ public:
char *end_not_used;
String *res;
res=val_str(&str_value);
- return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),
- &end_not_used, &err_not_used) : 0.0;
+ return res ? res->charset()->strntod((char*) res->ptr(),res->length(),
+ &end_not_used, &err_not_used) : 0.0;
}
longlong val_int()
{
@@ -1710,7 +1726,7 @@ public:
return 0; /* Null value */
cs= res->charset();
end= (char*) res->ptr()+res->length();
- return cs->cset->strtoll10(cs, res->ptr(), &end, &err_not_used);
+ return cs->strtoll10(res->ptr(), &end, &err_not_used);
}
my_decimal *val_decimal(my_decimal *dec);
const Type_handler *type_handler() const { return string_type_handler(); }
@@ -1834,8 +1850,12 @@ public:
C_MODE_START
int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
const void* key2);
+int group_concat_key_cmp_with_distinct_with_nulls(void* arg, const void* key1,
+ const void* key2);
int group_concat_key_cmp_with_order(void* arg, const void* key1,
const void* key2);
+int group_concat_key_cmp_with_order_with_nulls(void *arg, const void *key1,
+ const void *key2);
int dump_leaf_key(void* key_arg,
element_count count __attribute__((unused)),
void* item_arg);
@@ -1843,6 +1863,7 @@ C_MODE_END
class Item_func_group_concat : public Item_sum
{
+protected:
TMP_TABLE_PARAM *tmp_table_param;
String result;
String *separator;
@@ -1889,16 +1910,40 @@ class Item_func_group_concat : public Item_sum
*/
Item_func_group_concat *original;
+ /*
+ Used by Item_func_group_concat and Item_func_json_arrayagg. The latter
+ needs null values but the former doesn't.
+ */
+ bool add(bool exclude_nulls);
+
friend int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
const void* key2);
+ friend int group_concat_key_cmp_with_distinct_with_nulls(void* arg,
+ const void* key1,
+ const void* key2);
friend int group_concat_key_cmp_with_order(void* arg, const void* key1,
const void* key2);
+ friend int group_concat_key_cmp_with_order_with_nulls(void *arg,
+ const void *key1, const void *key2);
friend int dump_leaf_key(void* key_arg,
element_count count __attribute__((unused)),
void* item_arg);
bool repack_tree(THD *thd);
+ /*
+ Says whether the function should skip NULL arguments
+ or add them to the result.
+ Redefined in JSON_ARRAYAGG.
+ */
+ virtual bool skip_nulls() const { return true; }
+ virtual String *get_str_from_item(Item *i, String *tmp)
+ { return i->val_str(tmp); }
+ virtual String *get_str_from_field(Item *i, Field *f, String *tmp,
+ const uchar *key, size_t offset)
+ { return f->val_str(tmp, key + offset); }
+ virtual void cut_max_length(String *result,
+ uint old_length, uint max_length) const;
public:
// Methods used by ColumnStore
bool get_distinct() const { return distinct; }
@@ -1926,7 +1971,10 @@ public:
return &type_handler_varchar;
}
void clear();
- bool add();
+ bool add()
+ {
+ return add(skip_nulls());
+ }
void reset_field() { DBUG_ASSERT(0); } // not used
void update_field() { DBUG_ASSERT(0); } // not used
bool fix_fields(THD *,Item **);
@@ -1968,6 +2016,11 @@ public:
{ context= (Name_resolution_context *)cntx; return FALSE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_group_concat>(thd, this); }
+ qsort_cmp2 get_comparator_function_for_distinct();
+ qsort_cmp2 get_comparator_function_for_order_by();
+ uchar* get_record_pointer();
+ uint get_null_bytes();
+
};
#endif /* ITEM_SUM_INCLUDED */
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index b476b5f1f27..b8650a5c7e9 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -157,7 +157,7 @@ static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format,
for (; ptr != end && val != val_end; ptr++)
{
/* Skip pre-space between each argument */
- if ((val+= cs->cset->scan(cs, val, val_end, MY_SEQ_SPACES)) >= val_end)
+ if ((val+= cs->scan(val, val_end, MY_SEQ_SPACES)) >= val_end)
break;
if (*ptr == '%' && ptr+1 != end)
@@ -259,13 +259,9 @@ static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format,
case 'p':
if (val_len < 2 || ! usa_time)
goto err;
- if (!my_strnncoll(&my_charset_latin1,
- (const uchar *) val, 2,
- (const uchar *) "PM", 2))
+ if (!my_charset_latin1.strnncoll(val, 2, "PM", 2))
daypart= 12;
- else if (my_strnncoll(&my_charset_latin1,
- (const uchar *) val, 2,
- (const uchar *) "AM", 2))
+ else if (my_charset_latin1.strnncoll(val, 2, "AM", 2))
goto err;
val+= 2;
break;
@@ -478,7 +474,7 @@ err:
Create a formatted date/time value in a string.
*/
-static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time,
+static bool make_date_time(const String *format, const MYSQL_TIME *l_time,
timestamp_type type, const MY_LOCALE *locale,
String *str)
{
@@ -493,7 +489,7 @@ static bool make_date_time(const LEX_CSTRING &format, MYSQL_TIME *l_time,
if (l_time->neg)
str->append('-');
- end= (ptr= format.str) + format.length;
+ end= (ptr= format->ptr()) + format->length();
for (; ptr != end ; ptr++)
{
if (*ptr != '%' || ptr+1 == end)
@@ -987,7 +983,7 @@ String* Item_func_monthname::val_str(String* str)
return (String *) 0;
month_name= locale->month_names->type_names[d.get_mysql_time()->month - 1];
- str->copy(month_name, (uint) strlen(month_name), &my_charset_utf8_bin,
+ str->copy(month_name, (uint) strlen(month_name), &my_charset_utf8mb3_bin,
collation.collation, &err);
return str;
}
@@ -1133,7 +1129,7 @@ String* Item_func_dayname::val_str(String* str)
return (String*) 0;
day_name= locale->day_names->type_names[dt.weekday(false)];
- str->copy(day_name, (uint) strlen(day_name), &my_charset_utf8_bin,
+ str->copy(day_name, (uint) strlen(day_name), &my_charset_utf8mb3_bin,
collation.collation, &err);
return str;
}
@@ -1215,7 +1211,7 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds,
{
if ((null_value= field->is_null()))
return 1;
- *seconds= ((Field_timestamp*)field)->get_timestamp(second_part);
+ *seconds= field->get_timestamp(second_part);
return 0;
}
}
@@ -1271,7 +1267,7 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e
DBUG_ASSERT(arg_count == 1 &&
args[0]->type() == Item::FIELD_ITEM &&
args[0]->field_type() == MYSQL_TYPE_TIMESTAMP);
- Field_timestamp *field=(Field_timestamp *)(((Item_field*)args[0])->field);
+ Field *field= ((Item_field*)args[0])->field;
/* Leave the incl_endp intact */
ulong unused;
my_time_t ts= field->get_timestamp(&unused);
@@ -1645,7 +1641,7 @@ int Item_func_now_local::save_in_field(Field *field, bool no_conversions)
ulong sec_part= decimals ? thd->query_start_sec_part() : 0;
sec_part-= my_time_fraction_remainder(sec_part, decimals);
field->set_notnull();
- ((Field_timestamp*)field)->store_TIME(ts, sec_part);
+ field->store_timestamp(ts, sec_part);
return 0;
}
else
@@ -1747,7 +1743,7 @@ bool Item_func_date_format::fix_length_and_dec()
decimals=0;
CHARSET_INFO *cs= thd->variables.collation_connection;
- uint32 repertoire= arg1->collation.repertoire;
+ my_repertoire_t repertoire= arg1->collation.repertoire;
if (!thd->variables.lc_time_names->is_ascii)
repertoire|= MY_REPERTOIRE_EXTENDED;
collation.set(cs, arg1->collation.derivation, repertoire);
@@ -1882,6 +1878,7 @@ String *Item_func_date_format::val_str(String *str)
DBUG_ASSERT(fixed == 1);
date_conv_mode_t mode= is_time_format ? TIME_TIME_ONLY : TIME_CONV_NONE;
THD *thd= current_thd;
+
if ((null_value= args[0]->get_date(thd, &l_time,
Temporal::Options(mode, thd))))
return 0;
@@ -1906,7 +1903,7 @@ String *Item_func_date_format::val_str(String *str)
/* Create the result string */
str->set_charset(collation.collation);
- if (!make_date_time(format->lex_cstring(), &l_time,
+ if (!make_date_time(format, &l_time,
is_time_format ? MYSQL_TIMESTAMP_TIME :
MYSQL_TIMESTAMP_DATE,
lc, str))
@@ -1923,7 +1920,10 @@ bool Item_func_from_unixtime::fix_length_and_dec()
THD *thd= current_thd;
thd->time_zone_used= 1;
tz= thd->variables.time_zone;
- fix_attributes_datetime_not_fixed_dec(args[0]->decimals);
+ Type_std_attributes::set(
+ Type_temporal_attributes_not_fixed_dec(MAX_DATETIME_WIDTH,
+ args[0]->decimals, false),
+ DTCollation_numeric());
maybe_null= true;
return FALSE;
}
@@ -2004,7 +2004,7 @@ bool Item_date_add_interval::fix_length_and_dec()
{
enum_field_types arg0_field_type;
- if (!args[0]->type_handler()->is_traditional_type())
+ if (!args[0]->type_handler()->is_traditional_scalar_type())
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
args[0]->type_handler()->name().ptr(),
@@ -2111,6 +2111,21 @@ void Item_extract::print(String *str, enum_query_type query_type)
str->append(')');
}
+
+bool Item_extract::check_arguments() const
+{
+ if (!args[0]->type_handler()->can_return_extract_source(int_type))
+ {
+ char tmp[64];
+ my_snprintf(tmp, sizeof(tmp), "extract(%s)", interval_names[int_type]);
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ args[0]->type_handler()->name().ptr(), tmp);
+ return true;
+ }
+ return false;
+}
+
+
bool Item_extract::fix_length_and_dec()
{
maybe_null=1; // If wrong date
@@ -2326,7 +2341,7 @@ uint Item_char_typecast::adjusted_length_with_warn(uint length)
}
-String *Item_char_typecast::val_str(String *str)
+String *Item_char_typecast::val_str_generic(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res;
@@ -2382,11 +2397,75 @@ end:
}
+String *Item_char_typecast::val_str_binary_from_native(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ DBUG_ASSERT(cast_cs == &my_charset_bin);
+ NativeBuffer<STRING_BUFFER_USUAL_SIZE> native;
+
+ if (args[0]->val_native(current_thd, &native))
+ {
+ null_value= 1;
+ return 0;
+ }
+
+ if (has_explicit_length())
+ {
+ cast_length= adjusted_length_with_warn(cast_length);
+ if (cast_length > native.length())
+ {
+ // add trailing 0x00s
+ DBUG_ASSERT(cast_length <= current_thd->variables.max_allowed_packet);
+ str->alloc(cast_length);
+ str->copy(native.ptr(), native.length(), &my_charset_bin);
+ bzero((char*) str->end(), cast_length - str->length());
+ str->length(cast_length);
+ }
+ else
+ str->copy(native.ptr(), cast_length, &my_charset_bin);
+ }
+ else
+ str->copy(native.ptr(), native.length(), &my_charset_bin);
+
+ return ((null_value= (str->length() >
+ adjusted_length_with_warn(str->length())))) ? 0 : str;
+}
+
+
+class Item_char_typecast_func_handler: public Item_handled_func::Handler_str
+{
+public:
+ const Type_handler *return_type_handler(const Item_handled_func *item) const
+ {
+ return Type_handler::string_type_handler(item->max_length);
+ }
+ const Type_handler *
+ type_handler_for_create_select(const Item_handled_func *item) const
+ {
+ return return_type_handler(item)->type_handler_for_tmp_table(item);
+ }
+
+ bool fix_length_and_dec(Item_handled_func *item) const
+ {
+ return false;
+ }
+ String *val_str(Item_handled_func *item, String *to) const
+ {
+ DBUG_ASSERT(dynamic_cast<const Item_char_typecast*>(item));
+ return static_cast<Item_char_typecast*>(item)->val_str_generic(to);
+ }
+};
+
+
+static Item_char_typecast_func_handler item_char_typecast_func_handler;
+
+
void Item_char_typecast::fix_length_and_dec_numeric()
{
fix_length_and_dec_internal(from_cs= cast_cs->mbminlen == 1 ?
cast_cs :
&my_charset_latin1);
+ set_func_handler(&item_char_typecast_func_handler);
}
@@ -2395,6 +2474,24 @@ void Item_char_typecast::fix_length_and_dec_generic()
fix_length_and_dec_internal(from_cs= args[0]->dynamic_result() ?
0 :
args[0]->collation.collation);
+ set_func_handler(&item_char_typecast_func_handler);
+}
+
+
+void Item_char_typecast::fix_length_and_dec_str()
+{
+ fix_length_and_dec_generic();
+ m_suppress_warning_to_error_escalation= true;
+ set_func_handler(&item_char_typecast_func_handler);
+}
+
+
+void
+Item_char_typecast::fix_length_and_dec_native_to_binary(uint32 octet_length)
+{
+ collation.set(&my_charset_bin, DERIVATION_IMPLICIT);
+ max_length= has_explicit_length() ? (uint32) cast_length : octet_length;
+ maybe_null|= current_thd->is_strict_mode();
}
@@ -2438,6 +2535,8 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs)
(cast_cs == &my_charset_bin ? 1 :
args[0]->collation.collation->mbmaxlen));
max_length= char_length * cast_cs->mbmaxlen;
+ // Add NULL-ability in strict mode. See Item_str_func::fix_fields()
+ maybe_null= maybe_null || current_thd->is_strict_mode();
}
@@ -2524,8 +2623,8 @@ bool Item_func_add_time::fix_length_and_dec()
{
enum_field_types arg0_field_type;
- if (!args[0]->type_handler()->is_traditional_type() ||
- !args[1]->type_handler()->is_traditional_type())
+ if (!args[0]->type_handler()->is_traditional_scalar_type() ||
+ !args[1]->type_handler()->is_traditional_scalar_type())
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
args[0]->type_handler()->name().ptr(),
@@ -2831,9 +2930,8 @@ String *Item_func_get_format::val_str_ascii(String *str)
uint format_name_len;
format_name_len= (uint) strlen(format_name);
if (val_len == format_name_len &&
- !my_strnncoll(&my_charset_latin1,
- (const uchar *) val->ptr(), val_len,
- (const uchar *) format_name, val_len))
+ !my_charset_latin1.strnncoll(val->ptr(), val_len,
+ format_name, val_len))
{
const char *format_str= get_date_time_format_str(format, type);
str->set(format_str, (uint) strlen(format_str), &my_charset_numeric);
@@ -2935,8 +3033,8 @@ get_date_time_result_type(const char *format, uint length)
bool Item_func_str_to_date::fix_length_and_dec()
{
- if (!args[0]->type_handler()->is_traditional_type() ||
- !args[1]->type_handler()->is_traditional_type())
+ if (!args[0]->type_handler()->is_traditional_scalar_type() ||
+ !args[1]->type_handler()->is_traditional_scalar_type())
{
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
args[0]->type_handler()->name().ptr(),
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 9ee561f6abb..a910b2cb723 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -963,8 +963,8 @@ class Item_extract :public Item_int_func,
uint32 threashold)
{
if (length >= threashold)
- return &type_handler_longlong;
- return &type_handler_long;
+ return &type_handler_slonglong;
+ return &type_handler_slong;
}
void set_date_length(uint32 length)
{
@@ -995,7 +995,7 @@ class Item_extract :public Item_int_func,
const interval_type int_type; // keep it public
Item_extract(THD *thd, interval_type type_arg, Item *a):
Item_int_func(thd, a),
- Type_handler_hybrid_field_type(&type_handler_longlong),
+ Type_handler_hybrid_field_type(&type_handler_slonglong),
m_date_mode(date_mode_t(0)),
int_type(type_arg)
{ }
@@ -1006,6 +1006,7 @@ class Item_extract :public Item_int_func,
longlong val_int();
enum Functype functype() const { return EXTRACT_FUNC; }
const char *func_name() const { return "extract"; }
+ bool check_arguments() const;
bool fix_length_and_dec();
bool eq(const Item *item, bool binary_cmp) const;
void print(String *str, enum_query_type query_type);
@@ -1057,14 +1058,16 @@ class Item_extract :public Item_int_func,
};
-class Item_char_typecast :public Item_str_func
+class Item_char_typecast :public Item_handled_func
{
uint cast_length;
CHARSET_INFO *cast_cs, *from_cs;
bool charset_conversion;
String tmp_value;
bool m_suppress_warning_to_error_escalation;
+public:
bool has_explicit_length() const { return cast_length != ~0U; }
+private:
String *reuse(String *src, size_t length);
String *copy(String *src, CHARSET_INFO *cs);
uint adjusted_length_with_warn(uint length);
@@ -1075,20 +1078,18 @@ public:
uint get_cast_length() const { return cast_length; }
public:
Item_char_typecast(THD *thd, Item *a, uint length_arg, CHARSET_INFO *cs_arg):
- Item_str_func(thd, a), cast_length(length_arg), cast_cs(cs_arg),
+ Item_handled_func(thd, a), cast_length(length_arg), cast_cs(cs_arg),
m_suppress_warning_to_error_escalation(false) {}
enum Functype functype() const { return CHAR_TYPECAST_FUNC; }
bool eq(const Item *item, bool binary_cmp) const;
const char *func_name() const { return "cast_as_char"; }
CHARSET_INFO *cast_charset() const { return cast_cs; }
- String *val_str(String *a);
+ String *val_str_generic(String *a);
+ String *val_str_binary_from_native(String *a);
void fix_length_and_dec_generic();
void fix_length_and_dec_numeric();
- void fix_length_and_dec_str()
- {
- fix_length_and_dec_generic();
- m_suppress_warning_to_error_escalation= true;
- }
+ void fix_length_and_dec_str();
+ void fix_length_and_dec_native_to_binary(uint32 octet_length);
bool fix_length_and_dec()
{
return args[0]->type_handler()->Item_char_typecast_fix_length_and_dec(this);
@@ -1541,9 +1542,11 @@ public:
{
uint dec= MY_MAX(item->arguments()[0]->datetime_precision(current_thd),
interval_dec(item->arguments()[1], int_type(item)));
- item->collation.set(item->default_charset(),
- DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ item->Type_std_attributes::set(
+ Type_temporal_attributes_not_fixed_dec(MAX_DATETIME_WIDTH, dec, false),
+ DTCollation(item->default_charset(),
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII));
+ item->fix_char_length(item->max_length);
return false;
}
bool get_date(THD *thd, Item_handled_func *item,
@@ -1648,9 +1651,11 @@ public:
uint dec0= item->arguments()[0]->decimals;
uint dec1= Interval_DDhhmmssff::fsp(current_thd, item->arguments()[1]);
uint dec= MY_MAX(dec0, dec1);
- item->collation.set(item->default_charset(),
- DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- item->fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
+ item->Type_std_attributes::set(
+ Type_temporal_attributes_not_fixed_dec(MAX_DATETIME_WIDTH, dec, false),
+ DTCollation(item->default_charset(),
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII));
+ item->fix_char_length(item->max_length);
return false;
}
bool get_date(THD *thd, Item_handled_func *item,
diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc
index 5a114b7a193..24c2ef5a2f2 100644
--- a/sql/item_windowfunc.cc
+++ b/sql/item_windowfunc.cc
@@ -187,13 +187,6 @@ bool Item_window_func::check_result_type_of_order_item()
case Item_sum::PERCENTILE_DISC_FUNC:
{
Item *src_item= window_spec->order_list->first->item[0];
- Item_result rtype= src_item->cmp_type();
- // TODO-10.5: Fix MDEV-20280 PERCENTILE_DISC() rejects temporal and string input
- if (rtype != REAL_RESULT && rtype != INT_RESULT && rtype != DECIMAL_RESULT)
- {
- my_error(ER_WRONG_TYPE_FOR_PERCENTILE_FUNC, MYF(0), window_func()->func_name());
- return true;
- }
Item_sum_percentile_disc *func=
static_cast<Item_sum_percentile_disc*>(window_func());
func->set_handler(src_item->type_handler());
@@ -463,7 +456,8 @@ bool Item_sum_hybrid_simple::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t f
return retval;
}
-Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table)
+Field *Item_sum_hybrid_simple::create_tmp_field(MEM_ROOT *root,
+ bool group, TABLE *table)
{
DBUG_ASSERT(0);
return NULL;
diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h
index c038cb8d15f..99ef738ac69 100644
--- a/sql/item_windowfunc.h
+++ b/sql/item_windowfunc.h
@@ -118,6 +118,8 @@ public:
Item_sum_row_number(THD *thd)
: Item_sum_int(thd), count(0) {}
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
+
void clear()
{
count= 0;
@@ -179,6 +181,8 @@ public:
Item_sum_rank(THD *thd) : Item_sum_int(thd), peer_tracker(NULL) {}
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
+
void clear()
{
/* This is called on partition start */
@@ -266,6 +270,7 @@ class Item_sum_dense_rank: public Item_sum_int
Item_sum_dense_rank(THD *thd)
: Item_sum_int(thd), dense_rank(0), first_add(true), peer_tracker(NULL) {}
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
enum Sumfunctype sum_func () const
{
return DENSE_RANK_FUNC;
@@ -318,7 +323,7 @@ class Item_sum_hybrid_simple : public Item_sum_hybrid
const Type_handler *type_handler() const
{ return Type_handler_hybrid_field_type::type_handler(); }
void update_field();
- Field *create_tmp_field(bool group, TABLE *table);
+ Field *create_tmp_field(MEM_ROOT *root, bool group, TABLE *table);
void clear()
{
value->clear();
@@ -701,6 +706,8 @@ class Item_sum_ntile : public Item_sum_int,
void update_field() {}
+ const Type_handler *type_handler() const { return &type_handler_slonglong; }
+
void reset_field() { DBUG_ASSERT(0); }
void set_partition_row_count(ulonglong count)
@@ -723,7 +730,7 @@ class Item_sum_percentile_disc : public Item_sum_num,
{
public:
Item_sum_percentile_disc(THD *thd, Item* arg) : Item_sum_num(thd, arg),
- Type_handler_hybrid_field_type(&type_handler_longlong),
+ Type_handler_hybrid_field_type(&type_handler_slonglong),
value(NULL), val_calculated(FALSE), first_call(TRUE),
prev_value(0), order_item(NULL){}
@@ -776,12 +783,23 @@ public:
if (get_row_count() == 0 || get_arg(0)->is_null())
{
null_value= true;
- return 0;
+ return true;
}
null_value= false;
return value->get_date(thd, ltime, fuzzydate);
}
+ bool val_native(THD *thd, Native *to)
+ {
+ if (get_row_count() == 0 || get_arg(0)->is_null())
+ {
+ null_value= true;
+ return true;
+ }
+ null_value= false;
+ return value->val_native(thd, to);
+ }
+
bool add()
{
Item *arg= get_arg(0);
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 28bddb75df2..6e863b1ffef 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2005, 2019, Oracle and/or its affiliates.
- Copyright (c) 2009, 2019, MariaDB
+ Copyright (c) 2009, 2020, 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
@@ -71,15 +71,6 @@ typedef struct my_xpath_lex_st
} MY_XPATH_LEX;
-/* Structure to store nodesets */
-typedef struct my_xpath_flt_st
-{
- uint num; /* absolute position in MY_XML_NODE array */
- uint pos; /* relative position in context */
- uint size; /* context size */
-} MY_XPATH_FLT;
-
-
/* XPath function creator */
typedef struct my_xpath_function_names_st
{
@@ -105,50 +96,13 @@ typedef struct my_xpath_st
Item *item; /* current expression */
Item *context; /* last scanned context */
Item *rootelement; /* The root element */
- String *context_cache; /* last context provider */
+ Native *context_cache; /* last context provider */
String *pxml; /* Parsed XML, an array of MY_XML_NODE */
CHARSET_INFO *cs; /* character set/collation string comparison */
int error;
} MY_XPATH;
-/* Dynamic array of MY_XPATH_FLT */
-class XPathFilter :public String
-{
-public:
- XPathFilter() :String() {}
- inline bool append_element(MY_XPATH_FLT *flt)
- {
- String *str= this;
- return str->append((const char*)flt, (uint32) sizeof(MY_XPATH_FLT));
- }
- inline bool append_element(uint32 num, uint32 pos)
- {
- MY_XPATH_FLT add;
- add.num= num;
- add.pos= pos;
- add.size= 0;
- return append_element(&add);
- }
- inline bool append_element(uint32 num, uint32 pos, uint32 size)
- {
- MY_XPATH_FLT add;
- add.num= num;
- add.pos= pos;
- add.size= size;
- return append_element(&add);
- }
- inline MY_XPATH_FLT *element(uint i)
- {
- return (MY_XPATH_FLT*) (ptr() + i * sizeof(MY_XPATH_FLT));
- }
- inline uint32 numelements()
- {
- return length() / sizeof(MY_XPATH_FLT);
- }
-};
-
-
static Type_handler_long_blob type_handler_xpath_nodeset;
@@ -158,13 +112,13 @@ static Type_handler_long_blob type_handler_xpath_nodeset;
class Item_nodeset_func :public Item_str_func
{
protected:
- String tmp_value, tmp2_value;
+ NativeNodesetBuffer tmp_native_value, tmp2_native_value;
MY_XPATH_FLT *fltbeg, *fltend;
MY_XML_NODE *nodebeg, *nodeend;
uint numnodes;
public:
String *pxml;
- String context_cache;
+ NativeNodesetBuffer context_cache;
Item_nodeset_func(THD *thd, String *pxml_arg):
Item_str_func(thd), pxml(pxml_arg) {}
Item_nodeset_func(THD *thd, Item *a, String *pxml_arg):
@@ -179,12 +133,12 @@ public:
nodeend= (MY_XML_NODE*) (pxml->ptr() + pxml->length());
numnodes= (uint)(nodeend - nodebeg);
}
- void prepare(String *nodeset)
+ void prepare(THD *thd, Native *nodeset)
{
prepare_nodes();
- String *res= args[0]->val_raw(&tmp_value);
- fltbeg= (MY_XPATH_FLT*) res->ptr();
- fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ args[0]->val_native(thd, &tmp_native_value);
+ fltbeg= (MY_XPATH_FLT*) tmp_native_value.ptr();
+ fltend= (MY_XPATH_FLT*) tmp_native_value.end();
nodeset->length(0);
}
const Type_handler *type_handler() const
@@ -195,7 +149,7 @@ public:
{
return &type_handler_xpath_nodeset;
}
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(0);
@@ -204,9 +158,9 @@ public:
String *val_str(String *str)
{
prepare_nodes();
- String *res= val_raw(&tmp2_value);
- fltbeg= (MY_XPATH_FLT*) res->ptr();
- fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ val_native(current_thd, &tmp2_native_value);
+ fltbeg= (MY_XPATH_FLT*) tmp2_native_value.ptr();
+ fltend= (MY_XPATH_FLT*) tmp2_native_value.end();
String active;
active.alloc(numnodes);
bzero((char*) active.ptr(), numnodes);
@@ -235,7 +189,6 @@ public:
}
return str;
}
- enum Item_result result_type () const { return STRING_RESULT; }
bool fix_length_and_dec()
{
max_length= MAX_BLOB_WIDTH;
@@ -261,7 +214,7 @@ public:
Item_nodeset_func_rootelement(THD *thd, String *pxml):
Item_nodeset_func(thd, pxml) {}
const char *func_name() const { return "xpath_rootelement"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_rootelement>(thd, this); }
};
@@ -274,7 +227,7 @@ public:
Item_nodeset_func_union(THD *thd, Item *a, Item *b, String *pxml):
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_union"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_union>(thd, this); }
};
@@ -308,7 +261,7 @@ public:
String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_selfbyname"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_selfbyname>(thd, this); }
};
@@ -322,7 +275,7 @@ public:
String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_childbyname"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_childbyname>(thd, this); }
};
@@ -338,7 +291,7 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml),
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_descendantbyname"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_descendantbyname>(thd, this); }
};
@@ -354,7 +307,7 @@ public:
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml),
need_self(need_self_arg) {}
const char *func_name() const { return "xpath_ancestorbyname"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_ancestorbyname>(thd, this); }
};
@@ -368,7 +321,7 @@ public:
String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_parentbyname"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_parentbyname>(thd, this); }
};
@@ -382,7 +335,7 @@ public:
uint l_arg, String *pxml):
Item_nodeset_func_axisbyname(thd, a, n_arg, l_arg, pxml) {}
const char *func_name() const { return "xpath_attributebyname"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_attributebyname>(thd, this); }
};
@@ -399,7 +352,7 @@ public:
Item_nodeset_func_predicate(THD *thd, Item *a, Item *b, String *pxml):
Item_nodeset_func(thd, a, b, pxml) {}
const char *func_name() const { return "xpath_predicate"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_predicate>(thd, this); }
};
@@ -412,7 +365,7 @@ public:
Item_nodeset_func_elementbyindex(THD *thd, Item *a, Item *b, String *pxml):
Item_nodeset_func(thd, a, b, pxml) { }
const char *func_name() const { return "xpath_elementbyindex"; }
- String *val_raw(String *nodeset);
+ bool val_native(THD *thd, Native *nodeset);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_func_elementbyindex>(thd, this); }
};
@@ -427,7 +380,7 @@ public:
class Item_xpath_cast_bool :public Item_bool_func
{
String *pxml;
- String tmp_value;
+ NativeNodesetBuffer tmp_native_value;
public:
Item_xpath_cast_bool(THD *thd, Item *a, String *pxml_arg):
Item_bool_func(thd, a), pxml(pxml_arg) {}
@@ -436,8 +389,8 @@ public:
{
if (args[0]->fixed_type_handler() == &type_handler_xpath_nodeset)
{
- String *flt= args[0]->val_raw(&tmp_value);
- return flt->length() == sizeof(MY_XPATH_FLT) ? 1 : 0;
+ args[0]->val_native(current_thd, &tmp_native_value);
+ return tmp_native_value.elements() == 1 ? 1 : 0;
}
return args[0]->val_real() ? 1 : 0;
}
@@ -466,11 +419,13 @@ public:
class Item_nodeset_context_cache :public Item_nodeset_func
{
public:
- String *string_cache;
- Item_nodeset_context_cache(THD *thd, String *str_arg, String *pxml):
- Item_nodeset_func(thd, pxml), string_cache(str_arg) { }
- String *val_raw(String *res)
- { return string_cache; }
+ Native *native_cache;
+ Item_nodeset_context_cache(THD *thd, Native *native_arg, String *pxml):
+ Item_nodeset_func(thd, pxml), native_cache(native_arg) { }
+ bool val_native(THD *thd, Native *nodeset)
+ {
+ return nodeset->copy(*native_cache);
+ }
bool fix_length_and_dec() { max_length= MAX_BLOB_WIDTH;; return FALSE; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_nodeset_context_cache>(thd, this); }
@@ -480,7 +435,7 @@ public:
class Item_func_xpath_position :public Item_long_func
{
String *pxml;
- String tmp_value;
+ NativeNodesetBuffer tmp_native_value;
public:
Item_func_xpath_position(THD *thd, Item *a, String *p):
Item_long_func(thd, a), pxml(p) {}
@@ -488,9 +443,9 @@ public:
bool fix_length_and_dec() { max_length=10; return FALSE; }
longlong val_int()
{
- String *flt= args[0]->val_raw(&tmp_value);
- if (flt->length() == sizeof(MY_XPATH_FLT))
- return ((MY_XPATH_FLT*)flt->ptr())->pos + 1;
+ args[0]->val_native(current_thd, &tmp_native_value);
+ if (tmp_native_value.elements() == 1)
+ return tmp_native_value.element(0).pos + 1;
return 0;
}
Item *get_copy(THD *thd)
@@ -501,7 +456,7 @@ public:
class Item_func_xpath_count :public Item_long_func
{
String *pxml;
- String tmp_value;
+ NativeNodesetBuffer tmp_native_value;
public:
Item_func_xpath_count(THD *thd, Item *a, String *p):
Item_long_func(thd, a), pxml(p) {}
@@ -510,11 +465,11 @@ public:
longlong val_int()
{
uint predicate_supplied_context_size;
- String *res= args[0]->val_raw(&tmp_value);
- if (res->length() == sizeof(MY_XPATH_FLT) &&
- (predicate_supplied_context_size= ((MY_XPATH_FLT*)res->ptr())->size))
+ args[0]->val_native(current_thd, &tmp_native_value);
+ if (tmp_native_value.elements() == 1 &&
+ (predicate_supplied_context_size= tmp_native_value.element(0).size))
return predicate_supplied_context_size;
- return res->length() / sizeof(MY_XPATH_FLT);
+ return tmp_native_value.elements();
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_xpath_count>(thd, this); }
@@ -524,7 +479,7 @@ public:
class Item_func_xpath_sum :public Item_real_func
{
String *pxml;
- String tmp_value;
+ NativeNodesetBuffer tmp_native_value;
public:
Item_func_xpath_sum(THD *thd, Item *a, String *p):
Item_real_func(thd, a), pxml(p) {}
@@ -533,9 +488,9 @@ public:
double val_real()
{
double sum= 0;
- String *res= args[0]->val_raw(&tmp_value);
- MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
- MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ args[0]->val_native(current_thd, &tmp_native_value);
+ MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) tmp_native_value.ptr();
+ MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) tmp_native_value.end();
uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
@@ -552,8 +507,8 @@ public:
{
char *end;
int err;
- double add= my_strntod(collation.collation, (char*) node->beg,
- node->end - node->beg, &end, &err);
+ double add= collation.collation->strntod((char*) node->beg,
+ node->end - node->beg, &end, &err);
if (!err)
sum+= add;
}
@@ -596,7 +551,7 @@ public:
class Item_nodeset_to_const_comparator :public Item_bool_func
{
String *pxml;
- String tmp_nodeset;
+ NativeNodesetBuffer tmp_nodeset;
public:
Item_nodeset_to_const_comparator(THD *thd, Item *nodeset, Item *cmpfunc,
String *p):
@@ -606,7 +561,7 @@ public:
{
return mark_unsupported_function(func_name(), arg, VCOL_IMPOSSIBLE);
}
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(0);
@@ -617,9 +572,9 @@ public:
Item_func *comp= (Item_func*)args[1];
Item_string_xml_non_const *fake=
(Item_string_xml_non_const*)(comp->arguments()[0]);
- String *res= args[0]->val_raw(&tmp_nodeset);
- MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
- MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
+ args[0]->val_native(current_thd, &tmp_nodeset);
+ MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) tmp_nodeset.ptr();
+ MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) tmp_nodeset.end();
MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml->ptr();
uint numnodes= pxml->length() / sizeof(MY_XML_NODE);
@@ -648,32 +603,32 @@ public:
};
-String *Item_nodeset_func_rootelement::val_raw(String *nodeset)
+bool Item_nodeset_func_rootelement::val_native(THD *thd, Native *nodeset)
{
nodeset->length(0);
- ((XPathFilter*)nodeset)->append_element(0, 0);
- return nodeset;
+ return MY_XPATH_FLT(0, 0).append_to(nodeset);
}
-String * Item_nodeset_func_union::val_raw(String *nodeset)
+bool Item_nodeset_func_union::val_native(THD *thd, Native *nodeset)
{
uint num_nodes= pxml->length() / sizeof(MY_XML_NODE);
- String set0, *s0= args[0]->val_raw(&set0);
- String set1, *s1= args[1]->val_raw(&set1);
+ NativeNodesetBuffer set0, set1;
+ args[0]->val_native(thd, &set0);
+ args[1]->val_native(thd, &set1);
String both_str;
both_str.alloc(num_nodes);
char *both= (char*) both_str.ptr();
bzero((void*)both, num_nodes);
MY_XPATH_FLT *flt;
- fltbeg= (MY_XPATH_FLT*) s0->ptr();
- fltend= (MY_XPATH_FLT*) (s0->ptr() + s0->length());
+ fltbeg= (MY_XPATH_FLT*) set0.ptr();
+ fltend= (MY_XPATH_FLT*) set0.end();
for (flt= fltbeg; flt < fltend; flt++)
both[flt->num]= 1;
- fltbeg= (MY_XPATH_FLT*) s1->ptr();
- fltend= (MY_XPATH_FLT*) (s1->ptr() + s1->length());
+ fltbeg= (MY_XPATH_FLT*) set1.ptr();
+ fltend= (MY_XPATH_FLT*) set1.end();
for (flt= fltbeg; flt < fltend; flt++)
both[flt->num]= 1;
@@ -681,29 +636,29 @@ String * Item_nodeset_func_union::val_raw(String *nodeset)
for (uint i= 0, pos= 0; i < num_nodes; i++)
{
if (both[i])
- ((XPathFilter*)nodeset)->append_element(i, pos++);
+ MY_XPATH_FLT(i, pos++).append_to(nodeset);
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_selfbyname::val_raw(String *nodeset)
+bool Item_nodeset_func_selfbyname::val_native(THD *thd, Native *nodeset)
{
- prepare(nodeset);
+ prepare(thd, nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
{
uint pos= 0;
MY_XML_NODE *self= &nodebeg[flt->num];
if (validname(self))
- ((XPathFilter*)nodeset)->append_element(flt->num,pos++);
+ MY_XPATH_FLT(flt->num, pos++).append_to(nodeset);
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_childbyname::val_raw(String *nodeset)
+bool Item_nodeset_func_childbyname::val_native(THD *thd, Native *nodeset)
{
- prepare(nodeset);
+ prepare(thd, nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
{
MY_XML_NODE *self= &nodebeg[flt->num];
@@ -715,40 +670,40 @@ String *Item_nodeset_func_childbyname::val_raw(String *nodeset)
if ((node->parent == flt->num) &&
(node->type == MY_XML_NODE_TAG) &&
validname(node))
- ((XPathFilter*)nodeset)->append_element(j, pos++);
+ MY_XPATH_FLT(j, pos++).append_to(nodeset);
}
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_descendantbyname::val_raw(String *nodeset)
+bool Item_nodeset_func_descendantbyname::val_native(THD *thd, Native *nodeset)
{
- prepare(nodeset);
+ prepare(thd, nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
{
uint pos= 0;
MY_XML_NODE *self= &nodebeg[flt->num];
if (need_self && validname(self))
- ((XPathFilter*)nodeset)->append_element(flt->num,pos++);
+ MY_XPATH_FLT(flt->num, pos++).append_to(nodeset);
for (uint j= flt->num + 1 ; j < numnodes ; j++)
{
MY_XML_NODE *node= &nodebeg[j];
if (node->level <= self->level)
break;
if ((node->type == MY_XML_NODE_TAG) && validname(node))
- ((XPathFilter*)nodeset)->append_element(j,pos++);
+ MY_XPATH_FLT(j, pos++).append_to(nodeset);
}
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_ancestorbyname::val_raw(String *nodeset)
+bool Item_nodeset_func_ancestorbyname::val_native(THD *thd, Native *nodeset)
{
char *active;
String active_str;
- prepare(nodeset);
+ prepare(thd, nodeset);
active_str.alloc(numnodes);
active= (char*) active_str.ptr();
bzero((void*)active, numnodes);
@@ -780,17 +735,17 @@ String *Item_nodeset_func_ancestorbyname::val_raw(String *nodeset)
for (uint j= 0; j < numnodes ; j++)
{
if (active[j])
- ((XPathFilter*)nodeset)->append_element(j, --pos);
+ MY_XPATH_FLT(j, --pos).append_to(nodeset);
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_parentbyname::val_raw(String *nodeset)
+bool Item_nodeset_func_parentbyname::val_native(THD *thd, Native *nodeset)
{
char *active;
String active_str;
- prepare(nodeset);
+ prepare(thd, nodeset);
active_str.alloc(numnodes);
active= (char*) active_str.ptr();
bzero((void*)active, numnodes);
@@ -803,15 +758,15 @@ String *Item_nodeset_func_parentbyname::val_raw(String *nodeset)
for (uint j= 0, pos= 0; j < numnodes ; j++)
{
if (active[j])
- ((XPathFilter*)nodeset)->append_element(j, pos++);
+ MY_XPATH_FLT(j, pos++).append_to(nodeset);
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_attributebyname::val_raw(String *nodeset)
+bool Item_nodeset_func_attributebyname::val_native(THD *thd, Native *nodeset)
{
- prepare(nodeset);
+ prepare(thd, nodeset);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
{
MY_XML_NODE *self= &nodebeg[flt->num];
@@ -823,51 +778,49 @@ String *Item_nodeset_func_attributebyname::val_raw(String *nodeset)
if ((node->parent == flt->num) &&
(node->type == MY_XML_NODE_ATTR) &&
validname(node))
- ((XPathFilter*)nodeset)->append_element(j, pos++);
+ MY_XPATH_FLT(j, pos++).append_to(nodeset);
}
}
- return nodeset;
+ return false;
}
-String *Item_nodeset_func_predicate::val_raw(String *str)
+bool Item_nodeset_func_predicate::val_native(THD *thd, Native *str)
{
Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
uint pos= 0, size;
- prepare(str);
+ prepare(thd, str);
size= (uint)(fltend - fltbeg);
for (MY_XPATH_FLT *flt= fltbeg; flt < fltend; flt++)
{
nodeset_func->context_cache.length(0);
- ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
- flt->pos,
- size);
+ MY_XPATH_FLT(flt->num, flt->pos, size).
+ append_to(&nodeset_func->context_cache);
if (args[1]->val_int())
- ((XPathFilter*)str)->append_element(flt->num, pos++);
+ MY_XPATH_FLT(flt->num, pos++).append_to(str);
}
- return str;
+ return false;
}
-String *Item_nodeset_func_elementbyindex::val_raw(String *nodeset)
+bool Item_nodeset_func_elementbyindex::val_native(THD *thd, Native *nodeset)
{
Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
- prepare(nodeset);
+ prepare(thd, nodeset);
MY_XPATH_FLT *flt;
uint pos, size= (uint)(fltend - fltbeg);
for (pos= 0, flt= fltbeg; flt < fltend; flt++)
{
nodeset_func->context_cache.length(0);
- ((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
- flt->pos,
- size);
+ MY_XPATH_FLT(flt->num, flt->pos, size).
+ append_to(&nodeset_func->context_cache);
int index= (int) (args[1]->val_int()) - 1;
if (index >= 0 &&
(flt->pos == (uint) index ||
(args[1]->type_handler()->is_bool_type())))
- ((XPathFilter*)nodeset)->append_element(flt->num, pos++);
+ MY_XPATH_FLT(flt->num, pos++).append_to(nodeset);
}
- return nodeset;
+ return false;
}
@@ -1493,16 +1446,16 @@ my_xpath_lex_scan(MY_XPATH *xpath,
}
// Check ident, or a function call, or a keyword
- if ((length= xpath->cs->cset->ctype(xpath->cs, &ctype,
- (const uchar*) beg,
- (const uchar*) end)) > 0 &&
+ if ((length= xpath->cs->ctype(&ctype,
+ (const uchar*) beg,
+ (const uchar*) end)) > 0 &&
((ctype & (_MY_L | _MY_U)) || *beg == '_'))
{
// scan until the end of the identifier
for (beg+= length;
- (length= xpath->cs->cset->ctype(xpath->cs, &ctype,
- (const uchar*) beg,
- (const uchar*) end)) > 0 &&
+ (length= xpath->cs->ctype(&ctype,
+ (const uchar*) beg,
+ (const uchar*) end)) > 0 &&
((ctype & (_MY_L | _MY_U | _MY_NMR)) ||
*beg == '_' || *beg == '-' || *beg == '.') ;
beg+= length) /* no op */;
@@ -1797,7 +1750,7 @@ my_xpath_parse_AxisSpecifier_NodeTest_opt_Predicate_list(MY_XPATH *xpath)
while (my_xpath_parse_term(xpath, MY_XPATH_LEX_LB))
{
Item *prev_context= xpath->context;
- String *context_cache;
+ Native *context_cache;
context_cache= &((Item_nodeset_func*)xpath->context)->context_cache;
xpath->context= new (xpath->thd->mem_root)
Item_nodeset_context_cache(xpath->thd, context_cache, xpath->pxml);
@@ -3081,19 +3034,20 @@ bool Item_func_xml_update::collect_result(String *str,
String *Item_func_xml_update::val_str(String *str)
{
- String *nodeset, *rep;
+ String *rep;
null_value= 0;
if (!nodeset_func || get_xml(&xml) ||
!(rep= args[2]->val_str(&tmp_value3)) ||
- !(nodeset= nodeset_func->val_raw(&tmp_value2)))
+ nodeset_func->type_handler() != &type_handler_xpath_nodeset ||
+ nodeset_func->val_native(current_thd, &tmp_native_value2))
{
null_value= 1;
return 0;
}
- MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) nodeset->ptr();
- MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (nodeset->ptr() + nodeset->length());
+ MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) tmp_native_value2.ptr();
+ MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) tmp_native_value2.end();
/* Allow replacing of one tag only */
if (fltend - fltbeg != 1)
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index ce34697d9bd..806739d1139 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -2,6 +2,7 @@
#define ITEM_XMLFUNC_INCLUDED
/* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2019, 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
@@ -23,6 +24,42 @@
typedef struct my_xml_node_st MY_XML_NODE;
+/* Structure to store nodeset elements */
+class MY_XPATH_FLT
+{
+public:
+ uint num; // Absolute position in MY_XML_NODE array
+ uint pos; // Relative position in context
+ uint size; // Context size
+public:
+ MY_XPATH_FLT(uint32 num_arg, uint32 pos_arg)
+ :num(num_arg), pos(pos_arg), size(0)
+ { }
+ MY_XPATH_FLT(uint32 num_arg, uint32 pos_arg, uint32 size_arg)
+ :num(num_arg), pos(pos_arg), size(size_arg)
+ { }
+ bool append_to(Native *to) const
+ {
+ return to->append((const char*) this, (uint32) sizeof(*this));
+ }
+};
+
+
+class NativeNodesetBuffer: public NativeBuffer<16*sizeof(MY_XPATH_FLT)>
+{
+public:
+ const MY_XPATH_FLT &element(uint i) const
+ {
+ const MY_XPATH_FLT *p= (MY_XPATH_FLT*) (ptr() + i * sizeof(MY_XPATH_FLT));
+ return *p;
+ }
+ uint32 elements() const
+ {
+ return length() / sizeof(MY_XPATH_FLT);
+ }
+};
+
+
class Item_xml_str_func: public Item_str_func
{
protected:
@@ -103,7 +140,8 @@ public:
class Item_func_xml_update: public Item_xml_str_func
{
- String tmp_value2, tmp_value3;
+ NativeNodesetBuffer tmp_native_value2;
+ String tmp_value3;
bool collect_result(String *str,
const MY_XML_NODE *cut,
const String *replace);
diff --git a/sql/key.cc b/sql/key.cc
index adff6975631..8701d7b8053 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
- Copyright (c) 2018, MariaDB
+ Copyright (c) 2018, 2020, 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
@@ -112,7 +112,7 @@ int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
@param with_zerofill skipped bytes in the key buffer to be filled with 0
*/
-void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
+void key_copy(uchar *to_key, const uchar *from_record, const KEY *key_info,
uint key_length, bool with_zerofill)
{
uint length;
@@ -141,12 +141,13 @@ void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
continue;
}
}
+ auto *from_ptr= key_part->field->ptr_in_record(from_record);
if (key_part->key_part_flag & HA_BLOB_PART ||
key_part->key_part_flag & HA_VAR_LENGTH_PART)
{
key_length-= HA_KEY_BLOB_LENGTH;
length= MY_MIN(key_length, key_part->length);
- uint bytes= key_part->field->get_key_image(to_key, length,
+ uint bytes= key_part->field->get_key_image(to_key, length, from_ptr,
key_info->flags & HA_SPATIAL ? Field::itMBR : Field::itRAW);
if (with_zerofill && bytes < length)
bzero((char*) to_key + bytes, length - bytes);
@@ -157,9 +158,9 @@ void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
length= MY_MIN(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);
+ uint bytes= field->get_key_image(to_key, length, from_ptr, Field::itRAW);
if (bytes < length)
- cs->cset->fill(cs, (char*) to_key + bytes, length - bytes, ' ');
+ cs->fill((char*) to_key + bytes, length - bytes, ' ');
}
}
}
@@ -324,12 +325,10 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
const uchar *pos= table->record[0] + key_part->offset;
if (length > char_length)
{
- char_length= my_charpos(cs, pos, pos + length, char_length);
+ char_length= cs->charpos(pos, pos + length, char_length);
set_if_smaller(char_length, length);
}
- if (cs->coll->strnncollsp(cs,
- (const uchar*) key, length,
- (const uchar*) pos, char_length))
+ if (cs->strnncollsp(key, length, pos, char_length))
return 1;
continue;
}
@@ -387,9 +386,9 @@ void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
Align, returning not more than "char_length" characters.
*/
size_t charpos, char_length= max_length / cs->mbmaxlen;
- if ((charpos= my_charpos(cs, tmp.ptr(),
- tmp.ptr() + tmp.length(),
- char_length)) < tmp.length())
+ if ((charpos= cs->charpos(tmp.ptr(),
+ tmp.ptr() + tmp.length(),
+ char_length)) < tmp.length())
tmp.length(charpos);
}
if (max_length < field->pack_length())
@@ -757,12 +756,12 @@ ulong key_hashnr(KEY *key_info, uint used_key_parts, const uchar *key)
{
if (cs->mbmaxlen > 1)
{
- size_t char_length= my_charpos(cs, pos + pack_length,
- pos + pack_length + length,
- length / cs->mbmaxlen);
+ size_t char_length= cs->charpos(pos + pack_length,
+ pos + pack_length + length,
+ length / cs->mbmaxlen);
set_if_smaller(length, char_length);
}
- cs->coll->hash_sort(cs, pos+pack_length, length, &nr, &nr2);
+ cs->hash_sort(pos+pack_length, length, &nr, &nr2);
key+= pack_length;
}
else
@@ -871,19 +870,18 @@ bool key_buf_cmp(KEY *key_info, uint used_key_parts,
size_t byte_len1= length1, byte_len2= length2;
if (cs->mbmaxlen > 1)
{
- size_t char_length1= my_charpos(cs, pos1 + pack_length,
- pos1 + pack_length + length1,
- length1 / cs->mbmaxlen);
- size_t char_length2= my_charpos(cs, pos2 + pack_length,
- pos2 + pack_length + length2,
- length2 / cs->mbmaxlen);
+ size_t char_length1= cs->charpos(pos1 + pack_length,
+ pos1 + pack_length + length1,
+ length1 / cs->mbmaxlen);
+ size_t char_length2= cs->charpos(pos2 + pack_length,
+ pos2 + pack_length + length2,
+ length2 / cs->mbmaxlen);
set_if_smaller(length1, char_length1);
set_if_smaller(length2, char_length2);
}
if (length1 != length2 ||
- cs->coll->strnncollsp(cs,
- pos1 + pack_length, byte_len1,
- pos2 + pack_length, byte_len2))
+ cs->strnncollsp(pos1 + pack_length, byte_len1,
+ pos2 + pack_length, byte_len2))
return TRUE;
key1+= pack_length; key2+= pack_length;
}
diff --git a/sql/key.h b/sql/key.h
index 45f58c75655..871373bfcd5 100644
--- a/sql/key.h
+++ b/sql/key.h
@@ -25,7 +25,7 @@ typedef struct st_key_part_info KEY_PART_INFO;
int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
uint *key_length, uint *keypart);
-void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info,
+void key_copy(uchar *to_key, const uchar *from_record, const KEY *key_info,
uint key_length, bool with_zerofill= FALSE);
void key_restore(uchar *to_record, const uchar *from_key, KEY *key_info,
uint key_length);
diff --git a/sql/keycaches.cc b/sql/keycaches.cc
index 60049cdd67d..10bec7c1de8 100644
--- a/sql/keycaches.cc
+++ b/sql/keycaches.cc
@@ -23,6 +23,9 @@
NAMED_ILIST key_caches;
NAMED_ILIST rpl_filters;
+extern "C" PSI_memory_key key_memory_KEY_CACHE;
+extern PSI_memory_key key_memory_NAMED_ILINK_name;
+
/**
ilink (intrusive list element) with a name
*/
@@ -37,7 +40,8 @@ public:
size_t name_length_arg, uchar* data_arg)
:name_length(name_length_arg), data(data_arg)
{
- name= my_strndup(name_arg, name_length, MYF(MY_WME));
+ name= my_strndup(key_memory_NAMED_ILINK_name, name_arg, name_length,
+ MYF(MY_WME));
links->push_back(this);
}
inline bool cmp(const char *name_cmp, size_t length)
@@ -118,8 +122,8 @@ KEY_CACHE *create_key_cache(const char *name, size_t length)
DBUG_ENTER("create_key_cache");
DBUG_PRINT("enter",("name: %.*s", (int)length, name));
- if ((key_cache= (KEY_CACHE*) my_malloc(sizeof(KEY_CACHE),
- MYF(MY_ZEROFILL | MY_WME))))
+ if ((key_cache= (KEY_CACHE*) my_malloc(key_memory_KEY_CACHE,
+ sizeof(KEY_CACHE), MYF(MY_ZEROFILL | MY_WME))))
{
if (!new NAMED_ILINK(&key_caches, name, length, (uchar*) key_cache))
{
diff --git a/sql/lex.h b/sql/lex.h
index 92ce01a1094..542356c0e43 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -43,6 +43,9 @@ SYM_GROUP sym_group_rtree= {"RTree keys", "HAVE_RTREE_KEYS"};
NOTE! The symbol tables should be the same regardless of what features
are compiled into the server. Don't add ifdef'ed symbols to the
lists
+ NOTE!!
+ If you add or delete symbols from this file, you must also update results for
+ the perfschema.start_server_low_digest_sql_length test!
*/
static SYMBOL symbols[] = {
@@ -174,7 +177,7 @@ static SYMBOL symbols[] = {
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)},
- { "DEALLOCATE", SYM(DEALLOCATE_SYM)},
+ { "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)},
{ "DECLARE", SYM(DECLARE_MARIADB_SYM)},
@@ -239,6 +242,7 @@ static SYMBOL symbols[] = {
{ "FALSE", SYM(FALSE_SYM)},
{ "FAST", SYM(FAST_SYM)},
{ "FAULTS", SYM(FAULTS_SYM)},
+ { "FEDERATED", SYM(FEDERATED_SYM)},
{ "FETCH", SYM(FETCH_SYM)},
{ "FIELDS", SYM(COLUMNS)},
{ "FILE", SYM(FILE_SYM)},
@@ -261,8 +265,6 @@ static SYMBOL symbols[] = {
{ "FUNCTION", SYM(FUNCTION_SYM)},
{ "GENERAL", SYM(GENERAL)},
{ "GENERATED", SYM(GENERATED_SYM)},
- { "GEOMETRY", SYM(GEOMETRY_SYM)},
- { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
{ "GET_FORMAT", SYM(GET_FORMAT)},
{ "GET", SYM(GET_SYM)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
@@ -343,7 +345,6 @@ static SYMBOL symbols[] = {
{ "LIMIT", SYM(LIMIT)},
{ "LINEAR", SYM(LINEAR_SYM)},
{ "LINES", SYM(LINES)},
- { "LINESTRING", SYM(LINESTRING)},
{ "LIST", SYM(LIST_SYM)},
{ "LOAD", SYM(LOAD)},
{ "LOCAL", SYM(LOCAL_SYM)},
@@ -408,10 +409,8 @@ static SYMBOL symbols[] = {
{ "MODE", SYM(MODE_SYM)},
{ "MODIFIES", SYM(MODIFIES_SYM)},
{ "MODIFY", SYM(MODIFY_SYM)},
+ { "MONITOR", SYM(MONITOR_SYM)},
{ "MONTH", SYM(MONTH_SYM)},
- { "MULTILINESTRING", SYM(MULTILINESTRING)},
- { "MULTIPOINT", SYM(MULTIPOINT)},
- { "MULTIPOLYGON", SYM(MULTIPOLYGON)},
{ "MUTEX", SYM(MUTEX_SYM)},
{ "MYSQL", SYM(MYSQL_SYM)},
{ "MYSQL_ERRNO", SYM(MYSQL_ERRNO_SYM)},
@@ -459,6 +458,7 @@ static SYMBOL symbols[] = {
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
{ "OVER", SYM(OVER_SYM)},
+ { "OVERLAPS", SYM(OVERLAPS_SYM)},
{ "OWNER", SYM(OWNER_SYM)},
{ "PACKAGE", SYM(PACKAGE_MARIADB_SYM)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
@@ -476,8 +476,6 @@ static SYMBOL symbols[] = {
{ "PHASE", SYM(PHASE_SYM)},
{ "PLUGIN", SYM(PLUGIN_SYM)},
{ "PLUGINS", SYM(PLUGINS_SYM)},
- { "POINT", SYM(POINT_SYM)},
- { "POLYGON", SYM(POLYGON)},
{ "PORT", SYM(PORT_SYM)},
{ "PORTION", SYM(PORTION_SYM)},
{ "PRECEDES", SYM(PRECEDES_SYM)},
@@ -528,6 +526,10 @@ static SYMBOL symbols[] = {
{ "REPAIR", SYM(REPAIR)},
{ "REPEATABLE", SYM(REPEATABLE_SYM)},
{ "REPLACE", SYM(REPLACE)},
+ { "REPLAY", SYM(REPLAY_SYM)},
+ { "REPLICA", SYM(SLAVE)},
+ { "REPLICAS", SYM(SLAVES)},
+ { "REPLICA_POS", SYM(SLAVE_POS_SYM)},
{ "REPLICATION", SYM(REPLICATION)},
{ "REPEAT", SYM(REPEAT_SYM)},
{ "REQUIRE", SYM(REQUIRE_SYM)},
@@ -663,6 +665,7 @@ static SYMBOL symbols[] = {
{ "TRAILING", SYM(TRAILING)},
{ "TRANSACTION", SYM(TRANSACTION_SYM)},
{ "TRANSACTIONAL", SYM(TRANSACTIONAL_SYM)},
+ { "THREADS", SYM(THREADS_SYM)},
{ "TRIGGER", SYM(TRIGGER_SYM)},
{ "TRIGGERS", SYM(TRIGGERS_SYM)},
{ "TRUE", SYM(TRUE_SYM)},
@@ -705,6 +708,7 @@ static SYMBOL symbols[] = {
{ "VIA", SYM(VIA_SYM)},
{ "VIEW", SYM(VIEW_SYM)},
{ "VIRTUAL", SYM(VIRTUAL_SYM)},
+ { "VISIBLE", SYM(VISIBLE_SYM)},
{ "VERSIONING", SYM(VERSIONING_SYM)},
{ "WAIT", SYM(WAIT_SYM)},
{ "WARNINGS", SYM(WARNINGS)},
@@ -713,7 +717,7 @@ static SYMBOL symbols[] = {
{ "WHEN", SYM(WHEN_SYM)},
{ "WHERE", SYM(WHERE)},
{ "WHILE", SYM(WHILE_SYM)},
- { "WINDOW", SYM(WINDOW_SYM)},
+ { "WINDOW", SYM(WINDOW_SYM)},
{ "WITH", SYM(WITH)},
{ "WITHIN", SYM(WITHIN)},
{ "WITHOUT", SYM(WITHOUT)},
@@ -749,6 +753,8 @@ static SYMBOL sql_functions[] = {
{ "EXTRACT", SYM(EXTRACT_SYM)},
{ "FIRST_VALUE", SYM(FIRST_VALUE_SYM)},
{ "GROUP_CONCAT", SYM(GROUP_CONCAT_SYM)},
+ { "JSON_ARRAYAGG", SYM(JSON_ARRAYAGG_SYM)},
+ { "JSON_OBJECTAGG", SYM(JSON_OBJECTAGG_SYM)},
{ "LAG", SYM(LAG_SYM)},
{ "LEAD", SYM(LEAD_SYM)},
{ "MAX", SYM(MAX_SYM)},
diff --git a/sql/lex_string.h b/sql/lex_string.h
index 88a7154b064..008e5f75812 100644
--- a/sql/lex_string.h
+++ b/sql/lex_string.h
@@ -18,8 +18,50 @@
#ifndef LEX_STRING_INCLUDED
#define LEX_STRING_INCLUDED
+
typedef struct st_mysql_const_lex_string LEX_CSTRING;
+
+class Lex_cstring : public LEX_CSTRING
+{
+ public:
+ Lex_cstring()
+ {
+ str= NULL;
+ length= 0;
+ }
+ Lex_cstring(const LEX_CSTRING &str)
+ {
+ LEX_CSTRING::operator=(str);
+ }
+ Lex_cstring(const char *_str, size_t _len)
+ {
+ str= _str;
+ length= _len;
+ }
+ Lex_cstring(const char *start, const char *end)
+ {
+ DBUG_ASSERT(start <= end);
+ str= start;
+ length= end - start;
+ }
+ void set(const char *_str, size_t _len)
+ {
+ str= _str;
+ length= _len;
+ }
+};
+
+
+class Lex_cstring_strlen: public Lex_cstring
+{
+public:
+ Lex_cstring_strlen(const char *from)
+ :Lex_cstring(from, from ? strlen(from) : 0)
+ { }
+};
+
+
/* Functions to compare if two lex strings are equal */
static inline bool lex_string_cmp(CHARSET_INFO *charset, const LEX_CSTRING *a,
diff --git a/sql/lock.cc b/sql/lock.cc
index a3744d7f000..ff215795604 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -77,7 +77,6 @@
#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 "sql_handler.h"
#include <hash.h>
#include "wsrep_mysqld.h"
@@ -110,12 +109,13 @@ static int
lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
{
uint system_count, i;
- bool is_superuser, log_table_write_query;
+ bool ignore_read_only, log_table_write_query;
DBUG_ENTER("lock_tables_check");
system_count= 0;
- is_superuser= thd->security_ctx->master_access & SUPER_ACL;
+ ignore_read_only=
+ (thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) != NO_ACL;
log_table_write_query= (is_log_table_write_query(thd->lex->sql_command)
|| ((flags & MYSQL_LOCK_LOG_TABLE) != 0));
@@ -180,7 +180,7 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) && !t->s->tmp_table)
{
if (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE &&
- !is_superuser && opt_readonly && !thd->slave_thread)
+ !ignore_read_only && opt_readonly && !thd->slave_thread)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
DBUG_RETURN(1);
@@ -396,7 +396,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
while (--i)
{
tables--;
- (*tables)->file->ha_external_lock(thd, F_UNLCK);
+ (*tables)->file->ha_external_unlock(thd);
(*tables)->current_lock=F_UNLCK;
}
DBUG_RETURN(error);
@@ -410,17 +410,18 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
}
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
+int mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
{
- mysql_unlock_tables(thd, sql_lock,
- (thd->variables.option_bits & OPTION_TABLE_LOCK) ||
- !(sql_lock->flags & GET_LOCK_ON_THD));
+ return mysql_unlock_tables(thd, sql_lock,
+ (thd->variables.option_bits & OPTION_TABLE_LOCK) ||
+ !(sql_lock->flags & GET_LOCK_ON_THD));
}
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
+int mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
{
bool errors= thd->is_error();
+ int error= 0;
PSI_stage_info org_stage;
DBUG_ENTER("mysql_unlock_tables");
@@ -428,7 +429,7 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
THD_STAGE_INFO(thd, stage_unlocking_tables);
if (sql_lock->table_count)
- unlock_external(thd, sql_lock->table, sql_lock->table_count);
+ error= unlock_external(thd, sql_lock->table, sql_lock->table_count);
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks, sql_lock->lock_count, 0);
if (free_lock)
@@ -436,10 +437,12 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
DBUG_ASSERT(!(sql_lock->flags & GET_LOCK_ON_THD));
my_free(sql_lock);
}
- if (likely(!errors))
+ if (likely(!errors && !error))
thd->clear_error();
THD_STAGE_INFO(thd, org_stage);
- DBUG_VOID_RETURN;
+ if (error)
+ DBUG_PRINT("exit", ("error: %d", error));
+ DBUG_RETURN(error);
}
/**
@@ -448,12 +451,16 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
This will work even if get_lock_data fails (next unlock will free all)
*/
-void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag)
+int mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag)
{
- MYSQL_LOCK *sql_lock=
- get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag);
- if (sql_lock)
- mysql_unlock_tables(thd, sql_lock, 0);
+ int error;
+ MYSQL_LOCK *sql_lock;
+ if (!(sql_lock= get_lock_data(thd, table, count,
+ GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag)))
+ error= ER_OUTOFMEMORY;
+ else
+ error= mysql_unlock_tables(thd, sql_lock, 0);
+ return error;
}
@@ -461,9 +468,10 @@ void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag)
unlock all tables locked for read.
*/
-void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
+int mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
{
uint i,found;
+ int error= 0;
DBUG_ENTER("mysql_unlock_read_tables");
/* Call external lock for all tables to be unlocked */
@@ -483,7 +491,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
/* Unlock all read locked tables */
if (i != found)
{
- (void) unlock_external(thd,table,i-found);
+ error= unlock_external(thd,table,i-found);
sql_lock->table_count=found;
}
@@ -518,7 +526,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
found+= tbl->lock_count;
table++;
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
@@ -532,8 +540,9 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
@param table the table to unlock
*/
-void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
+int mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
{
+ int error= 0;
if (locked)
{
uint i;
@@ -542,13 +551,20 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
if (locked->table[i] == table)
{
uint j, removed_locks, old_tables;
+ int tmp_error;
TABLE *tbl;
uint lock_data_end;
DBUG_ASSERT(table->lock_position == i);
/* Unlock the table. */
- mysql_unlock_some_tables(thd, &table, /* table count */ 1, 0);
+ if ((tmp_error= mysql_unlock_some_tables(thd, &table,
+ /* table count */ 1, 0)))
+ {
+ table->file->print_error(tmp_error, MYF(0));
+ if (!error)
+ error= tmp_error;
+ }
/* Decrement table_count in advance, making below expressions easier */
old_tables= --locked->table_count;
@@ -590,6 +606,7 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
}
}
}
+ return error;
}
@@ -646,7 +663,7 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
a->lock_count, b->lock_count));
if (!(sql_lock= (MYSQL_LOCK*)
- my_malloc(sizeof(*sql_lock)+
+ my_malloc(key_memory_MYSQL_LOCK, sizeof(*sql_lock) +
sizeof(THR_LOCK_DATA*)*((a->lock_count+b->lock_count)*2) +
sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
DBUG_RETURN(0); // Fatal error
@@ -707,7 +724,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
if ((*table)->current_lock != F_UNLCK)
{
(*table)->current_lock = F_UNLCK;
- if (unlikely((error=(*table)->file->ha_external_lock(thd, F_UNLCK))))
+ if (unlikely((error=(*table)->file->ha_external_unlock(thd))))
{
error_code= error;
(*table)->file->print_error(error, MYF(0));
@@ -765,7 +782,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
sizeof(table_ptr) * table_count;
if (!(sql_lock= (MYSQL_LOCK*) (flags & GET_LOCK_ON_THD ?
thd->alloc(amount) :
- my_malloc(amount, MYF(0)))))
+ my_malloc(key_memory_MYSQL_LOCK, amount,
+ 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 + lock_count * 2);
@@ -859,8 +877,10 @@ bool lock_schema_name(THD *thd, const char *db)
if (thd->has_read_only_protection())
return TRUE;
- global_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DDL, MDL_STATEMENT);
- mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&global_request, MDL_key::BACKUP, "", "", MDL_BACKUP_DDL,
+ MDL_STATEMENT);
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
mdl_requests.push_front(&mdl_request);
mdl_requests.push_front(&global_request);
@@ -917,10 +937,12 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
if (thd->has_read_only_protection())
return TRUE;
- global_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DDL, MDL_STATEMENT);
- schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE,
- MDL_TRANSACTION);
- mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&global_request, MDL_key::BACKUP, "", "", MDL_BACKUP_DDL,
+ MDL_STATEMENT);
+ MDL_REQUEST_INIT(&schema_request, MDL_key::SCHEMA, db, "",
+ MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&mdl_request, mdl_type, db, name, MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
mdl_requests.push_front(&mdl_request);
mdl_requests.push_front(&schema_request);
@@ -1041,7 +1063,7 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
MDL_BACKUP_FTWRL1));
DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
MDL_BACKUP_FTWRL2));
- mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_FTWRL1,
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_FTWRL1,
MDL_EXPLICIT);
do
diff --git a/sql/lock.h b/sql/lock.h
index e5036ea032c..0b23ddd3846 100644
--- a/sql/lock.h
+++ b/sql/lock.h
@@ -28,11 +28,11 @@ typedef struct st_mysql_lock MYSQL_LOCK;
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags);
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock);
-void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
-void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
-void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag);
-void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
+int mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock);
+int mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
+int mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
+int mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag);
+int mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table);
bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
/* Lock based on name */
diff --git a/sql/log.cc b/sql/log.cc
index 480e3b696cc..2dd1a3a45ab 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -34,7 +34,6 @@
#include "sql_parse.h" // command_name
#include "sql_time.h" // calc_time_from_sec, my_time_compare
#include "tztime.h" // my_tz_OFFSET0, struct Time_zone
-#include "sql_acl.h" // SUPER_ACL
#include "log_event.h" // Query_log_event
#include "rpl_filter.h"
#include "rpl_rli.h"
@@ -91,7 +90,13 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
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);
+static int binlog_xa_recover_dummy(handlerton *hton, XID *xid_list, uint len);
+static int binlog_commit_by_xid(handlerton *hton, XID *xid);
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
+static int binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
+ Log_event *end_ev, bool all, bool using_stmt,
+ bool using_trx);
static const LEX_CSTRING write_error_msg=
{ STRING_WITH_LEN("error writing to the binary log") };
@@ -253,7 +258,7 @@ void make_default_log_name(char **out, const char* log_ext, bool once)
else
{
my_free(*out);
- *out= my_strdup(buff, MYF(MY_WME));
+ *out= my_strdup(PSI_INSTRUMENT_ME, buff, MYF(MY_WME));
}
}
@@ -509,6 +514,12 @@ void Log_event_writer::add_status(enum_logged_status status)
cache_data->add_status(status);
}
+void Log_event_writer::set_incident()
+{
+ cache_data->set_incident();
+}
+
+
class binlog_cache_mngr {
public:
binlog_cache_mngr(my_off_t param_max_binlog_stmt_cache_size,
@@ -711,7 +722,6 @@ bool Log_to_csv_event_handler::
uint field_index;
Silence_log_table_errors error_handler;
Open_tables_backup open_tables_backup;
- ulonglong save_thd_options;
bool save_time_zone_used;
DBUG_ENTER("log_general");
@@ -721,9 +731,6 @@ bool Log_to_csv_event_handler::
*/
save_time_zone_used= thd->time_zone_used;
- save_thd_options= thd->variables.option_bits;
- thd->variables.option_bits&= ~OPTION_BIN_LOG;
-
table_list.init_one_table(&MYSQL_SCHEMA_NAME, &GENERAL_LOG_NAME, 0,
TL_WRITE_CONCURRENT_INSERT);
@@ -771,7 +778,7 @@ bool Log_to_csv_event_handler::
DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP);
- ((Field_timestamp*) table->field[0])->store_TIME(
+ table->field[0]->store_timestamp(
hrtime_to_my_time(event_time), hrtime_sec_part(event_time));
/* do a write */
@@ -803,7 +810,6 @@ bool Log_to_csv_event_handler::
table->field[field_index]->set_default();
}
- /* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0]))
goto err;
@@ -824,7 +830,6 @@ err:
if (need_close)
close_log_table(thd, &open_tables_backup);
- thd->variables.option_bits= save_thd_options;
thd->time_zone_used= save_time_zone_used;
DBUG_RETURN(result);
}
@@ -877,7 +882,6 @@ bool Log_to_csv_event_handler::
ulong lock_time= (ulong) MY_MIN(lock_utime/1000000, TIME_MAX_VALUE_SECONDS);
ulong query_time_micro= (ulong) (query_utime % 1000000);
ulong lock_time_micro= (ulong) (lock_utime % 1000000);
-
DBUG_ENTER("Log_to_csv_event_handler::log_slow");
thd->push_internal_handler(& error_handler);
@@ -912,7 +916,7 @@ bool Log_to_csv_event_handler::
/* store the time and user values */
DBUG_ASSERT(table->field[0]->type() == MYSQL_TYPE_TIMESTAMP);
- ((Field_timestamp*) table->field[0])->store_TIME(
+ table->field[0]->store_timestamp(
hrtime_to_my_time(current_time), hrtime_sec_part(current_time));
if (table->field[1]->store(user_host, user_host_len, client_cs))
goto err;
@@ -994,7 +998,6 @@ bool Log_to_csv_event_handler::
0, TRUE))
goto err;
- /* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0]))
goto err;
@@ -1354,7 +1357,7 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, size_t query_length,
my_hrtime_t current_time= { hrtime_from_time(thd->start_time) +
thd->start_time_sec_part + query_utime };
- if (!query)
+ if (!query || thd->get_command() == COM_STMT_PREPARE)
{
is_command= TRUE;
query= command_name[thd->get_command()].str;
@@ -1683,9 +1686,6 @@ binlog_trans_log_truncate(THD *thd, my_off_t pos)
int binlog_init(void *p)
{
binlog_hton= (handlerton *)p;
- binlog_hton->state= (WSREP_ON || opt_bin_log) ? SHOW_OPTION_YES
- : SHOW_OPTION_NO;
- binlog_hton->db_type=DB_TYPE_BINLOG;
binlog_hton->savepoint_offset= sizeof(my_off_t);
binlog_hton->close_connection= binlog_close_connection;
binlog_hton->savepoint_set= binlog_savepoint_set;
@@ -1694,9 +1694,17 @@ int binlog_init(void *p)
binlog_savepoint_rollback_can_release_mdl;
binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback;
- binlog_hton->prepare= binlog_prepare;
- binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
- binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
+ binlog_hton->drop_table= [](handlerton *, const char*) { return -1; };
+ if (WSREP_ON || opt_bin_log)
+ {
+ binlog_hton->prepare= binlog_prepare;
+ binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
+ binlog_hton->commit_by_xid= binlog_commit_by_xid;
+ binlog_hton->rollback_by_xid= binlog_rollback_by_xid;
+ // recover needs to be set to make xa{commit,rollback}_handlerton effective
+ binlog_hton->recover= binlog_xa_recover_dummy;
+ }
+ binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN | HTON_NO_ROLLBACK;
return 0;
}
@@ -1725,8 +1733,8 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
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);
+ DBUG_ASSERT(cache_mngr->trx_cache.empty());
+ DBUG_ASSERT(cache_mngr->stmt_cache.empty());
cache_mngr->~binlog_cache_mngr();
my_free(cache_mngr);
DBUG_RETURN(0);
@@ -1768,7 +1776,8 @@ binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
DBUG_PRINT("enter", ("end_ev: %p", end_ev));
if ((using_stmt && !cache_mngr->stmt_cache.empty()) ||
- (using_trx && !cache_mngr->trx_cache.empty()))
+ (using_trx && !cache_mngr->trx_cache.empty()) ||
+ thd->transaction->xid_state.is_explicit_XA())
{
if (using_stmt && thd->binlog_flush_pending_rows_event(TRUE, FALSE))
DBUG_RETURN(1);
@@ -1840,6 +1849,17 @@ binlog_commit_flush_stmt_cache(THD *thd, bool all,
DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE));
}
+
+inline size_t serialize_with_xid(XID *xid, char *buf,
+ const char *query, size_t q_len)
+{
+ memcpy(buf, query, q_len);
+
+ return
+ q_len + strlen(static_cast<event_xid_t*>(xid)->serialize(buf + q_len));
+}
+
+
/**
This function flushes the trx-cache upon commit.
@@ -1853,11 +1873,28 @@ static inline int
binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr)
{
DBUG_ENTER("binlog_commit_flush_trx_cache");
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
+
+ const char query[]= "XA COMMIT ";
+ const size_t q_len= sizeof(query) - 1; // do not count trailing 0
+ char buf[q_len + ser_buf_size]= "COMMIT";
+ size_t buflen= sizeof("COMMIT") - 1;
+
+ if (thd->lex->sql_command == SQLCOM_XA_COMMIT &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ DBUG_ASSERT(thd->transaction->xid_state.is_explicit_XA());
+ DBUG_ASSERT(thd->transaction->xid_state.get_state_code() ==
+ XA_PREPARED);
+
+ buflen= serialize_with_xid(thd->transaction->xid_state.get_xid(),
+ buf, query, q_len);
+ }
+ Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0);
+
DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
}
+
/**
This function flushes the trx-cache upon rollback.
@@ -1871,8 +1908,20 @@ static inline int
binlog_rollback_flush_trx_cache(THD *thd, bool all,
binlog_cache_mngr *cache_mngr)
{
- Query_log_event end_evt(thd, STRING_WITH_LEN("ROLLBACK"),
- TRUE, TRUE, TRUE, 0);
+ const char query[]= "XA ROLLBACK ";
+ const size_t q_len= sizeof(query) - 1; // do not count trailing 0
+ char buf[q_len + ser_buf_size]= "ROLLBACK";
+ size_t buflen= sizeof("ROLLBACK") - 1;
+
+ if (thd->transaction->xid_state.is_explicit_XA())
+ {
+ /* for not prepared use plain ROLLBACK */
+ if (thd->transaction->xid_state.get_state_code() == XA_PREPARED)
+ buflen= serialize_with_xid(thd->transaction->xid_state.get_xid(),
+ buf, query, q_len);
+ }
+ Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0);
+
return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
}
@@ -1890,23 +1939,10 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
- {
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
+ DBUG_ASSERT(xid); // replaced former treatment of ONE-PHASE XA
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1947,7 +1983,7 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
if (cache_mngr->trx_cache.has_incident())
error= mysql_bin_log.write_incident(thd);
- thd->clear_binlog_table_maps();
+ thd->reset_binlog_for_next_statement();
cache_mngr->reset(false, true);
}
@@ -1962,17 +1998,62 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
DBUG_RETURN(error);
}
+
+inline bool is_preparing_xa(THD *thd)
+{
+ return
+ thd->transaction->xid_state.is_explicit_XA() &&
+ thd->lex->sql_command == SQLCOM_XA_PREPARE;
+}
+
+
static int binlog_prepare(handlerton *hton, THD *thd, bool all)
{
- /*
- do nothing.
- just pretend we can do 2pc, so that MySQL won't
- switch to 1pc.
- real work will be done in MYSQL_BIN_LOG::log_and_order()
- */
+ /* Do nothing unless the transaction is a user XA. */
+ return is_preparing_xa(thd) ? binlog_commit(NULL, thd, all) : 0;
+}
+
+
+static int binlog_xa_recover_dummy(handlerton *hton __attribute__((unused)),
+ XID *xid_list __attribute__((unused)),
+ uint len __attribute__((unused)))
+{
+ /* Does nothing. */
return 0;
}
+
+static int binlog_commit_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+
+ (void) thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT);
+
+ return binlog_commit(hton, thd, TRUE);
+}
+
+
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+
+ (void) thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK ||
+ (thd->transaction->xid_state.get_state_code() == XA_ROLLBACK_ONLY));
+ return binlog_rollback(hton, thd, TRUE);
+}
+
+
+inline bool is_prepared_xa(THD *thd)
+{
+ return thd->transaction->xid_state.is_explicit_XA() &&
+ thd->transaction->xid_state.get_state_code() == XA_PREPARED;
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -1995,7 +2076,50 @@ static bool trans_cannot_safely_rollback(THD *thd, bool all)
thd->wsrep_binlog_format() == BINLOG_FORMAT_MIXED) ||
(trans_has_updated_non_trans_table(thd) &&
ending_single_stmt_trans(thd,all) &&
- thd->wsrep_binlog_format() == BINLOG_FORMAT_MIXED));
+ thd->wsrep_binlog_format() == BINLOG_FORMAT_MIXED) ||
+ is_prepared_xa(thd));
+}
+
+
+/**
+ Specific log flusher invoked through log_xa_prepare().
+*/
+static int binlog_commit_flush_xa_prepare(THD *thd, bool all,
+ binlog_cache_mngr *cache_mngr)
+{
+ XID *xid= thd->transaction->xid_state.get_xid();
+ {
+ // todo assert wsrep_simulate || is_open()
+
+ /*
+ Log the XA END event first.
+ We don't do that in trans_xa_end() as XA COMMIT ONE PHASE
+ is logged as simple BEGIN/COMMIT so the XA END should
+ not get to the log.
+ */
+ const char query[]= "XA END ";
+ const size_t q_len= sizeof(query) - 1; // do not count trailing 0
+ char buf[q_len + ser_buf_size];
+ size_t buflen;
+ binlog_cache_data *cache_data;
+ IO_CACHE *file;
+
+ memcpy(buf, query, q_len);
+ buflen= q_len +
+ strlen(static_cast<event_xid_t*>(xid)->serialize(buf + q_len));
+ cache_data= cache_mngr->get_binlog_cache_data(true);
+ file= &cache_data->cache_log;
+ thd->lex->sql_command= SQLCOM_XA_END;
+ Query_log_event xa_end(thd, buf, buflen, true, false, true, 0);
+ if (mysql_bin_log.write_event(&xa_end, cache_data, file))
+ return 1;
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
@@ -2022,16 +2146,26 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
if (!cache_mngr)
{
- DBUG_ASSERT(WSREP(thd));
+ DBUG_ASSERT(WSREP(thd) ||
+ (thd->lex->sql_command != SQLCOM_XA_PREPARE &&
+ !(thd->lex->sql_command == SQLCOM_XA_COMMIT &&
+ thd->lex->xa_opt == XA_ONE_PHASE)));
+
DBUG_RETURN(0);
}
+ /*
+ This is true if we are doing an alter table that is replicated as
+ CREATE TABLE ... SELECT
+ */
+ if (thd->variables.option_bits & OPTION_BIN_COMMIT_OFF)
+ DBUG_RETURN(0);
DBUG_PRINT("debug",
("all: %d, in_transaction: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
all,
YESNO(thd->in_multi_stmt_transaction_mode()),
- YESNO(thd->transaction.all.modified_non_trans_table),
- YESNO(thd->transaction.stmt.modified_non_trans_table)));
+ YESNO(thd->transaction->all.modified_non_trans_table),
+ YESNO(thd->transaction->stmt.modified_non_trans_table)));
thd->backup_stage(&org_stage);
@@ -2041,7 +2175,8 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
}
- if (cache_mngr->trx_cache.empty())
+ if (cache_mngr->trx_cache.empty() &&
+ thd->transaction->xid_state.get_state_code() != XA_PREPARED)
{
/*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
@@ -2058,8 +2193,11 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
Otherwise, we accumulate the changes.
*/
if (likely(!error) && ending_trans(thd, all))
- error= binlog_commit_flush_trx_cache(thd, all, cache_mngr);
-
+ {
+ error= is_preparing_xa(thd) ?
+ binlog_commit_flush_xa_prepare(thd, all, cache_mngr) :
+ binlog_commit_flush_trx_cache (thd, all, cache_mngr);
+ }
/*
This is part of the stmt rollback.
*/
@@ -2083,6 +2221,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
static int binlog_rollback(handlerton *hton, THD *thd, bool all)
{
DBUG_ENTER("binlog_rollback");
+
int error= 0;
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
@@ -2090,13 +2229,15 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
if (!cache_mngr)
{
DBUG_ASSERT(WSREP(thd));
+ DBUG_ASSERT(thd->lex->sql_command != SQLCOM_XA_ROLLBACK);
+
DBUG_RETURN(0);
}
DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s, stmt.modified_non_trans_table: %s",
YESNO(all),
- YESNO(thd->transaction.all.modified_non_trans_table),
- YESNO(thd->transaction.stmt.modified_non_trans_table)));
+ YESNO(thd->transaction->all.modified_non_trans_table),
+ YESNO(thd->transaction->stmt.modified_non_trans_table)));
/*
If an incident event is set we do not flush the content of the statement
@@ -2104,20 +2245,22 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
*/
if (cache_mngr->stmt_cache.has_incident())
{
- error= mysql_bin_log.write_incident(thd);
+ error |= static_cast<int>(mysql_bin_log.write_incident(thd));
cache_mngr->reset(true, false);
}
else if (!cache_mngr->stmt_cache.empty())
{
- error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
+ error |= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
}
- if (cache_mngr->trx_cache.empty())
+ if (cache_mngr->trx_cache.empty() &&
+ thd->transaction->xid_state.get_state_code() != XA_PREPARED)
{
/*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
*/
cache_mngr->reset(false, true);
+ thd->reset_binlog_for_next_statement();
DBUG_RETURN(error);
}
if (!wsrep_emulate_bin_log && mysql_bin_log.check_write_error(thd))
@@ -2162,6 +2305,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
*/
if (!all)
cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+ thd->reset_binlog_for_next_statement();
DBUG_RETURN(error);
}
@@ -2214,8 +2358,8 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
if (WSREP_EMULATE_BINLOG(thd))
{
if (is_transactional)
- trans_register_ha(thd, TRUE, binlog_hton);
- trans_register_ha(thd, FALSE, binlog_hton);
+ trans_register_ha(thd, TRUE, binlog_hton, 0);
+ trans_register_ha(thd, FALSE, binlog_hton, 0);
}
#endif /* WITH_WSREP */
DBUG_VOID_RETURN;
@@ -2337,14 +2481,14 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
/*
When a SAVEPOINT is executed inside a stored function/trigger we force the
- pending event to be flushed with a STMT_END_F flag and clear the table maps
+ pending event to be flushed with a STMT_END_F flag and reset binlog
as well to ensure that following DMLs will have a clean state to start
with. ROLLBACK inside a stored routine has to finalize possibly existing
current row-based pending event with cleaning up table maps. That ensures
that following DMLs will have a clean state to start with.
*/
if (thd->in_sub_stmt)
- thd->clear_binlog_table_maps();
+ thd->reset_binlog_for_next_statement();
DBUG_RETURN(0);
}
@@ -2409,8 +2553,8 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
*errmsg = "Could not open log file";
goto err;
}
- if (init_io_cache(log, file, (size_t)binlog_file_cache_size, READ_CACHE, 0, 0,
- MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
+ if (init_io_cache_ext(log, file, (size_t)binlog_file_cache_size, READ_CACHE,
+ 0, 0, MYF(MY_WME|MY_DONT_CHECK_FILESIZE), key_file_binlog_cache))
{
sql_print_error("Failed to create a cache on log (file '%s')",
log_file_name);
@@ -2588,24 +2732,14 @@ end:
}
-void MYSQL_LOG::init(enum_log_type log_type_arg,
- enum cache_type io_cache_type_arg)
-{
- DBUG_ENTER("MYSQL_LOG::init");
- log_type= log_type_arg;
- io_cache_type= io_cache_type_arg;
- DBUG_PRINT("info",("log_type: %d", log_type));
- DBUG_VOID_RETURN;
-}
-
-
bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
const char *new_name,
ulong next_log_number,
enum_log_type log_type_arg,
enum cache_type io_cache_type_arg)
{
- init(log_type_arg, io_cache_type_arg);
+ log_type= log_type_arg;
+ io_cache_type= io_cache_type_arg;
if (new_name)
{
@@ -2659,7 +2793,7 @@ bool MYSQL_LOG::open(
write_error= 0;
- if (!(name= my_strdup(log_name, MYF(MY_WME))))
+ if (!(name= my_strdup(key_memory_MYSQL_LOG_name, log_name, MYF(MY_WME))))
{
name= (char *)log_name; // for the error message
goto err;
@@ -2702,7 +2836,9 @@ bool MYSQL_LOG::open(
else if ((seek_offset= mysql_file_tell(file, MYF(MY_WME))))
goto err;
- if (init_io_cache(&log_file, file, IO_SIZE, io_cache_type, seek_offset, 0,
+ if (init_io_cache(&log_file, file, (log_type == LOG_NORMAL ? IO_SIZE :
+ LOG_BIN_IO_SIZE),
+ io_cache_type, seek_offset, 0,
MYF(MY_WME | MY_NABP |
((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
goto err;
@@ -3399,10 +3535,11 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC,
MYF(MY_WME))) < 0 ||
mysql_file_sync(index_file_nr, MYF(MY_WME)) ||
- init_io_cache(&index_file, index_file_nr,
+ init_io_cache_ext(&index_file, index_file_nr,
IO_SIZE, WRITE_CACHE,
mysql_file_seek(index_file_nr, 0L, MY_SEEK_END, MYF(0)),
- 0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL),
+ m_key_file_log_index_cache) ||
DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
{
/*
@@ -3456,7 +3593,6 @@ bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
*/
bool MYSQL_BIN_LOG::open(const char *log_name,
- enum_log_type log_type_arg,
const char *new_name,
ulong next_log_number,
enum cache_type io_cache_type_arg,
@@ -3467,7 +3603,6 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
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));
mysql_mutex_assert_owner(&LOCK_log);
@@ -3487,7 +3622,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
/* We need to calculate new log file name for purge to delete old */
if (init_and_set_log_file_name(log_name, new_name, next_log_number,
- log_type_arg, io_cache_type_arg))
+ LOG_BIN, io_cache_type_arg))
{
sql_print_error("MYSQL_BIN_LOG::open failed to generate new file name.");
if (!is_relay_log)
@@ -4317,7 +4452,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD *thd, bool create_new_log,
}
}
if (create_new_log && !open_index_file(index_file_name, 0, FALSE))
- if (unlikely((error= open(save_name, log_type, 0, next_log_number,
+ if (unlikely((error= open(save_name, 0, next_log_number,
io_cache_type, max_size, 0, FALSE))))
goto err;
my_free((void *) save_name);
@@ -4453,14 +4588,16 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
{
rli->last_inuse_relaylog= NULL;
included= 1;
- to_purge_if_included= my_strdup(ir->name, MYF(0));
+ to_purge_if_included= my_strdup(key_memory_Relay_log_info_group_relay_log_name,
+ 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));
+ to_purge_if_included= my_strdup(key_memory_Relay_log_info_group_relay_log_name,
+ ir->name, MYF(0));
/*
Read the next log file name from the index file and pass it back to
@@ -5053,55 +5190,6 @@ MYSQL_BIN_LOG::is_xidlist_idle_nolock()
return true;
}
-#ifdef WITH_WSREP
-inline bool
-is_gtid_cached_internal(IO_CACHE *file)
-{
- uchar data[EVENT_TYPE_OFFSET+1];
- bool result= false;
- my_off_t write_pos= my_b_tell(file);
- if (reinit_io_cache(file, READ_CACHE, 0, 0, 0))
- return false;
- /*
- In the cache we have gtid event if , below condition is true,
- */
- my_b_read(file, data, sizeof(data));
- uint event_type= (uchar)data[EVENT_TYPE_OFFSET];
- if (event_type == GTID_LOG_EVENT)
- result= true;
- /*
- Cleanup , Why because we have not read the full buffer
- and this will cause next to next reinit_io_cache(called in write_cache)
- to make cache empty.
- */
- file->read_pos= file->read_end;
- if (reinit_io_cache(file, WRITE_CACHE, write_pos, 0, 0))
- return false;
- return result;
-}
-#endif
-
-#ifdef WITH_WSREP
-inline bool
-MYSQL_BIN_LOG::is_gtid_cached(THD *thd)
-{
- binlog_cache_mngr *mngr= (binlog_cache_mngr *) thd_get_ha_data(
- thd, binlog_hton);
- if (!mngr)
- return false;
- binlog_cache_data *cache_trans= mngr->get_binlog_cache_data(
- use_trans_cache(thd, true));
- binlog_cache_data *cache_stmt= mngr->get_binlog_cache_data(
- use_trans_cache(thd, false));
- if (cache_trans && !cache_trans->empty() &&
- is_gtid_cached_internal(&cache_trans->cache_log))
- return true;
- if (cache_stmt && !cache_stmt->empty() &&
- is_gtid_cached_internal(&cache_stmt->cache_log))
- return true;
- return false;
-}
-#endif
/**
Create a new log file name.
@@ -5224,34 +5312,33 @@ int MYSQL_BIN_LOG::new_file_impl()
}
new_name_ptr=new_name;
- if (log_type == LOG_BIN)
{
+ /*
+ We log the whole file name for log file as the user may decide
+ to change base names at some point.
+ */
+ Rotate_log_event r(new_name + dirname_length(new_name), 0, LOG_EVENT_OFFSET,
+ is_relay_log ? Rotate_log_event::RELAY_LOG : 0);
+ /*
+ The current relay-log's closing Rotate event must have checksum
+ value computed with an algorithm of the last relay-logged FD event.
+ */
+ if (is_relay_log)
+ r.checksum_alg= relay_log_checksum_alg;
+ DBUG_ASSERT(!is_relay_log ||
+ relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
+ if (DBUG_EVALUATE_IF("fault_injection_new_file_rotate_event",
+ (error= close_on_error= TRUE), FALSE) ||
+ (error= write_event(&r)))
{
- /*
- We log the whole file name for log file as the user may decide
- to change base names at some point.
- */
- Rotate_log_event r(new_name+dirname_length(new_name), 0, LOG_EVENT_OFFSET,
- is_relay_log ? Rotate_log_event::RELAY_LOG : 0);
- /*
- The current relay-log's closing Rotate event must have checksum
- value computed with an algorithm of the last relay-logged FD event.
- */
- if (is_relay_log)
- r.checksum_alg= relay_log_checksum_alg;
- DBUG_ASSERT(!is_relay_log || relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
- if(DBUG_EVALUATE_IF("fault_injection_new_file_rotate_event", (error=close_on_error=TRUE), FALSE) ||
- (error= write_event(&r)))
- {
- DBUG_EXECUTE_IF("fault_injection_new_file_rotate_event", errno=2;);
- close_on_error= TRUE;
- my_printf_error(ER_ERROR_ON_WRITE,
- ER_THD_OR_DEFAULT(current_thd, ER_CANT_OPEN_FILE),
- MYF(ME_FATAL), name, errno);
- goto end;
- }
- bytes_written += r.data_written;
+ DBUG_EXECUTE_IF("fault_injection_new_file_rotate_event", errno= 2;);
+ close_on_error= TRUE;
+ my_printf_error(ER_ERROR_ON_WRITE,
+ ER_THD_OR_DEFAULT(current_thd, ER_CANT_OPEN_FILE),
+ MYF(ME_FATAL), name, errno);
+ goto end;
}
+ bytes_written+= r.data_written;
}
/*
@@ -5282,7 +5369,7 @@ int MYSQL_BIN_LOG::new_file_impl()
delay_close= true;
}
close(close_flag);
- if (log_type == LOG_BIN && checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF)
+ if (checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF)
{
DBUG_ASSERT(!is_relay_log);
DBUG_ASSERT(binlog_checksum_options != checksum_alg_reset);
@@ -5309,8 +5396,7 @@ int MYSQL_BIN_LOG::new_file_impl()
{
/* reopen the binary log file. */
file_to_open= new_name_ptr;
- error= open(old_name, log_type, new_name_ptr, 0, io_cache_type,
- max_size, 1, FALSE);
+ error= open(old_name, new_name_ptr, 0, io_cache_type, max_size, 1, FALSE);
}
/* handle reopening errors */
@@ -5365,7 +5451,10 @@ bool MYSQL_BIN_LOG::write_event(Log_event *ev, binlog_cache_data *cache_data,
{
Log_event_writer writer(file, 0, &crypto);
if (crypto.scheme && file == &log_file)
+ {
writer.ctx= alloca(crypto.ctx_size);
+ writer.set_encrypted_writer();
+ }
if (cache_data)
cache_data->add_status(ev->logged_status());
return writer.write(ev);
@@ -5533,17 +5622,19 @@ trans_has_updated_trans_table(const THD* thd)
@param thd The client thread that executed the current statement.
@return
- @c true if a transactional table was updated, @c false otherwise.
+ @c true if a transactional table with rollback was updated,
+ @c false otherwise.
*/
bool
stmt_has_updated_trans_table(const THD *thd)
{
Ha_trx_info *ha_info;
- for (ha_info= thd->transaction.stmt.ha_list; ha_info;
+ for (ha_info= thd->transaction->stmt.ha_list; ha_info;
ha_info= ha_info->next())
{
- if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
+ if (ha_info->is_trx_read_write() &&
+ !(ha_info->ht()->flags & HTON_NO_ROLLBACK))
return (TRUE);
}
return (FALSE);
@@ -5619,8 +5710,8 @@ bool ending_single_stmt_trans(THD* thd, const bool all)
*/
bool trans_has_updated_non_trans_table(const THD* thd)
{
- return (thd->transaction.all.modified_non_trans_table ||
- thd->transaction.stmt.modified_non_trans_table);
+ return (thd->transaction->all.modified_non_trans_table ||
+ thd->transaction->stmt.modified_non_trans_table);
}
/**
@@ -5633,7 +5724,7 @@ bool trans_has_updated_non_trans_table(const THD* thd)
*/
bool stmt_has_updated_non_trans_table(const THD* thd)
{
- return (thd->transaction.stmt.modified_non_trans_table);
+ return (thd->transaction->stmt.modified_non_trans_table);
}
/*
@@ -5650,7 +5741,8 @@ binlog_cache_mngr *THD::binlog_setup_trx_data()
if (cache_mngr)
DBUG_RETURN(cache_mngr); // Already set up
- cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr), MYF(MY_ZEROFILL));
+ cache_mngr= (binlog_cache_mngr*) my_malloc(key_memory_binlog_cache_mngr,
+ sizeof(binlog_cache_mngr), MYF(MY_ZEROFILL));
if (!cache_mngr ||
open_cached_file(&cache_mngr->stmt_cache.cache_log, mysql_tmpdir,
LOG_PREFIX, (size_t)binlog_stmt_cache_size, MYF(MY_WME)) ||
@@ -5693,7 +5785,7 @@ binlog_cache_mngr *THD::binlog_setup_trx_data()
We only update the saved position if the old one was undefined,
the reason is that there are some cases (e.g., for CREATE-SELECT)
where the position is saved twice (e.g., both in
- select_create::prepare() and THD::binlog_write_table_map()) , but
+ select_create::prepare() and binlog_write_table_map()) , but
we should use the first. This means that calls to this function
can be used to start the statement before the first table map
event, to include some extra events.
@@ -5726,36 +5818,52 @@ THD::binlog_start_trans_and_stmt()
{
DBUG_VOID_RETURN;
}
- /* Write Gtid
- Get domain id only when gtid mode is set
- If this event is replicate through a master then ,
- we will forward the same gtid another nodes
- We have to do this only one time in mysql transaction.
- Since this function is called multiple times , We will check for
- ha_info->is_started()
- */
+ /* If this event replicates through a master-slave then we need to
+ inject manually GTID so it is preserved in the cluster. We are writing
+ directly to WSREP buffer and not in IO cache because in case of IO cache
+ GTID event will be duplicated in binlog.
+ We have to do this only one time in mysql transaction.
+ Since this function is called multiple times , We will check for
+ ha_info->is_started().
+ */
Ha_trx_info *ha_info;
ha_info= this->ha_data[binlog_hton->slot].ha_info + (mstmt_mode ? 1 : 0);
- if (!ha_info->is_started() && wsrep_gtid_mode
- && this->variables.gtid_seq_no)
- {
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
- binlog_cache_data *cache_data= cache_mngr->get_binlog_cache_data(1);
- IO_CACHE *file= &cache_data->cache_log;
- Log_event_writer writer(file, cache_data);
- Gtid_log_event gtid_event(this, this->variables.gtid_seq_no,
- this->variables.gtid_domain_id,
- true, LOG_EVENT_SUPPRESS_USE_F,
- true, 0);
- gtid_event.server_id= this->variables.server_id;
- writer.write(&gtid_event);
+ if (!ha_info->is_started() &&
+ (this->variables.gtid_seq_no || this->variables.wsrep_gtid_seq_no) &&
+ wsrep_on(this) &&
+ (this->wsrep_cs().mode() == wsrep::client_state::m_local))
+ {
+ uchar *buf= 0;
+ size_t len= 0;
+ IO_CACHE tmp_io_cache;
+ Log_event_writer writer(&tmp_io_cache, 0);
+ if(!open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX,
+ 128, MYF(MY_WME)))
+ {
+ uint64 seqno= this->variables.gtid_seq_no;
+ uint32 domain_id= this->variables.gtid_domain_id;
+ uint32 server_id= this->variables.server_id;
+ if (!this->variables.gtid_seq_no && this->variables.wsrep_gtid_seq_no)
+ {
+ seqno= this->variables.wsrep_gtid_seq_no;
+ domain_id= wsrep_gtid_server.domain_id;
+ server_id= wsrep_gtid_server.server_id;
+ }
+ Gtid_log_event gtid_event(this, seqno, domain_id, true,
+ LOG_EVENT_SUPPRESS_USE_F, true, 0);
+ gtid_event.server_id= server_id;
+ writer.write(&gtid_event);
+ wsrep_write_cache_buf(&tmp_io_cache, &buf, &len);
+ if (len > 0) this->wsrep_cs().append_data(wsrep::const_buffer(buf, len));
+ if (buf) my_free(buf);
+ close_cached_file(&tmp_io_cache);
+ }
}
#endif
if (mstmt_mode)
- trans_register_ha(this, TRUE, binlog_hton);
- trans_register_ha(this, FALSE, binlog_hton);
+ trans_register_ha(this, TRUE, binlog_hton, 0);
+ trans_register_ha(this, FALSE, binlog_hton, 0);
/*
Mark statement transaction as read/write. We never start
a binary log transaction and keep it read-only,
@@ -5799,50 +5907,154 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd)
strmake_buf(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file);
cache_mngr->last_commit_pos_offset= mysql_bin_log.last_commit_pos_offset;
- trans_register_ha(thd, TRUE, hton);
+ trans_register_ha(thd, TRUE, binlog_hton, 0);
DBUG_RETURN(err);
}
+
+/**
+ Prepare all tables that are updated for row logging
+
+ Annotate events and table maps are written by binlog_write_table_maps()
+*/
+
+void THD::binlog_prepare_for_row_logging()
+{
+ DBUG_ENTER("THD::binlog_prepare_for_row_logging");
+ for (TABLE *table= open_tables ; table; table= table->next)
+ {
+ if (table->query_id == query_id && table->current_lock == F_WRLCK)
+ table->file->prepare_for_row_logging();
+ }
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Write annnotated row event (the query) if needed
+*/
+
+bool THD::binlog_write_annotated_row(Log_event_writer *writer)
+{
+ int error;
+ DBUG_ENTER("THD::binlog_write_annotated_row");
+
+ if (!(IF_WSREP(!wsrep_fragments_certified_for_stmt(this), true) &&
+ variables.binlog_annotate_row_events &&
+ query_length()))
+ DBUG_RETURN(0);
+
+ Annotate_rows_log_event anno(this, 0, false);
+ if (unlikely((error= writer->write(&anno))))
+ {
+ if (my_errno == EFBIG)
+ writer->set_incident();
+ DBUG_RETURN(error);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Write table map events for all tables that are using row logging.
+ This includes all tables used by this statement, including tables
+ used in triggers.
+
+ Also write annotate events and start transactions.
+ This is using the "tables_with_row_logging" list prepared by
+ THD::binlog_prepare_for_row_logging
+*/
+
+bool THD::binlog_write_table_maps()
+{
+ bool with_annotate;
+ MYSQL_LOCK *locks[2], **locks_end= locks;
+ DBUG_ENTER("THD::binlog_write_table_maps");
+
+ DBUG_ASSERT(!binlog_table_maps);
+ DBUG_ASSERT(is_current_stmt_binlog_format_row());
+
+ /* Initialize cache_mngr once per statement */
+ binlog_start_trans_and_stmt();
+ with_annotate= 1; // Write annotate with first map
+
+ if ((*locks_end= extra_lock))
+ locks_end++;
+ if ((*locks_end= lock))
+ locks_end++;
+
+ for (MYSQL_LOCK **cur_lock= locks ; cur_lock < locks_end ; cur_lock++)
+ {
+ TABLE **const end_ptr= (*cur_lock)->table + (*cur_lock)->table_count;
+ for (TABLE **table_ptr= (*cur_lock)->table;
+ table_ptr != end_ptr ;
+ ++table_ptr)
+ {
+ TABLE *table= *table_ptr;
+ bool restore= 0;
+ /*
+ We have to also write table maps for tables that have not yet been
+ used, like for tables in after triggers
+ */
+ if (!table->file->row_logging &&
+ table->query_id != query_id && table->current_lock == F_WRLCK)
+ {
+ if (table->file->prepare_for_row_logging())
+ restore= 1;
+ }
+ if (table->file->row_logging)
+ {
+ if (binlog_write_table_map(table, with_annotate))
+ DBUG_RETURN(1);
+ with_annotate= 0;
+ }
+ if (restore)
+ {
+ /*
+ Restore original setting so that it doesn't cause problem for the
+ next statement
+ */
+ table->file->row_logging= table->file->row_logging_init= 0;
+ }
+ }
+ }
+ binlog_table_maps= 1; // Table maps written
+ DBUG_RETURN(0);
+}
+
+
/**
This function writes a table map to the binary log.
Note that in order to keep the signature uniform with related methods,
we use a redundant parameter to indicate whether a transactional table
was changed or not.
- If with_annotate != NULL and
- *with_annotate = TRUE write also Annotate_rows before the table map.
-
@param table a pointer to the table.
- @param is_transactional @c true indicates a transactional table,
- otherwise @c false a non-transactional.
+ @param with_annotate If true call binlog_write_annotated_row()
+
@return
nonzero if an error pops up when writing the table map event.
*/
-int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
- my_bool *with_annotate)
+
+bool THD::binlog_write_table_map(TABLE *table, bool with_annotate)
{
int error;
+ bool is_transactional= table->file->row_logging_has_trans;
DBUG_ENTER("THD::binlog_write_table_map");
DBUG_PRINT("enter", ("table: %p (%s: #%lu)",
table, table->s->table_name.str,
table->s->table_map_id));
+ /* Pre-conditions */
+ DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
+
/* 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 */
- DBUG_ASSERT(is_current_stmt_binlog_format_row());
- DBUG_ASSERT(WSREP_EMULATE_BINLOG_NNULL(this) || mysql_bin_log.is_open());
- DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
-
Table_map_log_event
the_event(this, table, table->s->table_map_id, is_transactional);
- if (binlog_table_maps == 0)
- binlog_start_trans_and_stmt();
-
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
binlog_cache_data *cache_data= (cache_mngr->
@@ -5850,25 +6062,17 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
IO_CACHE *file= &cache_data->cache_log;
Log_event_writer writer(file, cache_data);
- if (with_annotate && *with_annotate)
- {
- Annotate_rows_log_event anno(table->in_use, is_transactional, false);
- /* Annotate event should be written not more than once */
- *with_annotate= 0;
- if (unlikely((error= writer.write(&anno))))
- {
- if (my_errno == EFBIG)
- cache_data->set_incident();
- DBUG_RETURN(error);
- }
- }
+ if (with_annotate)
+ if (binlog_write_annotated_row(&writer))
+ DBUG_RETURN(1);
+
if (unlikely((error= writer.write(&the_event))))
DBUG_RETURN(error);
- binlog_table_maps++;
DBUG_RETURN(0);
}
+
/**
This function retrieves a pending row event from a cache which is
specified through the parameter @c is_transactional. Respectively, when it
@@ -6031,20 +6235,9 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
DBUG_ENTER("write_gtid_event");
DBUG_PRINT("enter", ("standalone: %d", standalone));
-#ifdef WITH_WSREP
- if (WSREP(thd) &&
- (wsrep_thd_trx_seqno(thd) > 0) &&
- wsrep_gtid_mode && !thd->variables.gtid_seq_no)
- {
- domain_id= wsrep_gtid_domain_id;
- } else {
-#endif /* WITH_WSREP */
+ seq_no= thd->variables.gtid_seq_no;
domain_id= thd->variables.gtid_domain_id;
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
local_server_id= thd->variables.server_id;
- seq_no= thd->variables.gtid_seq_no;
DBUG_ASSERT(local_server_id != 0);
@@ -6091,8 +6284,11 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
DBUG_ASSERT(this == &mysql_bin_log);
#ifdef WITH_WSREP
- if (wsrep_gtid_mode && is_gtid_cached(thd))
- DBUG_RETURN(false);
+ if (wsrep_gtid_mode)
+ {
+ thd->variables.gtid_domain_id= global_system_variables.gtid_domain_id;
+ thd->variables.server_id= global_system_variables.server_id;
+ }
#endif
if (write_event(&gtid_event))
@@ -6308,12 +6504,15 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
(WSREP(thd) && !(thd->variables.option_bits & OPTION_BIN_LOG))))
DBUG_RETURN(0);
- if (thd->variables.option_bits & OPTION_GTID_BEGIN)
+ if (thd->variables.option_bits &
+ (OPTION_GTID_BEGIN | OPTION_BIN_COMMIT_OFF))
{
DBUG_PRINT("info", ("OPTION_GTID_BEGIN was set"));
/* Wait for commit from binary log before we commit */
direct= 0;
using_trans= 1;
+ /* Set cache_type to ensure we don't get checksums for this event */
+ event_info->cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
}
if (thd->binlog_evt_union.do_union)
@@ -6369,7 +6568,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
nodes. A check has been added to stop them from getting logged into
binary log files.
*/
- if (WSREP(thd)) option_bin_log_flag= true;
+ if (WSREP(thd))
+ option_bin_log_flag= true;
if ((!(option_bin_log_flag)) ||
(thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT &&
@@ -6389,7 +6589,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
DBUG_PRINT("info", ("direct is set"));
DBUG_ASSERT(!thd->backup_commit_lock);
- mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT, MDL_EXPLICIT);
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_EXPLICIT);
thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout);
thd->backup_commit_lock= &mdl_request;
@@ -7077,8 +7278,10 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
CacheWriter writer(thd, &log_file, binlog_checksum_options, &crypto);
if (crypto.scheme)
+ {
writer.ctx= alloca(crypto.ctx_size);
-
+ writer.set_encrypted_writer();
+ }
// while there is just one alg the following must hold:
DBUG_ASSERT(binlog_checksum_options == BINLOG_CHECKSUM_ALG_OFF ||
binlog_checksum_options == BINLOG_CHECKSUM_ALG_CRC32);
@@ -7413,10 +7616,10 @@ 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;
+ entry.need_unlog= is_preparing_xa(thd);
+ ha_info= all ? thd->transaction->all.ha_list : thd->transaction->stmt.ha_list;
- for (; ha_info; ha_info= ha_info->next())
+ for (; !entry.need_unlog && ha_info; ha_info= ha_info->next())
{
if (ha_info->is_started() && ha_info->ht() != binlog_hton &&
!ha_info->ht()->commit_checkpoint_request)
@@ -7795,7 +7998,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
Release commit order and if leader, wait for prior commit to
complete. This establishes total order for group leaders.
*/
- if (wsrep_ordered_commit(entry->thd, entry->all, wsrep_apply_error()))
+ if (wsrep_ordered_commit(entry->thd, entry->all))
{
entry->thd->wakeup_subsequent_commits(1);
return 1;
@@ -8016,7 +8219,9 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
We already checked before that at least one cache is non-empty; if both
are empty we would have skipped calling into here.
*/
- DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
+ DBUG_ASSERT(!cache_mngr->stmt_cache.empty() ||
+ !cache_mngr->trx_cache.empty() ||
+ current->thd->transaction->xid_state.is_explicit_XA());
if (unlikely((current->error= write_transaction_or_stmt(current,
commit_id))))
@@ -8025,7 +8230,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
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)
+ if ((cache_mngr->using_xa && cache_mngr->xa_xid) || current->need_unlog)
{
/*
If all storage engines support commit_checkpoint_request(), then we
@@ -8260,7 +8465,8 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry,
binlog_cache_mngr *mngr= entry->cache_mngr;
DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_or_stmt");
- if (write_gtid_event(entry->thd, false, entry->using_trx_cache, commit_id))
+ if (write_gtid_event(entry->thd, is_prepared_xa(entry->thd),
+ entry->using_trx_cache, commit_id))
DBUG_RETURN(ER_ERROR_ON_WRITE);
if (entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
@@ -8546,9 +8752,9 @@ void MYSQL_BIN_LOG::close(uint exiting)
if (log_state == LOG_OPENED)
{
+ DBUG_ASSERT(log_type == LOG_BIN);
#ifdef HAVE_REPLICATION
- if (log_type == LOG_BIN &&
- (exiting & LOG_CLOSE_STOP_EVENT))
+ if (exiting & LOG_CLOSE_STOP_EVENT)
{
Stop_log_event s;
// the checksumming rule for relay-log case is similar to Rotate
@@ -8585,8 +8791,7 @@ void MYSQL_BIN_LOG::close(uint exiting)
#endif /* HAVE_REPLICATION */
/* don't pwrite in a file opened with O_APPEND - it doesn't work */
- if (log_file.type == WRITE_CACHE && log_type == LOG_BIN
- && !(exiting & LOG_CLOSE_DELAYED_CLOSE))
+ if (log_file.type == WRITE_CACHE && !(exiting & LOG_CLOSE_DELAYED_CLOSE))
{
my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
if (!failed_to_save_state)
@@ -8966,7 +9171,7 @@ void
TC_LOG::run_prepare_ordered(THD *thd, bool all)
{
Ha_trx_info *ha_info=
- all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
+ all ? thd->transaction->all.ha_list : thd->transaction->stmt.ha_list;
mysql_mutex_assert_owner(&LOCK_prepare_ordered);
for (; ha_info; ha_info= ha_info->next())
@@ -8983,7 +9188,7 @@ void
TC_LOG::run_commit_ordered(THD *thd, bool all)
{
Ha_trx_info *ha_info=
- all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
+ all ? thd->transaction->all.ha_list : thd->transaction->stmt.ha_list;
mysql_mutex_assert_owner(&LOCK_commit_ordered);
for (; ha_info; ha_info= ha_info->next())
@@ -9209,7 +9414,8 @@ int TC_LOG_MMAP::open(const char *opt_name)
npages=(uint)file_length/tc_log_page_size;
if (npages < 3) // to guarantee non-empty pool
goto err;
- if (!(pages=(PAGE *)my_malloc(npages*sizeof(PAGE), MYF(MY_WME|MY_ZEROFILL))))
+ if (!(pages=(PAGE *)my_malloc(key_memory_TC_LOG_MMAP_pages,
+ npages*sizeof(PAGE), MYF(MY_WME|MY_ZEROFILL))))
goto err;
inited=3;
for (pg=pages, i=0; i < npages; i++, pg++)
@@ -9519,7 +9725,8 @@ int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
{
uint32 size= sizeof(*pending_checkpoint) + sizeof(ulong) * (ncookies - 1);
if (!(pending_checkpoint=
- (pending_cookies *)my_malloc(size, MYF(MY_ZEROFILL))))
+ (pending_cookies *)my_malloc(PSI_INSTRUMENT_ME, size,
+ MYF(MY_ZEROFILL))))
{
my_error(ER_OUTOFMEMORY, MYF(0), size);
mysql_mutex_unlock(&LOCK_pending_checkpoint);
@@ -9658,8 +9865,8 @@ int TC_LOG_MMAP::recover()
goto err1;
}
- if (my_hash_init(&xids, &my_charset_bin, tc_log_page_size/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ if (my_hash_init(PSI_INSTRUMENT_ME, &xids, &my_charset_bin,
+ tc_log_page_size/3, 0, sizeof(my_xid), 0, 0, MYF(0)))
goto err1;
for ( ; p < end_p ; p++)
@@ -9737,7 +9944,7 @@ int TC_LOG_BINLOG::open(const char *opt_name)
{
mysql_mutex_lock(&LOCK_log);
/* generate a new binlog to mask a corrupted one */
- open(opt_name, LOG_BIN, 0, 0, WRITE_CACHE, max_binlog_size, 0, TRUE);
+ open(opt_name, 0, 0, WRITE_CACHE, max_binlog_size, 0, TRUE);
mysql_mutex_unlock(&LOCK_log);
cleanup();
return 1;
@@ -9963,6 +10170,63 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+static bool write_empty_xa_prepare(THD *thd, binlog_cache_mngr *cache_mngr)
+{
+ return binlog_commit_flush_xa_prepare(thd, true, cache_mngr);
+}
+
+int TC_LOG_BINLOG::unlog_xa_prepare(THD *thd, bool all)
+{
+ DBUG_ASSERT(is_preparing_xa(thd));
+
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+ int cookie= 0;
+
+ if (!cache_mngr->need_unlog)
+ {
+ Ha_trx_info *ha_info;
+ uint rw_count= ha_count_rw_all(thd, &ha_info);
+ bool rc= false;
+
+#ifndef DBUG_OFF
+ if (rw_count > 1)
+ {
+ /*
+ There must be no binlog_hton used in a transaction consisting of more
+ than 1 engine, *when* (at this point) this transaction has not been
+ binlogged. The one exception is if there is an engine without a
+ prepare method, as in this case the engine doesn't support XA and
+ we have to ignore this check.
+ */
+ bool binlog= false, exist_hton_without_prepare= false;
+ for (ha_info= thd->transaction->all.ha_list; ha_info;
+ ha_info= ha_info->next())
+ {
+ if (ha_info->ht() == binlog_hton)
+ binlog= true;
+ if (!ha_info->ht()->prepare)
+ exist_hton_without_prepare= true;
+ }
+ DBUG_ASSERT(!binlog || exist_hton_without_prepare);
+ }
+#endif
+ if (rw_count > 0)
+ {
+ /* an empty XA-prepare event group is logged */
+ rc= write_empty_xa_prepare(thd, cache_mngr); // normally gains need_unlog
+ trans_register_ha(thd, true, binlog_hton, 0); // do it for future commmit
+ }
+ if (rw_count == 0 || !cache_mngr->need_unlog)
+ return rc;
+ }
+
+ cookie= BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, cache_mngr->delayed_error);
+ cache_mngr->need_unlog= false;
+
+ return unlog(cookie, 1);
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
@@ -10172,13 +10436,13 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
#endif
if (! fdle->is_valid() ||
- (do_xa && my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
+ (do_xa && my_hash_init(key_memory_binlog_recover_exec, &xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
sizeof(my_xid), 0, 0, MYF(0))))
goto err1;
if (do_xa)
- init_alloc_root(&mem_root, "TC_LOG_BINLOG", TC_LOG_PAGE_SIZE,
- TC_LOG_PAGE_SIZE, MYF(0));
+ init_alloc_root(key_memory_binlog_recover_exec, &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
@@ -10279,6 +10543,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
((last_gtid_standalone && !ev->is_part_of_group(typ)) ||
(!last_gtid_standalone &&
(typ == XID_EVENT ||
+ typ == XA_PREPARE_LOG_EVENT ||
(LOG_EVENT_IS_QUERY(typ) &&
(((Query_log_event *)ev)->is_commit() ||
((Query_log_event *)ev)->is_rollback()))))))
@@ -10484,24 +10749,6 @@ MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
#ifdef INNODB_COMPATIBILITY_HOOKS
-/**
- Get the file name of the MySQL binlog.
- @return the name of the binlog file
-*/
-extern "C"
-const char* mysql_bin_log_file_name(void)
-{
- return mysql_bin_log.get_log_fname();
-}
-/**
- Get the current position of the MySQL binlog.
- @return byte offset from the beginning of the binlog
-*/
-extern "C"
-ulonglong mysql_bin_log_file_pos(void)
-{
- return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file;
-}
/*
Get the current position of the MySQL binlog for transaction currently being
committed.
@@ -10609,7 +10856,7 @@ static struct st_mysql_sys_var *binlog_sys_vars[]=
/*
Copy out the non-directory part of binlog position filename for the
`binlog_snapshot_file' status variable, same way as it is done for
- SHOW MASTER STATUS.
+ SHOW BINLOG STATUS.
*/
static void
set_binlog_snapshot_file(const char *src)
@@ -10780,7 +11027,7 @@ void wsrep_thd_binlog_trx_reset(THD * thd)
cache_mngr->stmt_cache.reset();
}
}
- thd->clear_binlog_table_maps();
+ thd->reset_binlog_for_next_statement();
DBUG_VOID_RETURN;
}
@@ -10807,7 +11054,6 @@ bool wsrep_stmt_rollback_is_safe(THD* thd)
binlog_cache_mngr *cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
-
if (binlog_hton && cache_mngr)
{
binlog_cache_data * trx_cache = &cache_mngr->trx_cache;
@@ -10862,8 +11108,8 @@ void wsrep_register_binlog_handler(THD *thd, bool trx)
Set callbacks in order to be able to call commmit or rollback.
*/
if (trx)
- trans_register_ha(thd, TRUE, binlog_hton);
- trans_register_ha(thd, FALSE, binlog_hton);
+ trans_register_ha(thd, TRUE, binlog_hton, 0);
+ trans_register_ha(thd, FALSE, binlog_hton, 0);
/*
Set the binary log as read/write otherwise callbacks are not called.
diff --git a/sql/log.h b/sql/log.h
index 4b80bdfd81f..58e681985eb 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -61,6 +61,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int unlog_xa_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -115,6 +116,10 @@ public:
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int unlog_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -198,6 +203,10 @@ class TC_LOG_MMAP: public TC_LOG
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 unlog_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -286,6 +295,12 @@ enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN };
enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED };
/*
+ Use larger buffers when reading from and to binary log
+ We make it one step smaller than 64K to account for malloc overhead.
+*/
+#define LOG_BIN_IO_SIZE MY_ALIGN_DOWN(65536-1, IO_SIZE)
+
+/*
TODO use mmap instead of IO_CACHE for binlog
(mmap+fsync is two times faster than write+fsync)
*/
@@ -305,13 +320,6 @@ public:
enum_log_type log_type,
const char *new_name, ulong next_file_number,
enum cache_type io_cache_type_arg);
- bool init_and_set_log_file_name(const char *log_name,
- const char *new_name,
- ulong next_log_number,
- enum_log_type log_type_arg,
- enum cache_type io_cache_type_arg);
- void init(enum_log_type log_type_arg,
- enum cache_type io_cache_type_arg);
void close(uint exiting);
inline bool is_open() { return log_state != LOG_CLOSED; }
const char *generate_name(const char *log_name,
@@ -335,7 +343,12 @@ public:
/** Instrumentation key to use for file io in @c log_file */
PSI_file_key m_log_file_key;
#endif
- /* for documentation of mutexes held in various places in code */
+
+ bool init_and_set_log_file_name(const char *log_name,
+ const char *new_name,
+ ulong next_log_number,
+ enum_log_type log_type_arg,
+ enum cache_type io_cache_type_arg);
};
/* Tell the io thread if we can delay the master info sync. */
@@ -414,8 +427,6 @@ struct wait_for_commit;
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
{
- private:
-#ifdef HAVE_PSI_INTERFACE
/** The instrumentation key to use for @ LOCK_index. */
PSI_mutex_key m_key_LOCK_index;
/** The instrumentation key to use for @ COND_relay_log_updated */
@@ -423,14 +434,13 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
/** The instrumentation key to use for @ COND_bin_log_updated */
PSI_cond_key m_key_bin_log_update;
/** The instrumentation key to use for opening the log file. */
- PSI_file_key m_key_file_log;
+ PSI_file_key m_key_file_log, m_key_file_log_cache;
/** The instrumentation key to use for opening the log index file. */
- PSI_file_key m_key_file_log_index;
+ PSI_file_key m_key_file_log_index, m_key_file_log_index_cache;
- PSI_file_key m_key_COND_queue_busy;
+ PSI_cond_key m_key_COND_queue_busy;
/** The instrumentation key to use for LOCK_binlog_end_pos. */
PSI_mutex_key m_key_LOCK_binlog_end_pos;
-#endif
struct group_commit_entry
{
@@ -564,13 +574,6 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
bool write_transaction_to_binlog_events(group_commit_entry *entry);
void trx_group_commit_leader(group_commit_entry *leader);
bool is_xidlist_idle_nolock();
-#ifdef WITH_WSREP
- /*
- When this mariadb node is slave and galera enabled. So in this case
- we write the gtid in wsrep_run_commit itself.
- */
- inline bool is_gtid_cached(THD *thd);
-#endif
public:
/*
A list of struct xid_count_per_binlog is used to keep track of how many
@@ -597,7 +600,7 @@ public:
:binlog_id(0), xid_count(0), notify_count(0)
{
binlog_name_len= log_file_name_len;
- binlog_name= (char *) my_malloc(binlog_name_len, MYF(MY_ZEROFILL));
+ binlog_name= (char *) my_malloc(PSI_INSTRUMENT_ME, binlog_name_len, MYF(MY_ZEROFILL));
if (binlog_name)
memcpy(binlog_name, log_file_name, binlog_name_len);
}
@@ -684,15 +687,19 @@ public:
PSI_cond_key key_relay_log_update,
PSI_cond_key key_bin_log_update,
PSI_file_key key_file_log,
+ PSI_file_key key_file_log_cache,
PSI_file_key key_file_log_index,
- PSI_file_key key_COND_queue_busy,
+ PSI_file_key key_file_log_index_cache,
+ PSI_cond_key key_COND_queue_busy,
PSI_mutex_key key_LOCK_binlog_end_pos)
{
m_key_LOCK_index= key_LOCK_index;
m_key_relay_log_update= key_relay_log_update;
m_key_bin_log_update= key_bin_log_update;
m_key_file_log= key_file_log;
+ m_key_file_log_cache= key_file_log_cache;
m_key_file_log_index= key_file_log_index;
+ m_key_file_log_index_cache= key_file_log_index_cache;
m_key_COND_queue_busy= key_COND_queue_busy;
m_key_LOCK_binlog_end_pos= key_LOCK_binlog_end_pos;
}
@@ -705,6 +712,7 @@ 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 unlog_xa_prepare(THD *thd, bool all);
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);
@@ -787,7 +795,6 @@ public:
void init_pthread_objects();
void cleanup();
bool open(const char *log_name,
- enum_log_type log_type,
const char *new_name,
ulong next_log_number,
enum cache_type io_cache_type_arg,
@@ -1143,6 +1150,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
void make_default_log_name(char **out, const char* log_ext, bool once);
void binlog_reset_cache(THD *thd);
+bool write_annotated_row(THD *thd);
extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
extern handlerton *binlog_hton;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index b06336669f3..0388a2b19b1 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2000, 2019, Oracle and/or its affiliates.
+ Copyright (c) 2000, 2018, Oracle and/or its affiliates.
Copyright (c) 2009, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
#include "mariadb.h"
#include "sql_priv.h"
-
+#include "handler.h"
#ifndef MYSQL_CLIENT
#include "unireg.h"
#include "log_event.h"
@@ -39,7 +39,6 @@
#include "transaction.h"
#include <my_dir.h>
#include "sql_show.h" // append_identifier
-#include "debug_sync.h" // debug_sync
#include <mysql/psi/mysql_statement.h>
#include <strfunc.h>
#include "compat56.h"
@@ -57,6 +56,10 @@
#define my_b_write_string(A, B) my_b_write((A), (uchar*)(B), (uint) (sizeof(B) - 1))
+PSI_memory_key key_memory_log_event;
+PSI_memory_key key_memory_Incident_log_event_message;
+PSI_memory_key key_memory_Rows_query_log_event_rows_query;
+
/**
BINLOG_CHECKSUM variable.
*/
@@ -80,9 +83,6 @@ TYPELIB binlog_checksum_typelib=
};
-
-#define log_cs &my_charset_latin1
-
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
/*
@@ -107,179 +107,6 @@ const Version checksum_version_split_mariadb(5, 3, 0);
// First MySQL version with fraction seconds
const Version fsp_version_split_mysql(5, 6, 0);
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd);
-
-static const char *HA_ERR(int i)
-{
- /*
- This function should only be called in case of an error
- was detected
- */
- DBUG_ASSERT(i != 0);
- switch (i) {
- case HA_ERR_KEY_NOT_FOUND: return "HA_ERR_KEY_NOT_FOUND";
- case HA_ERR_FOUND_DUPP_KEY: return "HA_ERR_FOUND_DUPP_KEY";
- case HA_ERR_RECORD_CHANGED: return "HA_ERR_RECORD_CHANGED";
- case HA_ERR_WRONG_INDEX: return "HA_ERR_WRONG_INDEX";
- case HA_ERR_CRASHED: return "HA_ERR_CRASHED";
- case HA_ERR_WRONG_IN_RECORD: return "HA_ERR_WRONG_IN_RECORD";
- case HA_ERR_OUT_OF_MEM: return "HA_ERR_OUT_OF_MEM";
- case HA_ERR_NOT_A_TABLE: return "HA_ERR_NOT_A_TABLE";
- case HA_ERR_WRONG_COMMAND: return "HA_ERR_WRONG_COMMAND";
- case HA_ERR_OLD_FILE: return "HA_ERR_OLD_FILE";
- case HA_ERR_NO_ACTIVE_RECORD: return "HA_ERR_NO_ACTIVE_RECORD";
- case HA_ERR_RECORD_DELETED: return "HA_ERR_RECORD_DELETED";
- case HA_ERR_RECORD_FILE_FULL: return "HA_ERR_RECORD_FILE_FULL";
- case HA_ERR_INDEX_FILE_FULL: return "HA_ERR_INDEX_FILE_FULL";
- case HA_ERR_END_OF_FILE: return "HA_ERR_END_OF_FILE";
- case HA_ERR_UNSUPPORTED: return "HA_ERR_UNSUPPORTED";
- case HA_ERR_TO_BIG_ROW: return "HA_ERR_TO_BIG_ROW";
- case HA_WRONG_CREATE_OPTION: return "HA_WRONG_CREATE_OPTION";
- case HA_ERR_FOUND_DUPP_UNIQUE: return "HA_ERR_FOUND_DUPP_UNIQUE";
- case HA_ERR_UNKNOWN_CHARSET: return "HA_ERR_UNKNOWN_CHARSET";
- case HA_ERR_WRONG_MRG_TABLE_DEF: return "HA_ERR_WRONG_MRG_TABLE_DEF";
- case HA_ERR_CRASHED_ON_REPAIR: return "HA_ERR_CRASHED_ON_REPAIR";
- case HA_ERR_CRASHED_ON_USAGE: return "HA_ERR_CRASHED_ON_USAGE";
- case HA_ERR_LOCK_WAIT_TIMEOUT: return "HA_ERR_LOCK_WAIT_TIMEOUT";
- case HA_ERR_LOCK_TABLE_FULL: return "HA_ERR_LOCK_TABLE_FULL";
- case HA_ERR_READ_ONLY_TRANSACTION: return "HA_ERR_READ_ONLY_TRANSACTION";
- case HA_ERR_LOCK_DEADLOCK: return "HA_ERR_LOCK_DEADLOCK";
- case HA_ERR_CANNOT_ADD_FOREIGN: return "HA_ERR_CANNOT_ADD_FOREIGN";
- case HA_ERR_NO_REFERENCED_ROW: return "HA_ERR_NO_REFERENCED_ROW";
- case HA_ERR_ROW_IS_REFERENCED: return "HA_ERR_ROW_IS_REFERENCED";
- case HA_ERR_NO_SAVEPOINT: return "HA_ERR_NO_SAVEPOINT";
- case HA_ERR_NON_UNIQUE_BLOCK_SIZE: return "HA_ERR_NON_UNIQUE_BLOCK_SIZE";
- case HA_ERR_NO_SUCH_TABLE: return "HA_ERR_NO_SUCH_TABLE";
- case HA_ERR_TABLE_EXIST: return "HA_ERR_TABLE_EXIST";
- case HA_ERR_NO_CONNECTION: return "HA_ERR_NO_CONNECTION";
- case HA_ERR_NULL_IN_SPATIAL: return "HA_ERR_NULL_IN_SPATIAL";
- case HA_ERR_TABLE_DEF_CHANGED: return "HA_ERR_TABLE_DEF_CHANGED";
- case HA_ERR_NO_PARTITION_FOUND: return "HA_ERR_NO_PARTITION_FOUND";
- case HA_ERR_RBR_LOGGING_FAILED: return "HA_ERR_RBR_LOGGING_FAILED";
- case HA_ERR_DROP_INDEX_FK: return "HA_ERR_DROP_INDEX_FK";
- case HA_ERR_FOREIGN_DUPLICATE_KEY: return "HA_ERR_FOREIGN_DUPLICATE_KEY";
- case HA_ERR_TABLE_NEEDS_UPGRADE: return "HA_ERR_TABLE_NEEDS_UPGRADE";
- case HA_ERR_TABLE_READONLY: return "HA_ERR_TABLE_READONLY";
- case HA_ERR_AUTOINC_READ_FAILED: return "HA_ERR_AUTOINC_READ_FAILED";
- case HA_ERR_AUTOINC_ERANGE: return "HA_ERR_AUTOINC_ERANGE";
- case HA_ERR_GENERIC: return "HA_ERR_GENERIC";
- case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME";
- case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE";
- case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT";
- case HA_ERR_ROWS_EVENT_APPLY : return "HA_ERR_ROWS_EVENT_APPLY";
- }
- 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->speculation == rpl_group_info::SPECULATE_OPTIMISTIC)
- return true;
- 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
-
- @param level error, warning or info
- @param ha_error HA_ERR_ code
- @param rli pointer to the active Relay_log_info instance
- @param thd pointer to the slave thread's thd
- @param table pointer to the event's table object
- @param type the type of the event
- @param log_name the master binlog file name
- @param pos the master binlog file pos (the next after the event)
-
-*/
-static void inline slave_rows_error_report(enum loglevel level, int ha_error,
- rpl_group_info *rgi, THD *thd,
- TABLE *table, const char * type,
- const char *log_name, my_off_t pos)
-{
- const char *handler_error= (ha_error ? HA_ERR(ha_error) : NULL);
- char buff[MAX_SLAVE_ERRMSG], *slider;
- const char *buff_end= buff + sizeof(buff);
- size_t len;
- 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++)
- {
- len= my_snprintf(slider, buff_end - slider,
- " %s, Error_code: %d;", err->get_message_text(),
- err->get_sql_errno());
- }
-
- if (ha_error != 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 %llu",
- type, table->s->db.str, table->s->table_name.str,
- buff, handler_error == NULL ? "<unknown>" : handler_error,
- log_name, pos);
- else
- 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 %llu",
- type, table->s->db.str, table->s->table_name.str,
- buff, log_name, pos);
-}
-#endif
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-static void set_thd_db(THD *thd, Rpl_filter *rpl_filter,
- const char *db, uint32 db_len)
-{
- char lcase_db_buf[NAME_LEN +1];
- LEX_CSTRING new_db;
- new_db.length= db_len;
- if (lower_case_table_names == 1)
- {
- strmov(lcase_db_buf, db);
- my_casedn_str(system_charset_info, lcase_db_buf);
- new_db.str= lcase_db_buf;
- }
- else
- new_db.str= db;
- /* TODO WARNING this makes rewrite_db respect lower_case_table_names values
- * for more info look MDEV-17446 */
- new_db.str= rpl_filter->get_rewrite_db(new_db.str, &new_db.length);
- thd->set_db(&new_db);
-}
-#endif
/*
Cache that will automatically be written to a dedicated file on
destruction.
@@ -292,7 +119,7 @@ class Write_on_release_cache
public:
enum flag
{
- FLUSH_F= 1
+ FLUSH_F
};
typedef unsigned short flag_set;
@@ -383,187 +210,6 @@ private:
Log_event *m_ev; // Used for Flashback
};
-/*
- pretty_print_str()
-*/
-
-#ifdef MYSQL_CLIENT
-static bool pretty_print_str(IO_CACHE* cache, const char* str, int len)
-{
- const char* end = str + len;
- if (my_b_write_byte(cache, '\''))
- goto err;
-
- while (str < end)
- {
- char c;
- int error;
-
- switch ((c=*str++)) {
- case '\n': error= my_b_write(cache, (uchar*)"\\n", 2); break;
- case '\r': error= my_b_write(cache, (uchar*)"\\r", 2); break;
- case '\\': error= my_b_write(cache, (uchar*)"\\\\", 2); break;
- case '\b': error= my_b_write(cache, (uchar*)"\\b", 2); break;
- case '\t': error= my_b_write(cache, (uchar*)"\\t", 2); break;
- case '\'': error= my_b_write(cache, (uchar*)"\\'", 2); break;
- case 0 : error= my_b_write(cache, (uchar*)"\\0", 2); break;
- default:
- error= my_b_write_byte(cache, c);
- break;
- }
- if (unlikely(error))
- goto err;
- }
- return my_b_write_byte(cache, '\'');
-
-err:
- return 1;
-}
-#endif /* MYSQL_CLIENT */
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-inline int idempotent_error_code(int err_code)
-{
- int ret= 0;
-
- switch (err_code)
- {
- case 0:
- ret= 1;
- break;
- /*
- The following list of "idempotent" errors
- means that an error from the list might happen
- because of idempotent (more than once)
- applying of a binlog file.
- Notice, that binlog has a ddl operation its
- second applying may cause
-
- case HA_ERR_TABLE_DEF_CHANGED:
- case HA_ERR_CANNOT_ADD_FOREIGN:
-
- which are not included into to the list.
-
- Note that HA_ERR_RECORD_DELETED is not in the list since
- do_exec_row() should not return that error code.
- */
- case HA_ERR_RECORD_CHANGED:
- case HA_ERR_KEY_NOT_FOUND:
- case HA_ERR_END_OF_FILE:
- case HA_ERR_FOUND_DUPP_KEY:
- case HA_ERR_FOUND_DUPP_UNIQUE:
- case HA_ERR_FOREIGN_DUPLICATE_KEY:
- case HA_ERR_NO_REFERENCED_ROW:
- case HA_ERR_ROW_IS_REFERENCED:
- ret= 1;
- break;
- default:
- ret= 0;
- break;
- }
- return (ret);
-}
-
-/**
- Ignore error code specified on command line.
-*/
-
-inline int ignored_error_code(int err_code)
-{
- if (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code))
- {
- statistic_increment(slave_skipped_errors, LOCK_status);
- return 1;
- }
- return err_code == ER_SLAVE_IGNORED_TABLE;
-}
-
-/*
- This function converts an engine's error to a server error.
-
- If the thread does not have an error already reported, it tries to
- define it by calling the engine's method print_error. However, if a
- mapping is not found, it uses the ER_UNKNOWN_ERROR and prints out a
- warning message.
-*/
-int convert_handler_error(int error, THD* thd, TABLE *table)
-{
- 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->get_stmt_da()->sql_errno() :
- ER_UNKNOWN_ERROR);
- if (actual_error == ER_UNKNOWN_ERROR)
- if (global_system_variables.log_warnings)
- sql_print_warning("Unknown error detected %d in handler", error);
- }
-
- return (actual_error);
-}
-
-inline bool concurrency_error_code(int error)
-{
- switch (error)
- {
- case ER_LOCK_WAIT_TIMEOUT:
- case ER_LOCK_DEADLOCK:
- case ER_XA_RBDEADLOCK:
- return TRUE;
- default:
- return (FALSE);
- }
-}
-
-inline bool unexpected_error_code(int unexpected_error)
-{
- switch (unexpected_error)
- {
- case ER_NET_READ_ERROR:
- case ER_NET_ERROR_ON_WRITE:
- case ER_QUERY_INTERRUPTED:
- case ER_STATEMENT_TIMEOUT:
- case ER_CONNECTION_KILLED:
- case ER_SERVER_SHUTDOWN:
- case ER_NEW_ABORTING_CONNECTION:
- return(TRUE);
- default:
- return(FALSE);
- }
-}
-
-/*
- pretty_print_str()
-*/
-
-static void
-pretty_print_str(String *packet, const char *str, int len)
-{
- const char *end= str + len;
- packet->append(STRING_WITH_LEN("'"));
- while (str < end)
- {
- char c;
- switch ((c=*str++)) {
- case '\n': packet->append(STRING_WITH_LEN("\\n")); break;
- case '\r': packet->append(STRING_WITH_LEN("\\r")); break;
- case '\\': packet->append(STRING_WITH_LEN("\\\\")); break;
- case '\b': packet->append(STRING_WITH_LEN("\\b")); break;
- case '\t': packet->append(STRING_WITH_LEN("\\t")); break;
- case '\'': packet->append(STRING_WITH_LEN("\\'")); break;
- case 0 : packet->append(STRING_WITH_LEN("\\0")); break;
- default:
- packet->append(&c, 1);
- break;
- }
- }
- packet->append(STRING_WITH_LEN("'"));
-}
-#endif /* !MYSQL_CLIENT */
-
#ifndef DBUG_OFF
#define DBUG_DUMP_EVENT_BUF(B,L) \
do { \
@@ -587,131 +233,6 @@ pretty_print_str(String *packet, const char *str, int len)
#define DBUG_DUMP_EVENT_BUF(B,L) do { } while(0)
#endif
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-/**
- 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_CSTRING *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 ext Extension for file name
-
- @return
- Pointer to start of extension
-*/
-
-static char *slave_load_file_stem(char *buf, uint file_id,
- int event_server_id, const char *ext,
- LEX_CSTRING *connection_name)
-{
- char *res;
- res= buf+ unpack_dirname(buf, slave_load_tmpdir);
- to_unix_path(buf);
- 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
- return res; // Pointer to extension
-}
-#endif
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-/**
- Delete all temporary files used for SQL_LOAD.
-*/
-
-static void cleanup_load_tmpdir(LEX_CSTRING *connection_name)
-{
- MY_DIR *dirp;
- FILEINFO *file;
- uint i;
- char dir[FN_REFLEN], fname[FN_REFLEN];
- char prefbuf[31 + MAX_CONNECTION_NAME* MAX_FILENAME_MBWIDTH + 1];
- DBUG_ENTER("cleanup_load_tmpdir");
-
- unpack_dirname(dir, slave_load_tmpdir);
- if (!(dirp=my_dir(dir, MYF(MY_WME))))
- return;
-
- /*
- When we are deleting temporary files, we should only remove
- the files associated with the server id of our server.
- We don't use event_server_id here because since we've disabled
- direct binlogging of Create_file/Append_file/Exec_load events
- we cannot meet Start_log event in the middle of events from one
- LOAD DATA.
- */
-
- 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))
- {
- fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
- mysql_file_delete(key_file_misc, fname, MYF(0));
- }
- }
-
- my_dirend(dirp);
- DBUG_VOID_RETURN;
-}
-#endif
-
-
/*
read_str()
*/
@@ -893,7 +414,7 @@ query_event_uncompress(const Format_description_log_event *description_event,
}
else
{
- new_dst = (char *)my_malloc(alloc_size, MYF(MY_WME));
+ new_dst = (char *)my_malloc(PSI_INSTRUMENT_ME, alloc_size, MYF(MY_WME));
if (!new_dst)
return 1;
@@ -1015,7 +536,7 @@ row_log_event_uncompress(const Format_description_log_event *description_event,
}
else
{
- new_dst = (char *)my_malloc(alloc_size, MYF(MY_WME));
+ new_dst = (char *)my_malloc(PSI_INSTRUMENT_ME, alloc_size, MYF(MY_WME));
if (!new_dst)
return 1;
@@ -1126,80 +647,6 @@ int binlog_buf_uncompress(const char *src, char *dst, uint32 len,
return 0;
}
-#ifndef MYSQL_CLIENT
-
-/**
- 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(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 + 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, str, len);
- else
- {
- *ptr++= '\'';
- if (!no_backslash)
- {
- ptr+= escape_string_for_mysql(csinfo, ptr, 0, str, len);
- }
- else
- {
- const char *frm_str= str;
-
- for (; frm_str < (str + len); frm_str++)
- {
- /* Using '' way to represent "'" */
- if (*frm_str == '\'')
- *ptr++= *frm_str;
-
- *ptr++= *frm_str;
- }
- }
-
- *ptr++= '\'';
- }
- to->length((uint32)(orig_len + ptr - beg));
- return 0;
-}
-#endif
-
-
-/**
- Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
- commands just before it prints a query.
-*/
-
-#ifdef MYSQL_CLIENT
-
-static bool print_set_option(IO_CACHE* file, uint32 bits_changed,
- uint32 option, uint32 flags, const char* name,
- bool* need_comma)
-{
- if (bits_changed & option)
- {
- if (*need_comma)
- if (my_b_write(file, (uchar*)", ", 2))
- goto err;
- if (my_b_printf(file, "%s=%d", name, MY_TEST(flags & option)))
- goto err;
- *need_comma= 1;
- }
- return 0;
-err:
- return 1;
-}
-#endif
/**************************************************************************
Log_event methods (= the parent class of all events)
@@ -1280,51 +727,6 @@ const char* Log_event::get_type_str()
Log_event::Log_event()
*/
-#ifndef MYSQL_CLIENT
-Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
- :log_pos(0), temp_buf(0), exec_time(0), thd(thd_arg),
- checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
-{
- server_id= thd->variables.server_id;
- when= thd->start_time;
- when_sec_part=thd->start_time_sec_part;
-
- if (using_trans)
- cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
- else
- cache_type= Log_event::EVENT_STMT_CACHE;
- flags= flags_arg |
- (thd->variables.option_bits & OPTION_SKIP_REPLICATION ?
- LOG_EVENT_SKIP_REPLICATION_F : 0);
-}
-
-/**
- This minimal constructor is for when you are not even sure that there
- is a valid THD. For example in the server when we are shutting down or
- flushing logs after receiving a SIGHUP (then we must write a Rotate to
- the binlog but we have no THD, so we need this minimal constructor).
-*/
-
-Log_event::Log_event()
- :temp_buf(0), exec_time(0), flags(0), cache_type(EVENT_INVALID_CACHE),
- thd(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
-{
- 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
- */
- when= 0;
- when_sec_part=0;
- log_pos= 0;
-}
-#endif /* !MYSQL_CLIENT */
-
-
-/*
- Log_event::Log_event()
-*/
-
Log_event::Log_event(const char* buf,
const Format_description_log_event* description_event)
:temp_buf(0), exec_time(0), cache_type(Log_event::EVENT_INVALID_CACHE),
@@ -1395,395 +797,6 @@ Log_event::Log_event(const char* buf,
/* otherwise, go on with reading the header from buf (nothing now) */
}
-#ifndef MYSQL_CLIENT
-#ifdef HAVE_REPLICATION
-
-int Log_event::do_update_pos(rpl_group_info *rgi)
-{
- Relay_log_info *rli= rgi->rli;
- DBUG_ENTER("Log_event::do_update_pos");
-
- DBUG_ASSERT(!rli->belongs_to_client());
- /*
- 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
- Execute_load_log_event::do_apply_event. In this case, we don't
- do anything here ; Execute_load_log_event::do_apply_event will
- call Log_event::do_apply_event again later with the proper rli.
- Strictly speaking, if we were sure that rli is null only in the
- case discussed above, 'if (rli)' is useless here. But as we are
- not 100% sure, keep it for now.
-
- Matz: I don't think we will need this check with this refactoring.
- */
- if (rli)
- {
- /*
- 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.
- */
- if (!rgi->is_parallel_exec || is_group_event(get_type_code()))
- rli->stmt_done(log_pos, thd, rgi);
- }
- DBUG_RETURN(0); // Cannot fail currently
-}
-
-
-Log_event::enum_skip_reason
-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 == 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))
- return EVENT_SKIP_IGNORE;
- if (rli->slave_skip_counter > 0)
- return EVENT_SKIP_COUNT;
- return EVENT_SKIP_NOT;
-}
-
-
-/*
- Log_event::pack_info()
-*/
-
-void Log_event::pack_info(Protocol *protocol)
-{
- protocol->store("", &my_charset_bin);
-}
-
-
-/**
- Only called by SHOW BINLOG EVENTS
-*/
-int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
-{
- const char *p= strrchr(log_name, FN_LIBCHAR);
- const char *event_type;
- if (p)
- log_name = p + 1;
-
- protocol->prepare_for_resend();
- protocol->store(log_name, &my_charset_bin);
- protocol->store((ulonglong) pos);
- event_type = get_type_str();
- protocol->store(event_type, strlen(event_type), &my_charset_bin);
- protocol->store((uint32) server_id);
- protocol->store((ulonglong) log_pos);
- pack_info(protocol);
- return protocol->write();
-}
-#endif /* HAVE_REPLICATION */
-
-
-/**
- init_show_field_list() prepares the column names and types for the
- output of SHOW BINLOG EVENTS; it is used only by SHOW BINLOG
- EVENTS.
-*/
-
-void Log_event::init_show_field_list(THD *thd, List<Item>* field_list)
-{
- MEM_ROOT *mem_root= thd->mem_root;
- field_list->push_back(new (mem_root)
- Item_empty_string(thd, "Log_name", 20),
- mem_root);
- field_list->push_back(new (mem_root)
- Item_return_int(thd, "Pos",
- MY_INT64_NUM_DECIMAL_DIGITS,
- MYSQL_TYPE_LONGLONG),
- mem_root);
- field_list->push_back(new (mem_root)
- Item_empty_string(thd, "Event_type", 20),
- mem_root);
- field_list->push_back(new (mem_root)
- Item_return_int(thd, "Server_id", 10,
- MYSQL_TYPE_LONG),
- mem_root);
- field_list->push_back(new (mem_root)
- Item_return_int(thd, "End_log_pos",
- MY_INT64_NUM_DECIMAL_DIGITS,
- MYSQL_TYPE_LONGLONG),
- mem_root);
- field_list->push_back(new (mem_root) Item_empty_string(thd, "Info", 20),
- mem_root);
-}
-
-/**
- A decider of whether to trigger checksum computation or not.
- To be invoked in Log_event::write() stack.
- The decision is positive
-
- S,M) if it's been marked for checksumming with @c checksum_alg
-
- M) otherwise, if @@global.binlog_checksum is not NONE and the event is
- directly written to the binlog file.
- The to-be-cached event decides at @c write_cache() time.
-
- Otherwise the decision is negative.
-
- @note A side effect of the method is altering Log_event::checksum_alg
- it the latter was undefined at calling.
-
- @return true (positive) or false (negative)
-*/
-my_bool Log_event::need_checksum()
-{
- DBUG_ENTER("Log_event::need_checksum");
- my_bool ret;
- /*
- few callers of Log_event::write
- (incl FD::write, FD constructing code on the slave side, Rotate relay log
- and Stop event)
- provides their checksum alg preference through Log_event::checksum_alg.
- */
- if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
- ret= checksum_alg != BINLOG_CHECKSUM_ALG_OFF;
- else
- {
- ret= binlog_checksum_options && cache_type == Log_event::EVENT_NO_CACHE;
- checksum_alg= ret ? (enum_binlog_checksum_alg)binlog_checksum_options
- : 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
- call (and therefore data_written is not zero) then `ret' must be
- TRUE. It may not be null because FD is always checksummed.
- */
-
- DBUG_ASSERT(get_type_code() != FORMAT_DESCRIPTION_EVENT || ret ||
- data_written == 0);
-
- DBUG_ASSERT(!ret ||
- ((checksum_alg == binlog_checksum_options ||
- /*
- Stop event closes the relay-log and its checksum alg
- preference is set by the caller can be different
- from the server's binlog_checksum_options.
- */
- get_type_code() == STOP_EVENT ||
- /*
- Rotate:s can be checksummed regardless of the server's
- binlog_checksum_options. That applies to both
- the local RL's Rotate and the master's Rotate
- which IO thread instantiates via queue_binlog_ver_3_event.
- */
- get_type_code() == ROTATE_EVENT ||
- get_type_code() == START_ENCRYPTION_EVENT ||
- /* FD is always checksummed */
- get_type_code() == FORMAT_DESCRIPTION_EVENT) &&
- checksum_alg != BINLOG_CHECKSUM_ALG_OFF));
-
- DBUG_ASSERT(checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
-
- DBUG_ASSERT(((get_type_code() != ROTATE_EVENT &&
- get_type_code() != STOP_EVENT) ||
- get_type_code() != FORMAT_DESCRIPTION_EVENT) ||
- cache_type == Log_event::EVENT_NO_CACHE);
-
- DBUG_RETURN(ret);
-}
-
-int Log_event_writer::write_internal(const uchar *pos, size_t len)
-{
- if (my_b_safe_write(file, pos, len))
- return 1;
- bytes_written+= len;
- return 0;
-}
-
-/*
- as soon as encryption produces the first output block, write event_len
- where it should be in a valid event header
-*/
-int Log_event_writer::maybe_write_event_len(uchar *pos, size_t len)
-{
- if (len && event_len)
- {
- DBUG_ASSERT(len >= EVENT_LEN_OFFSET);
- if (write_internal(pos + EVENT_LEN_OFFSET - 4, 4))
- return 1;
- int4store(pos + EVENT_LEN_OFFSET - 4, event_len);
- event_len= 0;
- }
- return 0;
-}
-
-int Log_event_writer::encrypt_and_write(const uchar *pos, size_t len)
-{
- uchar *dst= 0;
- size_t dstsize= 0;
-
- if (ctx)
- {
- dstsize= encryption_encrypted_length((uint)len, ENCRYPTION_KEY_SYSTEM_DATA,
- crypto->key_version);
- if (!(dst= (uchar*)my_safe_alloca(dstsize)))
- return 1;
-
- uint dstlen;
- if (len == 0)
- dstlen= 0;
- else if (encryption_ctx_update(ctx, pos, (uint)len, dst, &dstlen))
- goto err;
-
- if (maybe_write_event_len(dst, dstlen))
- return 1;
- pos= dst;
- len= dstlen;
- }
- if (write_internal(pos, len))
- goto err;
-
- my_safe_afree(dst, dstsize);
- return 0;
-err:
- my_safe_afree(dst, dstsize);
- return 1;
-}
-
-int Log_event_writer::write_header(uchar *pos, size_t len)
-{
- DBUG_ENTER("Log_event_writer::write_header");
- /*
- recording checksum of FD event computed with dropped
- possibly active LOG_EVENT_BINLOG_IN_USE_F flag.
- Similar step at verication: the active flag is dropped before
- checksum computing.
- */
- if (checksum_len)
- {
- uchar save=pos[FLAGS_OFFSET];
- pos[FLAGS_OFFSET]&= ~LOG_EVENT_BINLOG_IN_USE_F;
- crc= my_checksum(0, pos, len);
- pos[FLAGS_OFFSET]= save;
- }
-
- if (ctx)
- {
- uchar iv[BINLOG_IV_LENGTH];
- crypto->set_iv(iv, (uint32)my_b_safe_tell(file));
- if (encryption_ctx_init(ctx, crypto->key, crypto->key_length,
- iv, sizeof(iv), ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD,
- ENCRYPTION_KEY_SYSTEM_DATA, crypto->key_version))
- DBUG_RETURN(1);
-
- DBUG_ASSERT(len >= LOG_EVENT_HEADER_LEN);
- event_len= uint4korr(pos + EVENT_LEN_OFFSET);
- DBUG_ASSERT(event_len >= len);
- memcpy(pos + EVENT_LEN_OFFSET, pos, 4);
- pos+= 4;
- len-= 4;
- }
- DBUG_RETURN(encrypt_and_write(pos, len));
-}
-
-int Log_event_writer::write_data(const uchar *pos, size_t len)
-{
- DBUG_ENTER("Log_event_writer::write_data");
- if (checksum_len)
- crc= my_checksum(crc, pos, len);
-
- DBUG_RETURN(encrypt_and_write(pos, len));
-}
-
-int Log_event_writer::write_footer()
-{
- DBUG_ENTER("Log_event_writer::write_footer");
- if (checksum_len)
- {
- uchar checksum_buf[BINLOG_CHECKSUM_LEN];
- int4store(checksum_buf, crc);
- if (encrypt_and_write(checksum_buf, BINLOG_CHECKSUM_LEN))
- DBUG_RETURN(ER_ERROR_ON_WRITE);
- }
- if (ctx)
- {
- uint dstlen;
- uchar dst[MY_AES_BLOCK_SIZE*2];
- if (encryption_ctx_finish(ctx, dst, &dstlen))
- DBUG_RETURN(1);
- if (maybe_write_event_len(dst, dstlen) || write_internal(dst, dstlen))
- DBUG_RETURN(ER_ERROR_ON_WRITE);
- }
- DBUG_RETURN(0);
-}
-
-/*
- Log_event::write_header()
-*/
-
-bool Log_event::write_header(size_t event_data_length)
-{
- uchar header[LOG_EVENT_HEADER_LEN];
- ulong now;
- DBUG_ENTER("Log_event::write_header");
- DBUG_PRINT("enter", ("filepos: %lld length: %zu type: %d",
- (longlong) writer->pos(), event_data_length,
- (int) get_type_code()));
-
- writer->checksum_len= need_checksum() ? BINLOG_CHECKSUM_LEN : 0;
-
- /* Store number of bytes that will be written by this event */
- data_written= event_data_length + sizeof(header) + writer->checksum_len;
-
- /*
- log_pos != 0 if this is relay-log event. In this case we should not
- change the position
- */
-
- if (is_artificial_event())
- {
- /*
- Artificial events are automatically generated and do not exist
- in master's binary log, so log_pos should be set to 0.
- */
- log_pos= 0;
- }
- else if (!log_pos)
- {
- /*
- Calculate the position of where the next event will start
- (end of this event, that is).
- */
-
- log_pos= writer->pos() + data_written;
-
- DBUG_EXECUTE_IF("dbug_master_binlog_over_2GB", log_pos += (1ULL <<31););
- }
-
- now= get_time(); // Query start time
-
- /*
- Header will be of size LOG_EVENT_HEADER_LEN for all events, except for
- FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be
- LOG_EVENT_MINIMAL_HEADER_LEN (remember these 2 have a frozen header,
- because we read them before knowing the format).
- */
-
- int4store(header, now); // timestamp
- header[EVENT_TYPE_OFFSET]= get_type_code();
- int4store(header+ SERVER_ID_OFFSET, server_id);
- int4store(header+ EVENT_LEN_OFFSET, data_written);
- int4store(header+ LOG_POS_OFFSET, log_pos);
- int2store(header + FLAGS_OFFSET, flags);
-
- bool ret= writer->write_header(header, sizeof(header));
- DBUG_RETURN(ret);
-}
-
-#endif /* !MYSQL_CLIENT */
/**
This needn't be format-tolerant, because we only parse the first
@@ -1864,7 +877,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
*/
sz += MY_AES_BLOCK_SIZE;
#endif
- char *newpkt= (char*)my_malloc(sz, MYF(MY_WME));
+ char *newpkt= (char*)my_malloc(PSI_INSTRUMENT_ME, sz, MYF(MY_WME));
if (!newpkt)
DBUG_RETURN(LOG_READ_MEM);
memcpy(newpkt, packet->ptr(), ev_offset);
@@ -2169,6 +1182,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2220,7 +1236,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -2298,2203 +1313,6 @@ exit:
DBUG_RETURN(ev);
}
-#ifdef MYSQL_CLIENT
-
-static bool 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
- size_t emit_buf_written;
-
- if (my_b_printf(file,
- "# "
- "|Timestamp "
- "|Type "
- "|Master ID "
- "|Size "
- "|Master Pos "
- "|Flags\n"))
- goto err;
- 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));
- if (my_b_write(file, reinterpret_cast<uchar*>(emit_buf), emit_buf_written) ||
- my_b_write(file, (uchar*)"#\n", 2))
- goto err;
-
- return 0;
-err:
- return 1;
-}
-
-
-/*
- 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 bool 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 0; // ok, nothing to do
-
- 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]= ' ';
- if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
- sizeof(emit_buffer) - 1))
- goto err;
- 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)));
- if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
- c - emit_buffer))
- goto err;
- }
- if (my_b_write(file, (uchar*)"#\n", 2))
- goto err;
-
- return 0;
-err:
- return 1;
-}
-
-/*
- Log_event::print_header()
-*/
-
-bool Log_event::print_header(IO_CACHE* file,
- PRINT_EVENT_INFO* print_event_info,
- bool is_more __attribute__((unused)))
-{
- char llbuff[22];
- my_off_t hexdump_from= print_event_info->hexdump_from;
- DBUG_ENTER("Log_event::print_header");
-
- if (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)))
- goto err;
-
- /* print the checksum */
-
- if (checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
- {
- char checksum_buf[BINLOG_CHECKSUM_LEN * 2 + 4]; // to fit to "%p "
- size_t const bytes_written=
- my_snprintf(checksum_buf, sizeof(checksum_buf), "0x%08x ", crc);
- if (my_b_printf(file, "%s ", get_type(&binlog_checksum_typelib,
- checksum_alg)) ||
- my_b_printf(file, checksum_buf, bytes_written))
- goto err;
- }
-
- /* mysqlbinlog --hexdump */
- if (print_event_info->hexdump_from)
- {
- my_b_write_byte(file, '\n');
- uchar *ptr= (uchar*)temp_buf;
- my_off_t size= uint4korr(ptr + EVENT_LEN_OFFSET);
- my_off_t hdr_len= get_header_len(print_event_info->common_header_len);
-
- size-= hdr_len;
-
- if (my_b_printf(file, "# Position\n"))
- goto err;
-
- /* Write the header, nicely formatted by field. */
- if (hexdump_minimal_header_to_io_cache(file, hexdump_from, ptr))
- goto err;
-
- ptr+= hdr_len;
- hexdump_from+= hdr_len;
-
- /* Print the rest of the data, mimicking "hexdump -C" output. */
- if (hexdump_data_to_io_cache(file, hexdump_from, ptr, size))
- goto err;
-
- /*
- Prefix the next line so that the output from print_helper()
- will appear as a comment.
- */
- if (my_b_write(file, (uchar*)"# Event: ", 9))
- goto err;
- }
-
- DBUG_RETURN(0);
-
-err:
- DBUG_RETURN(1);
-}
-
-
-/**
- Prints a quoted string to io cache.
- Control characters are displayed as hex sequence, e.g. \x00
- Single-quote and backslash characters are escaped with a \
-
- @param[in] file IO cache
- @param[in] prt Pointer to string
- @param[in] length String length
-*/
-
-static void
-my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length)
-{
- const uchar *s;
- my_b_write_byte(file, '\'');
- for (s= ptr; length > 0 ; s++, length--)
- {
- if (*s > 0x1F)
- my_b_write_byte(file, *s);
- else if (*s == '\'')
- my_b_write(file, (uchar*)"\\'", 2);
- else if (*s == '\\')
- my_b_write(file, (uchar*)"\\\\", 2);
- else
- {
- uchar hex[10];
- size_t len= my_snprintf((char*) hex, sizeof(hex), "%s%02x", "\\x", *s);
- my_b_write(file, hex, len);
- }
- }
- my_b_write_byte(file, '\'');
-}
-
-
-/**
- Prints a bit string to io cache in format b'1010'.
-
- @param[in] file IO cache
- @param[in] ptr Pointer to string
- @param[in] nbits Number of bits
-*/
-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_write(file, (uchar*)"b'", 2);
- for (bitnum= skip_bits ; bitnum < nbits8; bitnum++)
- {
- int is_set= (ptr[(bitnum) / 8] >> (7 - bitnum % 8)) & 0x01;
- my_b_write_byte(file, (is_set ? '1' : '0'));
- }
- my_b_write_byte(file, '\'');
-}
-
-
-/**
- Prints a packed string to io cache.
- The string consists of length packed to 1 or 2 bytes,
- followed by string data itself.
-
- @param[in] file IO cache
- @param[in] ptr Pointer to string
- @param[in] length String size
-
- @retval - number of bytes scanned.
-*/
-static size_t
-my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length)
-{
- if (length < 256)
- {
- length= *ptr;
- my_b_write_quoted(file, ptr + 1, length);
- return length + 1;
- }
- else
- {
- length= uint2korr(ptr);
- my_b_write_quoted(file, ptr + 2, length);
- return length + 2;
- }
-}
-
-
-/**
- Prints a 32-bit number in both signed and unsigned representation
-
- @param[in] file IO cache
- @param[in] sl Signed number
- @param[in] ul Unsigned number
-*/
-static bool
-my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui)
-{
- bool res= my_b_printf(file, "%d", si);
- if (si < 0)
- if (my_b_printf(file, " (%u)", ui))
- res= 1;
- return res;
-}
-
-
-/**
- Print a packed value of the given SQL type into IO cache
-
- @param[in] file IO cache
- @param[in] ptr Pointer to string
- @param[in] type Column type
- @param[in] meta Column meta information
- @param[out] typestr SQL type string buffer (for verbose output)
- @param[out] typestr_length Size of typestr
-
- @retval - number of bytes scanned from ptr.
- Except in case of NULL, in which case we return 1 to indicate ok
-*/
-
-static size_t
-log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info,
- const uchar *ptr, uint type, uint meta,
- char *typestr, size_t typestr_length)
-{
- uint32 length= 0;
-
- if (type == MYSQL_TYPE_STRING)
- {
- if (meta >= 256)
- {
- uint byte0= meta >> 8;
- uint byte1= meta & 0xFF;
-
- if ((byte0 & 0x30) != 0x30)
- {
- /* a long CHAR() field: see #37426 */
- length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
- type= byte0 | 0x30;
- }
- else
- length = meta & 0xFF;
- }
- else
- length= meta;
- }
-
- switch (type) {
- case MYSQL_TYPE_LONG:
- {
- strmake(typestr, "INT", typestr_length);
- if (!ptr)
- goto return_null;
-
- int32 si= sint4korr(ptr);
- uint32 ui= uint4korr(ptr);
- my_b_write_sint32_and_uint32(file, si, ui);
- return 4;
- }
-
- case MYSQL_TYPE_TINY:
- {
- strmake(typestr, "TINYINT", typestr_length);
- if (!ptr)
- goto return_null;
-
- my_b_write_sint32_and_uint32(file, (int) (signed char) *ptr,
- (uint) (unsigned char) *ptr);
- return 1;
- }
-
- case MYSQL_TYPE_SHORT:
- {
- strmake(typestr, "SHORTINT", typestr_length);
- if (!ptr)
- goto return_null;
-
- int32 si= (int32) sint2korr(ptr);
- uint32 ui= (uint32) uint2korr(ptr);
- my_b_write_sint32_and_uint32(file, si, ui);
- return 2;
- }
-
- case MYSQL_TYPE_INT24:
- {
- strmake(typestr, "MEDIUMINT", typestr_length);
- if (!ptr)
- goto return_null;
-
- int32 si= sint3korr(ptr);
- uint32 ui= uint3korr(ptr);
- my_b_write_sint32_and_uint32(file, si, ui);
- return 3;
- }
-
- case MYSQL_TYPE_LONGLONG:
- {
- strmake(typestr, "LONGINT", typestr_length);
- if (!ptr)
- goto return_null;
-
- char tmp[64];
- size_t length;
- longlong si= sint8korr(ptr);
- length= (longlong10_to_str(si, tmp, -10) - tmp);
- my_b_write(file, (uchar*)tmp, length);
- if (si < 0)
- {
- ulonglong ui= uint8korr(ptr);
- longlong10_to_str((longlong) ui, tmp, 10);
- my_b_printf(file, " (%s)", tmp);
- }
- return 8;
- }
-
- case MYSQL_TYPE_NEWDECIMAL:
- {
- uint precision= meta >> 8;
- uint decimals= meta & 0xFF;
- my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)",
- precision, decimals);
- if (!ptr)
- goto return_null;
-
- uint bin_size= my_decimal_get_binary_size(precision, decimals);
- my_decimal dec((const uchar *) ptr, precision, decimals);
- int length= DECIMAL_MAX_STR_LENGTH;
- char buff[DECIMAL_MAX_STR_LENGTH + 1];
- decimal2string(&dec, buff, &length, 0, 0, 0);
- my_b_write(file, (uchar*)buff, length);
- return bin_size;
- }
-
- case MYSQL_TYPE_FLOAT:
- {
- strmake(typestr, "FLOAT", typestr_length);
- if (!ptr)
- goto return_null;
-
- float fl;
- float4get(fl, ptr);
- char tmp[320];
- sprintf(tmp, "%-20g", (double) fl);
- my_b_printf(file, "%s", tmp); /* my_snprintf doesn't support %-20g */
- return 4;
- }
-
- case MYSQL_TYPE_DOUBLE:
- {
- double dbl;
- strmake(typestr, "DOUBLE", typestr_length);
- if (!ptr)
- goto return_null;
-
- float8get(dbl, ptr);
- char tmp[320];
- sprintf(tmp, "%-.20g", dbl); /* strmake doesn't support %-20g */
- my_b_printf(file, tmp, "%s");
- return 8;
- }
-
- case MYSQL_TYPE_BIT:
- {
- /* Meta-data: bit_len, bytes_in_rec, 2 bytes */
- uint nbits= ((meta >> 8) * 8) + (meta & 0xFF);
- my_snprintf(typestr, typestr_length, "BIT(%d)", nbits);
- if (!ptr)
- goto return_null;
-
- length= (nbits + 7) / 8;
- my_b_write_bit(file, ptr, nbits);
- return length;
- }
-
- case MYSQL_TYPE_TIMESTAMP:
- {
- strmake(typestr, "TIMESTAMP", typestr_length);
- if (!ptr)
- goto return_null;
-
- uint32 i32= uint4korr(ptr);
- my_b_printf(file, "%d", i32);
- return 4;
- }
-
- case MYSQL_TYPE_TIMESTAMP2:
- {
- my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", meta);
- if (!ptr)
- goto return_null;
-
- 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, (uchar*)buf, buflen);
- return my_timestamp_binary_length(meta);
- }
-
- case MYSQL_TYPE_DATETIME:
- {
- strmake(typestr, "DATETIME", typestr_length);
- if (!ptr)
- goto return_null;
-
- ulong d, t;
- uint64 i64= uint8korr(ptr); /* YYYYMMDDhhmmss */
- d= (ulong) (i64 / 1000000);
- t= (ulong) (i64 % 1000000);
-
- 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);
- return 8;
- }
-
- case MYSQL_TYPE_DATETIME2:
- {
- my_snprintf(typestr, typestr_length, "DATETIME(%d)", meta);
- if (!ptr)
- goto return_null;
-
- 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);
- return my_datetime_binary_length(meta);
- }
-
- case MYSQL_TYPE_TIME:
- {
- strmake(typestr, "TIME", typestr_length);
- if (!ptr)
- goto return_null;
-
- 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);
- return 3;
- }
-
- case MYSQL_TYPE_TIME2:
- {
- my_snprintf(typestr, typestr_length, "TIME(%d)", meta);
- if (!ptr)
- goto return_null;
-
- 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);
- return my_time_binary_length(meta);
- }
-
- case MYSQL_TYPE_NEWDATE:
- {
- strmake(typestr, "DATE", typestr_length);
- if (!ptr)
- goto return_null;
-
- uint32 tmp= uint3korr(ptr);
- int part;
- char buf[11];
- char *pos= &buf[10]; // start from '\0' to the beginning
-
- /* Copied from field.cc */
- *pos--=0; // End NULL
- part=(int) (tmp & 31);
- *pos--= (char) ('0'+part%10);
- *pos--= (char) ('0'+part/10);
- *pos--= ':';
- part=(int) (tmp >> 5 & 15);
- *pos--= (char) ('0'+part%10);
- *pos--= (char) ('0'+part/10);
- *pos--= ':';
- part=(int) (tmp >> 9);
- *pos--= (char) ('0'+part%10); part/=10;
- *pos--= (char) ('0'+part%10); part/=10;
- *pos--= (char) ('0'+part%10); part/=10;
- *pos= (char) ('0'+part);
- my_b_printf(file , "'%s'", buf);
- return 3;
- }
-
- case MYSQL_TYPE_DATE:
- {
- strmake(typestr, "DATE", typestr_length);
- if (!ptr)
- goto return_null;
-
- uint i32= uint3korr(ptr);
- my_b_printf(file , "'%04d:%02d:%02d'",
- (int)(i32 / (16L * 32L)), (int)(i32 / 32L % 16L),
- (int)(i32 % 32L));
- return 3;
- }
-
- case MYSQL_TYPE_YEAR:
- {
- strmake(typestr, "YEAR", typestr_length);
- if (!ptr)
- goto return_null;
-
- uint32 i32= *ptr;
- my_b_printf(file, "%04d", i32+ 1900);
- return 1;
- }
-
- case MYSQL_TYPE_ENUM:
- switch (meta & 0xFF) {
- case 1:
- strmake(typestr, "ENUM(1 byte)", typestr_length);
- if (!ptr)
- goto return_null;
-
- my_b_printf(file, "%d", (int) *ptr);
- return 1;
- case 2:
- {
- strmake(typestr, "ENUM(2 bytes)", typestr_length);
- if (!ptr)
- goto return_null;
-
- int32 i32= uint2korr(ptr);
- my_b_printf(file, "%d", i32);
- return 2;
- }
- default:
- my_b_printf(file, "!! Unknown ENUM packlen=%d", meta & 0xFF);
- return 0;
- }
- break;
-
- case MYSQL_TYPE_SET:
- my_snprintf(typestr, typestr_length, "SET(%d bytes)", meta & 0xFF);
- if (!ptr)
- goto return_null;
-
- my_b_write_bit(file, ptr , (meta & 0xFF) * 8);
- return meta & 0xFF;
-
- case MYSQL_TYPE_BLOB:
- switch (meta) {
- case 1:
- strmake(typestr, "TINYBLOB/TINYTEXT", typestr_length);
- if (!ptr)
- goto return_null;
-
- length= *ptr;
- my_b_write_quoted(file, ptr + 1, length);
- return length + 1;
- case 2:
- strmake(typestr, "BLOB/TEXT", typestr_length);
- if (!ptr)
- goto return_null;
-
- length= uint2korr(ptr);
- my_b_write_quoted(file, ptr + 2, length);
- return length + 2;
- case 3:
- strmake(typestr, "MEDIUMBLOB/MEDIUMTEXT", typestr_length);
- if (!ptr)
- goto return_null;
-
- length= uint3korr(ptr);
- my_b_write_quoted(file, ptr + 3, length);
- return length + 3;
- case 4:
- strmake(typestr, "LONGBLOB/LONGTEXT", typestr_length);
- if (!ptr)
- goto return_null;
-
- length= uint4korr(ptr);
- my_b_write_quoted(file, ptr + 4, length);
- return length + 4;
- default:
- my_b_printf(file, "!! Unknown BLOB packlen=%d", length);
- return 0;
- }
-
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- length= meta;
- my_snprintf(typestr, typestr_length, "VARSTRING(%d)", length);
- if (!ptr)
- goto return_null;
-
- return my_b_write_quoted_with_length(file, ptr, length);
-
- case MYSQL_TYPE_STRING:
- my_snprintf(typestr, typestr_length, "STRING(%d)", length);
- if (!ptr)
- goto return_null;
-
- return my_b_write_quoted_with_length(file, ptr, length);
-
- case MYSQL_TYPE_DECIMAL:
- print_event_info->flush_for_error();
- fprintf(stderr, "\nError: Found Old DECIMAL (mysql-4.1 or earlier). "
- "Not enough metadata to display the value.\n");
- break;
-
- case MYSQL_TYPE_GEOMETRY:
- strmake(typestr, "GEOMETRY", typestr_length);
- if (!ptr)
- goto return_null;
-
- length= uint4korr(ptr);
- my_b_write_quoted(file, ptr + meta, length);
- return length + meta;
-
- default:
- print_event_info->flush_for_error();
- fprintf(stderr,
- "\nError: Don't know how to handle column type: %d meta: %d (%04x)\n",
- type, meta, meta);
- break;
- }
- *typestr= 0;
- return 0;
-
-return_null:
- return my_b_write(file, (uchar*) "NULL", 4) ? 0 : 1;
-}
-
-
-/**
- Print a packed row into IO cache
-
- @param[in] file IO cache
- @param[in] td Table definition
- @param[in] print_event_into Print parameters
- @param[in] cols_bitmap Column bitmaps.
- @param[in] value Pointer to packed row
- @param[in] prefix Row's SQL clause ("SET", "WHERE", etc)
-
- @retval 0 error
- # number of bytes scanned.
-*/
-
-
-size_t
-Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
- PRINT_EVENT_INFO *print_event_info,
- MY_BITMAP *cols_bitmap,
- const uchar *value, const uchar *prefix,
- const my_bool no_fill_output)
-{
- const uchar *value0= value;
- const uchar *null_bits= value;
- uint null_bit_index= 0;
- char typestr[64]= "";
-
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- /* Storing the review SQL */
- IO_CACHE *review_sql= &print_event_info->review_sql_cache;
- LEX_STRING review_str;
-#endif
-
- /*
- Skip metadata bytes which gives the information about nullabity of master
- columns. Master writes one bit for each affected column.
- */
-
- value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
-
- if (!no_fill_output)
- if (my_b_printf(file, "%s", prefix))
- goto err;
-
- for (uint i= 0; i < (uint)td->size(); i ++)
- {
- size_t size;
- int is_null= (null_bits[null_bit_index / 8]
- >> (null_bit_index % 8)) & 0x01;
-
- if (bitmap_is_set(cols_bitmap, i) == 0)
- continue;
-
- if (!no_fill_output)
- if (my_b_printf(file, "### @%d=", static_cast<int>(i + 1)))
- goto err;
-
- if (!is_null)
- {
- size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
- if (value + fsize > m_rows_end)
- {
- if (!no_fill_output)
- if (my_b_printf(file, "***Corrupted replication event was detected."
- " Not printing the value***\n"))
- goto err;
- value+= fsize;
- return 0;
- }
- }
-
- if (!no_fill_output)
- {
- size= log_event_print_value(file, print_event_info, is_null? NULL: value,
- td->type(i), td->field_metadata(i),
- typestr, sizeof(typestr));
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- if (need_flashback_review)
- {
- String tmp_str, hex_str;
- IO_CACHE tmp_cache;
-
- // Using a tmp IO_CACHE to get the value output
- open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
- size= log_event_print_value(&tmp_cache, print_event_info,
- is_null ? NULL: value,
- td->type(i), td->field_metadata(i),
- typestr, sizeof(typestr));
- error= copy_event_cache_to_string_and_reinit(&tmp_cache, &review_str);
- close_cached_file(&tmp_cache);
- if (unlikely(error))
- return 0;
-
- switch (td->type(i)) // Converting a string to HEX format
- {
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_BLOB:
- // Avoid write_pos changed to a new area
- // tmp_str.free();
- tmp_str.append(review_str.str + 1, review_str.length - 2); // Removing quotation marks
- if (hex_str.alloc(tmp_str.length()*2+1)) // If out of memory
- {
- fprintf(stderr, "\nError: Out of memory. "
- "Could not print correct binlog event.\n");
- exit(1);
- }
- octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length());
- if (my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr()))
- goto err;
- break;
- default:
- tmp_str.free();
- if (tmp_str.append(review_str.str, review_str.length) ||
- my_b_printf(review_sql, ", %s", tmp_str.ptr()))
- goto err;
- break;
- }
- my_free(revieww_str.str);
- }
-#endif
- }
- else
- {
- IO_CACHE tmp_cache;
- open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
- size= log_event_print_value(&tmp_cache, print_event_info,
- is_null ? NULL: value,
- td->type(i), td->field_metadata(i),
- typestr, sizeof(typestr));
- close_cached_file(&tmp_cache);
- }
-
- if (!size)
- goto err;
-
- if (!is_null)
- value+= size;
-
- if (print_event_info->verbose > 1 && !no_fill_output)
- {
- if (my_b_write(file, (uchar*)" /* ", 4) ||
- my_b_printf(file, "%s ", typestr) ||
- my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
- td->field_metadata(i),
- td->maybe_null(i), is_null) ||
- my_b_write(file, (uchar*)"*/", 2))
- goto err;
- }
-
- if (!no_fill_output)
- if (my_b_write_byte(file, '\n'))
- goto err;
-
- null_bit_index++;
- }
- return value - value0;
-
-err:
- return 0;
-}
-
-
-/**
- Exchange the SET part and WHERE part for the Update events.
- Revert the operations order for the Write and Delete events.
- And then revert the events order from the last one to the first one.
-
- @param[in] print_event_info PRINT_EVENT_INFO
- @param[in] rows_buff Packed event buff
-*/
-
-void Rows_log_event::change_to_flashback_event(PRINT_EVENT_INFO *print_event_info,
- uchar *rows_buff, Log_event_type ev_type)
-{
- Table_map_log_event *map;
- table_def *td;
- DYNAMIC_ARRAY rows_arr;
- uchar *swap_buff1;
- uchar *rows_pos= rows_buff + m_rows_before_size;
-
- if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
- !(td= map->create_table_def()))
- return;
-
- /* If the write rows event contained no values for the AI */
- if (((get_general_type_code() == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
- goto end;
-
- (void) my_init_dynamic_array(&rows_arr, sizeof(LEX_STRING), 8, 8, MYF(0));
-
- for (uchar *value= m_rows_buf; value < m_rows_end; )
- {
- uchar *start_pos= value;
- size_t length1= 0;
- if (!(length1= print_verbose_one_row(NULL, td, print_event_info,
- &m_cols, value,
- (const uchar*) "", TRUE)))
- {
- fprintf(stderr, "\nError row length: %zu\n", length1);
- exit(1);
- }
- value+= length1;
-
- swap_buff1= (uchar *) my_malloc(length1, MYF(0));
- if (!swap_buff1)
- {
- fprintf(stderr, "\nError: Out of memory. "
- "Could not exchange to flashback event.\n");
- exit(1);
- }
- memcpy(swap_buff1, start_pos, length1);
-
- // For Update_event, we have the second part
- size_t length2= 0;
- if (ev_type == UPDATE_ROWS_EVENT ||
- ev_type == UPDATE_ROWS_EVENT_V1)
- {
- if (!(length2= print_verbose_one_row(NULL, td, print_event_info,
- &m_cols, value,
- (const uchar*) "", TRUE)))
- {
- fprintf(stderr, "\nError row length: %zu\n", length2);
- exit(1);
- }
- value+= length2;
-
- void *swap_buff2= my_malloc(length2, MYF(0));
- if (!swap_buff2)
- {
- fprintf(stderr, "\nError: Out of memory. "
- "Could not exchange to flashback event.\n");
- exit(1);
- }
- memcpy(swap_buff2, start_pos + length1, length2); // WHERE part
-
- /* Swap SET and WHERE part */
- memcpy(start_pos, swap_buff2, length2);
- memcpy(start_pos + length2, swap_buff1, length1);
- my_free(swap_buff2);
- }
-
- my_free(swap_buff1);
-
- /* Copying one row into a buff, and pushing into the array */
- LEX_STRING one_row;
-
- one_row.length= length1 + length2;
- one_row.str= (char *) my_malloc(one_row.length, MYF(0));
- memcpy(one_row.str, start_pos, one_row.length);
- if (one_row.str == NULL || push_dynamic(&rows_arr, (uchar *) &one_row))
- {
- fprintf(stderr, "\nError: Out of memory. "
- "Could not push flashback event into array.\n");
- exit(1);
- }
- }
-
- /* Copying rows from the end to the begining into event */
- for (uint i= rows_arr.elements; i > 0; --i)
- {
- LEX_STRING *one_row= dynamic_element(&rows_arr, i - 1, LEX_STRING*);
-
- memcpy(rows_pos, (uchar *)one_row->str, one_row->length);
- rows_pos+= one_row->length;
- my_free(one_row->str);
- }
- delete_dynamic(&rows_arr);
-
-end:
- delete td;
-}
-
-/**
- Calc length of a packed value of the given SQL type
-
- @param[in] ptr Pointer to string
- @param[in] type Column type
- @param[in] meta Column meta information
-
- @retval - number of bytes scanned from ptr.
- Except in case of NULL, in which case we return 1 to indicate ok
-*/
-
-static size_t calc_field_event_length(const uchar *ptr, uint type, uint meta)
-{
- uint32 length= 0;
-
- if (type == MYSQL_TYPE_STRING)
- {
- if (meta >= 256)
- {
- uint byte0= meta >> 8;
- uint byte1= meta & 0xFF;
-
- if ((byte0 & 0x30) != 0x30)
- {
- /* a long CHAR() field: see #37426 */
- length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
- type= byte0 | 0x30;
- }
- else
- length = meta & 0xFF;
- }
- else
- length= meta;
- }
-
- switch (type) {
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_TIMESTAMP:
- return 4;
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_YEAR:
- return 1;
- case MYSQL_TYPE_SHORT:
- return 2;
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_DATE:
- return 3;
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_DATETIME:
- return 8;
- case MYSQL_TYPE_NEWDECIMAL:
- {
- uint precision= meta >> 8;
- uint decimals= meta & 0xFF;
- uint bin_size= my_decimal_get_binary_size(precision, decimals);
- return bin_size;
- }
- case MYSQL_TYPE_FLOAT:
- return 4;
- case MYSQL_TYPE_DOUBLE:
- return 8;
- case MYSQL_TYPE_BIT:
- {
- /* Meta-data: bit_len, bytes_in_rec, 2 bytes */
- uint nbits= ((meta >> 8) * 8) + (meta & 0xFF);
- length= (nbits + 7) / 8;
- return length;
- }
- case MYSQL_TYPE_TIMESTAMP2:
- return my_timestamp_binary_length(meta);
- case MYSQL_TYPE_DATETIME2:
- return my_datetime_binary_length(meta);
- case MYSQL_TYPE_TIME2:
- return my_time_binary_length(meta);
- case MYSQL_TYPE_ENUM:
- switch (meta & 0xFF) {
- case 1:
- case 2:
- return (meta & 0xFF);
- default:
- /* Unknown ENUM packlen=%d", meta & 0xFF */
- return 0;
- }
- break;
- case MYSQL_TYPE_SET:
- return meta & 0xFF;
- case MYSQL_TYPE_BLOB:
- switch (meta) {
- default:
- return 0;
- case 1:
- return *ptr + 1;
- case 2:
- return uint2korr(ptr) + 2;
- case 3:
- return uint3korr(ptr) + 3;
- case 4:
- return uint4korr(ptr) + 4;
- }
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VAR_STRING:
- length= meta;
- /* fall through */
- case MYSQL_TYPE_STRING:
- if (length < 256)
- return (uint) *ptr + 1;
- return uint2korr(ptr) + 2;
- case MYSQL_TYPE_DECIMAL:
- break;
- default:
- break;
- }
- return 0;
-}
-
-
-size_t
-Rows_log_event::calc_row_event_length(table_def *td,
- PRINT_EVENT_INFO *print_event_info,
- MY_BITMAP *cols_bitmap,
- const uchar *value)
-{
- const uchar *value0= value;
- const uchar *null_bits= value;
- uint null_bit_index= 0;
-
- /*
- Skip metadata bytes which gives the information about nullabity of master
- columns. Master writes one bit for each affected column.
- */
-
- value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
-
- for (uint i= 0; i < (uint)td->size(); i ++)
- {
- int is_null;
- is_null= (null_bits[null_bit_index / 8] >> (null_bit_index % 8)) & 0x01;
-
- if (bitmap_is_set(cols_bitmap, i) == 0)
- continue;
-
- if (!is_null)
- {
- size_t size;
- size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
- if (value + fsize > m_rows_end)
- {
- /* Corrupted replication event was detected, skipping entry */
- return 0;
- }
- if (!(size= calc_field_event_length(value, td->type(i),
- td->field_metadata(i))))
- return 0;
- value+= size;
- }
- null_bit_index++;
- }
- return value - value0;
-}
-
-
-/**
- Calculate how many rows there are in the event
-
- @param[in] file IO cache
- @param[in] print_event_into Print parameters
-*/
-
-void Rows_log_event::count_row_events(PRINT_EVENT_INFO *print_event_info)
-{
- Table_map_log_event *map;
- table_def *td;
- uint row_events;
- Log_event_type general_type_code= get_general_type_code();
-
- switch (general_type_code) {
- case WRITE_ROWS_EVENT:
- case DELETE_ROWS_EVENT:
- row_events= 1;
- break;
- case UPDATE_ROWS_EVENT:
- row_events= 2;
- break;
- default:
- DBUG_ASSERT(0); /* Not possible */
- return;
- }
-
- if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
- !(td= map->create_table_def()))
- {
- /* Row event for unknown table */
- return;
- }
-
- for (const uchar *value= m_rows_buf; value < m_rows_end; )
- {
- size_t length;
- print_event_info->row_events++;
-
- /* Print the first image */
- if (!(length= calc_row_event_length(td, print_event_info,
- &m_cols, value)))
- break;
- value+= length;
- DBUG_ASSERT(value <= m_rows_end);
-
- /* Print the second image (for UPDATE only) */
- if (row_events == 2)
- {
- if (!(length= calc_row_event_length(td, print_event_info,
- &m_cols_ai, value)))
- break;
- value+= length;
- DBUG_ASSERT(value <= m_rows_end);
- }
- }
- delete td;
-}
-
-
-/**
- Print a row event into IO cache in human readable form (in SQL format)
-
- @param[in] file IO cache
- @param[in] print_event_into Print parameters
-*/
-
-bool Rows_log_event::print_verbose(IO_CACHE *file,
- PRINT_EVENT_INFO *print_event_info)
-{
- Table_map_log_event *map;
- table_def *td= 0;
- const char *sql_command, *sql_clause1, *sql_clause2;
- const char *sql_command_short __attribute__((unused));
- Log_event_type general_type_code= get_general_type_code();
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- IO_CACHE *review_sql= &print_event_info->review_sql_cache;
-#endif
-
- 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);
-
- if (my_b_printf(file, "### Extra row data format: %u, len: %u :",
- m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET],
- extra_payload_len))
- goto err;
- 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);
- if (my_b_printf(file, "%s", buff))
- goto err;
- }
- if (my_b_printf(file, "\n"))
- goto err;
- }
-
- switch (general_type_code) {
- case WRITE_ROWS_EVENT:
- sql_command= "INSERT INTO";
- sql_clause1= "### SET\n";
- sql_clause2= NULL;
- sql_command_short= "I";
- break;
- case DELETE_ROWS_EVENT:
- sql_command= "DELETE FROM";
- sql_clause1= "### WHERE\n";
- sql_clause2= NULL;
- sql_command_short= "D";
- break;
- case UPDATE_ROWS_EVENT:
- sql_command= "UPDATE";
- sql_clause1= "### WHERE\n";
- sql_clause2= "### SET\n";
- sql_command_short= "U";
- break;
- default:
- sql_command= sql_clause1= sql_clause2= NULL;
- sql_command_short= "";
- DBUG_ASSERT(0); /* Not possible */
- }
-
- if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
- !(td= map->create_table_def()))
- {
- return (my_b_printf(file, "### Row event for unknown table #%lu",
- (ulong) m_table_id));
- }
-
- /* If the write rows event contained no values for the AI */
- if (((general_type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
- {
- if (my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n",
- map->get_db_name(), map->get_table_name()))
- goto err;
- goto end;
- }
-
- for (const uchar *value= m_rows_buf; value < m_rows_end; )
- {
- size_t length;
- print_event_info->row_events++;
-
- if (my_b_printf(file, "### %s %`s.%`s\n",
- sql_command,
- map->get_db_name(), map->get_table_name()))
- goto err;
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- if (need_flashback_review)
- if (my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'",
- map->get_review_dbname(), map->get_review_tablename(),
- sql_command_short))
- goto err;
-#endif
-
- /* Print the first image */
- if (!(length= print_verbose_one_row(file, td, print_event_info,
- &m_cols, value,
- (const uchar*) sql_clause1)))
- goto err;
- value+= length;
-
- /* Print the second image (for UPDATE only) */
- if (sql_clause2)
- {
- if (!(length= print_verbose_one_row(file, td, print_event_info,
- &m_cols_ai, value,
- (const uchar*) sql_clause2)))
- goto err;
- value+= length;
- }
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- else
- {
- if (need_flashback_review)
- for (size_t i= 0; i < td->size(); i ++)
- if (my_b_printf(review_sql, ", NULL"))
- goto err;
- }
-
- if (need_flashback_review)
- if (my_b_printf(review_sql, ")%s\n", print_event_info->delimiter))
- goto err;
-#endif
- }
-
-end:
- delete td;
- return 0;
-err:
- delete td;
- return 1;
-}
-
-void free_table_map_log_event(Table_map_log_event *event)
-{
- delete event;
-}
-
-/**
- Encode the event, optionally per 'do_print_encoded' arg store the
- result into the argument cache; optionally per event_info's
- 'verbose' print into the cache a verbose representation of the event.
- Note, no extra wrapping is done to the being io-cached data, like
- to producing a BINLOG query. It's left for a routine that extracts from
- the cache.
-
- @param file pointer to IO_CACHE
- @param print_event_info pointer to print_event_info specializing
- what out of and how to print the event
- @param do_print_encoded whether to store base64-encoded event
- into @file.
-*/
-bool Log_event::print_base64(IO_CACHE* file,
- PRINT_EVENT_INFO* print_event_info,
- bool do_print_encoded)
-{
- uchar *ptr= (uchar *)temp_buf;
- uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
- DBUG_ENTER("Log_event::print_base64");
-
- if (is_flashback)
- {
- uint tmp_size= size;
- Rows_log_event *ev= NULL;
- Log_event_type ev_type = (enum Log_event_type) ptr[EVENT_TYPE_OFFSET];
- if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
- checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
- tmp_size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
- switch (ev_type) {
- case WRITE_ROWS_EVENT:
- ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT;
- ev= new Delete_rows_log_event((const char*) ptr, tmp_size,
- glob_description_event);
- ev->change_to_flashback_event(print_event_info, ptr, ev_type);
- break;
- case WRITE_ROWS_EVENT_V1:
- ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT_V1;
- ev= new Delete_rows_log_event((const char*) ptr, tmp_size,
- glob_description_event);
- ev->change_to_flashback_event(print_event_info, ptr, ev_type);
- break;
- case DELETE_ROWS_EVENT:
- ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT;
- ev= new Write_rows_log_event((const char*) ptr, tmp_size,
- glob_description_event);
- ev->change_to_flashback_event(print_event_info, ptr, ev_type);
- break;
- case DELETE_ROWS_EVENT_V1:
- ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT_V1;
- ev= new Write_rows_log_event((const char*) ptr, tmp_size,
- glob_description_event);
- ev->change_to_flashback_event(print_event_info, ptr, ev_type);
- break;
- case UPDATE_ROWS_EVENT:
- case UPDATE_ROWS_EVENT_V1:
- ev= new Update_rows_log_event((const char*) ptr, tmp_size,
- glob_description_event);
- ev->change_to_flashback_event(print_event_info, ptr, ev_type);
- break;
- default:
- break;
- }
- delete ev;
- }
-
- if (do_print_encoded)
- {
- size_t const tmp_str_sz= my_base64_needed_encoded_length((int) size);
- char *tmp_str;
- if (!(tmp_str= (char *) my_malloc(tmp_str_sz, MYF(MY_WME))))
- goto err;
-
- if (my_base64_encode(ptr, (size_t) size, tmp_str))
- {
- DBUG_ASSERT(0);
- }
-
- my_b_printf(file, "%s\n", tmp_str);
- my_free(tmp_str);
- }
-
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- if (print_event_info->verbose || print_event_info->print_row_count ||
- need_flashback_review)
-#else
- // Flashback need the table_map to parse the event
- if (print_event_info->verbose || print_event_info->print_row_count ||
- is_flashback)
-#endif
- {
- 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
-
- switch (et)
- {
- case TABLE_MAP_EVENT:
- {
- Table_map_log_event *map;
- map= new Table_map_log_event((const char*) ptr, size,
- glob_description_event);
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- if (need_flashback_review)
- {
- map->set_review_dbname(m_review_dbname.ptr());
- map->set_review_tablename(m_review_tablename.ptr());
- }
-#endif
- print_event_info->m_table_map.set_table(map->get_table_id(), map);
- break;
- }
- case WRITE_ROWS_EVENT:
- case WRITE_ROWS_EVENT_V1:
- {
- ev= new Write_rows_log_event((const char*) ptr, size,
- glob_description_event);
- break;
- }
- case DELETE_ROWS_EVENT:
- case DELETE_ROWS_EVENT_V1:
- {
- ev= new Delete_rows_log_event((const char*) ptr, size,
- glob_description_event);
- break;
- }
- case UPDATE_ROWS_EVENT:
- case UPDATE_ROWS_EVENT_V1:
- {
- ev= new Update_rows_log_event((const char*) ptr, size,
- glob_description_event);
- break;
- }
- case WRITE_ROWS_COMPRESSED_EVENT:
- case WRITE_ROWS_COMPRESSED_EVENT_V1:
- {
- ev= new Write_rows_compressed_log_event((const char*) ptr, size,
- glob_description_event);
- break;
- }
- case UPDATE_ROWS_COMPRESSED_EVENT:
- case UPDATE_ROWS_COMPRESSED_EVENT_V1:
- {
- ev= new Update_rows_compressed_log_event((const char*) ptr, size,
- glob_description_event);
- break;
- }
- case DELETE_ROWS_COMPRESSED_EVENT:
- case DELETE_ROWS_COMPRESSED_EVENT_V1:
- {
- ev= new Delete_rows_compressed_log_event((const char*) ptr, size,
- glob_description_event);
- break;
- }
- default:
- break;
- }
-
- if (ev)
- {
- bool error= 0;
-
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- ev->need_flashback_review= need_flashback_review;
- if (print_event_info->verbose)
- {
- if (ev->print_verbose(&print_event_info->tail_cache, print_event_info))
- goto err;
- }
- else
- {
- IO_CACHE tmp_cache;
-
- if (open_cached_file(&tmp_cache, NULL, NULL, 0,
- MYF(MY_WME | MY_NABP)))
- {
- delete ev;
- goto err;
- }
-
- error= ev->print_verbose(&tmp_cache, print_event_info);
- close_cached_file(&tmp_cache);
- if (unlikely(error))
- {
- delete ev;
- goto err;
- }
- }
-#else
- if (print_event_info->verbose)
- error= ev->print_verbose(&print_event_info->tail_cache, print_event_info);
- else
- ev->count_row_events(print_event_info);
-#endif
- delete ev;
- if (unlikely(error))
- goto err;
- }
- }
- DBUG_RETURN(0);
-
-err:
- DBUG_RETURN(1);
-}
-
-
-/*
- Log_event::print_timestamp()
-*/
-
-bool Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
-{
- struct tm *res;
- time_t my_when= when;
- DBUG_ENTER("Log_event::print_timestamp");
- if (!ts)
- ts = &my_when;
- res=localtime(ts);
-
- DBUG_RETURN(my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d",
- res->tm_year % 100,
- res->tm_mon+1,
- res->tm_mday,
- res->tm_hour,
- res->tm_min,
- res->tm_sec));
-}
-
-#endif /* MYSQL_CLIENT */
-
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-inline Log_event::enum_skip_reason
-Log_event::continue_group(rpl_group_info *rgi)
-{
- if (rgi->rli->slave_skip_counter == 1)
- return Log_event::EVENT_SKIP_IGNORE;
- return Log_event::do_shall_skip(rgi);
-}
-#endif
-
-/**************************************************************************
- Query_log_event methods
-**************************************************************************/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-/**
- This (which is used only for SHOW BINLOG EVENTS) could be updated to
- print SET @@session_var=. But this is not urgent, as SHOW BINLOG EVENTS is
- only an information, it does not produce suitable queries to replay (for
- example it does not print LOAD DATA INFILE).
- @todo
- show the catalog ??
-*/
-
-void Query_log_event::pack_info(Protocol *protocol)
-{
- // TODO: show the catalog ??
- char buf_mem[1024];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- buf.real_alloc(9 + db_len + q_len);
- if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
- && db && db_len)
- {
- buf.append(STRING_WITH_LEN("use "));
- append_identifier(protocol->thd, &buf, db, db_len);
- buf.append(STRING_WITH_LEN("; "));
- }
- if (query && q_len)
- buf.append(query, q_len);
- protocol->store(&buf);
-}
-#endif
-
-#ifndef MYSQL_CLIENT
-
-/**
- Utility function for the next method (Query_log_event::write()) .
-*/
-static void store_str_with_code_and_len(uchar **dst, const char *src,
- uint len, uint code)
-{
- /*
- only 1 byte to store the length of catalog, so it should not
- surpass 255
- */
- DBUG_ASSERT(len <= 255);
- DBUG_ASSERT(src);
- *((*dst)++)= (uchar) code;
- *((*dst)++)= (uchar) len;
- bmove(*dst, src, len);
- (*dst)+= len;
-}
-
-
-/**
- Query_log_event::write().
-
- @note
- In this event we have to modify the header to have the correct
- EVENT_LEN_OFFSET as we don't yet know how many status variables we
- will print!
-*/
-
-bool Query_log_event::write()
-{
- uchar buf[QUERY_HEADER_LEN + MAX_SIZE_LOG_EVENT_STATUS];
- uchar *start, *start_of_status;
- ulong event_length;
-
- if (!query)
- return 1; // Something wrong with event
-
- /*
- We want to store the thread id:
- (- as an information for the user when he reads the binlog)
- - if the query uses temporary table: for the slave SQL thread to know to
- which master connection the temp table belongs.
- Now imagine we (write()) are called by the slave SQL thread (we are
- logging a query executed by this thread; the slave runs with
- --log-slave-updates). Then this query will be logged with
- thread_id=the_thread_id_of_the_SQL_thread. Imagine that 2 temp tables of
- the same name were created simultaneously on the master (in the master
- binlog you have
- CREATE TEMPORARY TABLE t; (thread 1)
- CREATE TEMPORARY TABLE t; (thread 2)
- ...)
- then in the slave's binlog there will be
- CREATE TEMPORARY TABLE t; (thread_id_of_the_slave_SQL_thread)
- CREATE TEMPORARY TABLE t; (thread_id_of_the_slave_SQL_thread)
- which is bad (same thread id!).
-
- To avoid this, we log the thread's thread id EXCEPT for the SQL
- slave thread for which we log the original (master's) thread id.
- Now this moves the bug: what happens if the thread id on the
- master was 10 and when the slave replicates the query, a
- connection number 10 is opened by a normal client on the slave,
- and updates a temp table of the same name? We get a problem
- again. To avoid this, in the handling of temp tables (sql_base.cc)
- we use thread_id AND server_id. TODO when this is merged into
- 4.1: in 4.1, slave_proxy_id has been renamed to pseudo_thread_id
- and is a session variable: that's to make mysqlbinlog work with
- temp tables. We probably need to introduce
-
- SET PSEUDO_SERVER_ID
- for mysqlbinlog in 4.1. mysqlbinlog would print:
- SET PSEUDO_SERVER_ID=
- SET PSEUDO_THREAD_ID=
- for each query using temp tables.
- */
- int4store(buf + Q_THREAD_ID_OFFSET, slave_proxy_id);
- int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
- buf[Q_DB_LEN_OFFSET] = (char) db_len;
- int2store(buf + Q_ERR_CODE_OFFSET, error_code);
-
- /*
- You MUST always write status vars in increasing order of code. This
- guarantees that a slightly older slave will be able to parse those he
- knows.
- */
- start_of_status= start= buf+QUERY_HEADER_LEN;
- if (flags2_inited)
- {
- *start++= Q_FLAGS2_CODE;
- int4store(start, flags2);
- start+= 4;
- }
- if (sql_mode_inited)
- {
- *start++= Q_SQL_MODE_CODE;
- int8store(start, (ulonglong)sql_mode);
- start+= 8;
- }
- if (catalog_len) // i.e. this var is inited (false for 4.0 events)
- {
- store_str_with_code_and_len(&start,
- catalog, catalog_len, Q_CATALOG_NZ_CODE);
- /*
- In 5.0.x where x<4 masters we used to store the end zero here. This was
- a waste of one byte so we don't do it in x>=4 masters. We change code to
- Q_CATALOG_NZ_CODE, because re-using the old code would make x<4 slaves
- of this x>=4 master segfault (expecting a zero when there is
- none). Remaining compatibility problems are: the older slave will not
- find the catalog; but it is will not crash, and it's not an issue
- that it does not find the catalog as catalogs were not used in these
- older MySQL versions (we store it in binlog and read it from relay log
- but do nothing useful with it). What is an issue is that the older slave
- will stop processing the Q_* blocks (and jumps to the db/query) as soon
- as it sees unknown Q_CATALOG_NZ_CODE; so it will not be able to read
- Q_AUTO_INCREMENT*, Q_CHARSET and so replication will fail silently in
- various ways. Documented that you should not mix alpha/beta versions if
- they are not exactly the same version, with example of 5.0.3->5.0.2 and
- 5.0.4->5.0.3. If replication is from older to new, the new will
- recognize Q_CATALOG_CODE and have no problem.
- */
- }
- if (auto_increment_increment != 1 || auto_increment_offset != 1)
- {
- *start++= Q_AUTO_INCREMENT;
- int2store(start, auto_increment_increment);
- int2store(start+2, auto_increment_offset);
- start+= 4;
- }
- if (charset_inited)
- {
- *start++= Q_CHARSET_CODE;
- memcpy(start, charset, 6);
- start+= 6;
- }
- if (time_zone_len)
- {
- /* In the TZ sys table, column Name is of length 64 so this should be ok */
- DBUG_ASSERT(time_zone_len <= MAX_TIME_ZONE_NAME_LENGTH);
- store_str_with_code_and_len(&start,
- time_zone_str, time_zone_len, Q_TIME_ZONE_CODE);
- }
- if (lc_time_names_number)
- {
- DBUG_ASSERT(lc_time_names_number <= 0xFFFF);
- *start++= Q_LC_TIME_NAMES_CODE;
- int2store(start, lc_time_names_number);
- start+= 2;
- }
- if (charset_database_number)
- {
- DBUG_ASSERT(charset_database_number <= 0xFFFF);
- *start++= Q_CHARSET_DATABASE_CODE;
- int2store(start, charset_database_number);
- start+= 2;
- }
- if (table_map_for_update)
- {
- *start++= Q_TABLE_MAP_FOR_UPDATE_CODE;
- int8store(start, table_map_for_update);
- start+= 8;
- }
- if (master_data_written != 0)
- {
- /*
- Q_MASTER_DATA_WRITTEN_CODE only exists in relay logs where the master
- has binlog_version<4 and the slave has binlog_version=4. See comment
- for master_data_written in log_event.h for details.
- */
- *start++= Q_MASTER_DATA_WRITTEN_CODE;
- int4store(start, master_data_written);
- start+= 4;
- }
-
- if (thd && thd->need_binlog_invoker())
- {
- LEX_CSTRING user;
- LEX_CSTRING host;
- memset(&user, 0, sizeof(user));
- memset(&host, 0, sizeof(host));
-
- if (thd->slave_thread && thd->has_invoker())
- {
- /* user will be null, if master is older than this patch */
- user= thd->get_invoker_user();
- host= thd->get_invoker_host();
- }
- else
- {
- Security_context *ctx= thd->security_ctx;
-
- if (thd->need_binlog_invoker() == THD::INVOKER_USER)
- {
- user.str= ctx->priv_user;
- host.str= ctx->priv_host;
- host.length= strlen(host.str);
- }
- else
- {
- user.str= ctx->priv_role;
- host= empty_clex_str;
- }
- user.length= strlen(user.str);
- }
-
- if (user.length > 0)
- {
- *start++= Q_INVOKER;
-
- /*
- Store user length and user. The max length of use is 16, so 1 byte is
- enough to store the user's length.
- */
- *start++= (uchar)user.length;
- memcpy(start, user.str, user.length);
- start+= user.length;
-
- /*
- Store host length and host. The max length of host is 60, so 1 byte is
- enough to store the host's length.
- */
- *start++= (uchar)host.length;
- memcpy(start, host.str, host.length);
- start+= host.length;
- }
- }
-
- if (thd && thd->query_start_sec_part_used)
- {
- *start++= Q_HRNOW;
- get_time();
- int3store(start, when_sec_part);
- start+= 3;
- }
- /*
- NOTE: When adding new status vars, please don't forget to update
- the MAX_SIZE_LOG_EVENT_STATUS in log_event.h and update the function
- code_name() in this file.
-
- Here there could be code like
- if (command-line-option-which-says-"log_this_variable" && inited)
- {
- *start++= Q_THIS_VARIABLE_CODE;
- int4store(start, this_variable);
- start+= 4;
- }
- */
-
- /* Store length of status variables */
- status_vars_len= (uint) (start-start_of_status);
- DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS);
- int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len);
-
- /*
- Calculate length of whole event
- The "1" below is the \0 in the db's length
- */
- event_length= (uint) (start-buf) + get_post_header_size_for_derived() + db_len + 1 + q_len;
-
- return write_header(event_length) ||
- write_data(buf, QUERY_HEADER_LEN) ||
- write_post_header_for_derived() ||
- write_data(start_of_status, (uint) (start-start_of_status)) ||
- write_data(safe_str(db), db_len + 1) ||
- write_data(query, q_len) ||
- write_footer();
-}
-
-bool Query_compressed_log_event::write()
-{
- const char *query_tmp = query;
- uint32 q_len_tmp = q_len;
- uint32 alloc_size;
- bool ret = true;
- q_len = alloc_size = binlog_get_compress_len(q_len);
- query = (char *)my_safe_alloca(alloc_size);
- if(query && !binlog_buf_compress(query_tmp, (char *)query, q_len_tmp, &q_len))
- {
- ret = Query_log_event::write();
- }
- my_safe_afree((void *)query, alloc_size);
- query = query_tmp;
- q_len = q_len_tmp;
- return ret;
-}
-
-/**
- The simplest constructor that could possibly work. This is used for
- creating static objects that have a special meaning and are invisible
- to the log.
-*/
-Query_log_event::Query_log_event()
- :Log_event(), data_buf(0)
-{
- memset(&user, 0, sizeof(user));
- memset(&host, 0, sizeof(host));
-}
-
-
-/*
- SYNOPSIS
- Query_log_event::Query_log_event()
- thd_arg - thread handle
- 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
-
- DESCRIPTION
- Creates an event for binlogging
- The value for `errcode' should be supplied by caller.
-*/
-Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t query_length, bool using_trans,
- bool direct, bool suppress_use, int errcode)
-
- :Log_event(thd_arg,
- (thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F :
- 0) |
- (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
- using_trans),
- data_buf(0), query(query_arg), catalog(thd_arg->catalog),
- db(thd_arg->db.str), q_len((uint32) query_length),
- thread_id(thd_arg->thread_id),
- /* save the original thread id; we already know the server id */
- slave_proxy_id((ulong)thd_arg->variables.pseudo_thread_id),
- flags2_inited(1), sql_mode_inited(1), charset_inited(1),
- sql_mode(thd_arg->variables.sql_mode),
- auto_increment_increment(thd_arg->variables.auto_increment_increment),
- auto_increment_offset(thd_arg->variables.auto_increment_offset),
- lc_time_names_number(thd_arg->variables.lc_time_names->number),
- charset_database_number(0),
- table_map_for_update((ulonglong)thd_arg->table_map_for_update),
- master_data_written(0)
-{
- time_t end_time;
-
-#ifdef WITH_WSREP
- /*
- If Query_log_event will contain non trans keyword (not BEGIN, COMMIT,
- SAVEPOINT or ROLLBACK) we disable PA for this transaction.
- Note that here WSREP(thd) might not be true e.g. when wsrep_shcema
- is created we create tables with thd->variables.wsrep_on=false
- to avoid replicating wsrep_schema tables to other nodes.
- */
- if (WSREP_ON && !is_trans_keyword())
- {
- thd->wsrep_PA_safe= false;
- }
-#endif /* WITH_WSREP */
-
- memset(&user, 0, sizeof(user));
- memset(&host, 0, sizeof(host));
-
- error_code= errcode;
-
- end_time= my_time(0);
- exec_time = (ulong) (end_time - thd_arg->start_time);
- /**
- @todo this means that if we have no catalog, then it is replicated
- as an existing catalog of length zero. is that safe? /sven
- */
- catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
- /* status_vars_len is set just before writing the event */
- db_len = (db) ? (uint32) strlen(db) : 0;
- if (thd_arg->variables.collation_database != thd_arg->db_charset)
- charset_database_number= thd_arg->variables.collation_database->number;
-
- /*
- We only replicate over the bits of flags2 that we need: the rest
- are masked out by "& OPTIONS_WRITTEN_TO_BINLOG".
-
- We also force AUTOCOMMIT=1. Rationale (cf. BUG#29288): After
- fixing BUG#26395, we always write BEGIN and COMMIT around all
- transactions (even single statements in autocommit mode). This is
- so that replication from non-transactional to transactional table
- and error recovery from XA to non-XA table should work as
- expected. The BEGIN/COMMIT are added in log.cc. However, there is
- one exception: MyISAM bypasses log.cc and writes directly to the
- binlog. So if autocommit is off, master has MyISAM, and slave has
- a transactional engine, then the slave will just see one long
- never-ending transaction. The only way to bypass explicit
- BEGIN/COMMIT in the binlog is by using a non-transactional table.
- So setting AUTOCOMMIT=1 will make this work as expected.
-
- Note: explicitly replicate AUTOCOMMIT=1 from master. We do not
- assume AUTOCOMMIT=1 on slave; the slave still reads the state of
- the autocommit flag as written by the master to the binlog. This
- behavior may change after WL#4162 has been implemented.
- */
- flags2= (uint32) (thd_arg->variables.option_bits &
- (OPTIONS_WRITTEN_TO_BIN_LOG & ~OPTION_NOT_AUTOCOMMIT));
- DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256);
- DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256);
- DBUG_ASSERT(thd_arg->variables.collation_server->number < 256*256);
- DBUG_ASSERT(thd_arg->variables.character_set_client->mbminlen == 1);
- int2store(charset, thd_arg->variables.character_set_client->number);
- int2store(charset+2, thd_arg->variables.collation_connection->number);
- int2store(charset+4, thd_arg->variables.collation_server->number);
- if (thd_arg->time_zone_used)
- {
- /*
- Note that our event becomes dependent on the Time_zone object
- representing the time zone. Fortunately such objects are never deleted
- or changed during mysqld's lifetime.
- */
- time_zone_len= thd_arg->variables.time_zone->get_name()->length();
- time_zone_str= thd_arg->variables.time_zone->get_name()->ptr();
- }
- else
- time_zone_len= 0;
-
- LEX *lex= thd->lex;
- /*
- Defines that the statement will be written directly to the binary log
- without being wrapped by a BEGIN...COMMIT. Otherwise, the statement
- will be written to either the trx-cache or stmt-cache.
-
- Note that a cache will not be used if the parameter direct is TRUE.
- */
- bool use_cache= FALSE;
- /*
- TRUE defines that the trx-cache must be used and by consequence the
- use_cache is TRUE.
-
- Note that a cache will not be used if the parameter direct is TRUE.
- */
- bool trx_cache= FALSE;
- cache_type= Log_event::EVENT_INVALID_CACHE;
-
- if (!direct)
- {
- switch (lex->sql_command)
- {
- case SQLCOM_DROP_TABLE:
- case SQLCOM_DROP_SEQUENCE:
- use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode());
- break;
-
- case SQLCOM_CREATE_TABLE:
- case SQLCOM_CREATE_SEQUENCE:
- /*
- 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->first_select_lex()->item_list.elements &&
- thd->is_current_stmt_binlog_format_row()) ||
- (thd->variables.option_bits & OPTION_GTID_BEGIN);
- use_cache= (lex->tmp_table() &&
- thd->in_multi_stmt_transaction_mode()) || trx_cache;
- break;
- case SQLCOM_SET_OPTION:
- if (lex->autocommit)
- use_cache= trx_cache= FALSE;
- else
- use_cache= TRUE;
- break;
- case SQLCOM_RELEASE_SAVEPOINT:
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- case SQLCOM_SAVEPOINT:
- use_cache= trx_cache= TRUE;
- break;
- default:
- use_cache= sqlcom_can_generate_row_events(thd);
- break;
- }
- }
-
- if (!use_cache || direct)
- {
- cache_type= Log_event::EVENT_NO_CACHE;
- }
- else if (using_trans || trx_cache || stmt_has_updated_trans_table(thd) ||
- thd->lex->is_mixed_stmt_unsafe(thd->in_multi_stmt_transaction_mode(),
- thd->variables.binlog_direct_non_trans_update,
- trans_has_updated_trans_table(thd),
- thd->tx_isolation))
- cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
- 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 cache_tye: %d",
- (ulong) flags2, sql_mode, cache_type));
-}
-
-Query_compressed_log_event::Query_compressed_log_event(THD* thd_arg, const char* query_arg,
- ulong query_length, bool using_trans,
- bool direct, bool suppress_use, int errcode)
- :Query_log_event(thd_arg, query_arg, query_length, using_trans, direct,
- suppress_use, errcode),
- query_buf(0)
-{
-
-}
-#endif /* MYSQL_CLIENT */
/* 2 utility functions for the next method */
@@ -4530,7 +1348,7 @@ get_str_len_and_pointer(const Log_event::Byte **src,
const Log_event::Byte *end)
{
if (*src >= end)
- return -1; // Will be UINT_MAX in two-complement arithmetic
+ return -1; // Will be UINT_MAX in two-complement arithmetics
uint length= **src;
if (length > 0)
{
@@ -4617,7 +1435,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
Log_event_type event_type)
:Log_event(buf, description_event), data_buf(0), query(NullS),
db(NullS), catalog_len(0), status_vars_len(0),
- flags2_inited(0), sql_mode_inited(0), charset_inited(0),
+ flags2_inited(0), sql_mode_inited(0), charset_inited(0), flags2(0),
auto_increment_increment(1), auto_increment_offset(1),
time_zone_len(0), lc_time_names_number(0), charset_database_number(0),
table_map_for_update(0), master_data_written(0)
@@ -4853,7 +1671,8 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
- if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1
+ if (!(start= data_buf = (Log_event::Byte*) my_malloc(PSI_INSTRUMENT_ME,
+ catalog_len + 1
+ time_zone_len + 1
+ user.length + 1
+ host.length + 1
@@ -4864,7 +1683,8 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
+ QUERY_CACHE_FLAGS_SIZE,
MYF(MY_WME))))
#else
- if (!(start= data_buf = (Log_event::Byte*) my_malloc(catalog_len + 1
+ if (!(start= data_buf = (Log_event::Byte*) my_malloc(PSI_INSTRUMENT_ME,
+ catalog_len + 1
+ time_zone_len + 1
+ user.length + 1
+ host.length + 1
@@ -4918,7 +1738,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
/* A 2nd variable part; this is common to all versions */
memcpy((char*) start, end, data_len); // Copy db and query
- start[data_len]= '\0'; // End query with \0 (For safety)
+ start[data_len]= '\0'; // End query with \0 (For safetly)
db= (char *)start;
query= (char *)(start + db_len + 1);
q_len= data_len - db_len -1;
@@ -4971,8 +1791,8 @@ Query_compressed_log_event::Query_compressed_log_event(const char *buf,
}
/* Reserve one byte for '\0' */
- query_buf = (Log_event::Byte*)my_malloc(ALIGN_SIZE(un_len + 1),
- MYF(MY_WME));
+ query_buf = (Log_event::Byte*)my_malloc(PSI_INSTRUMENT_ME,
+ ALIGN_SIZE(un_len + 1), MYF(MY_WME));
if(query_buf &&
!binlog_buf_uncompress(query, (char *)query_buf, q_len, &un_len))
{
@@ -5161,961 +1981,10 @@ Query_log_event::begin_event(String *packet, ulong ev_offset,
}
-#ifdef MYSQL_CLIENT
-/**
- Query_log_event::print().
-
- @todo
- print the catalog ??
-*/
-bool Query_log_event::print_query_header(IO_CACHE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- // TODO: print the catalog ??
- char buff[64], *end; // Enough for SET TIMESTAMP
- bool different_db= 1;
- uint32 tmp;
-
- if (!print_event_info->short_form)
- {
- if (print_header(file, print_event_info, FALSE) ||
- my_b_printf(file,
- "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n",
- get_type_str(), (ulong) thread_id, (ulong) exec_time,
- error_code))
- goto err;
- }
-
- if ((flags & LOG_EVENT_SUPPRESS_USE_F))
- {
- if (!is_trans_keyword())
- print_event_info->db[0]= '\0';
- }
- else if (db)
- {
- different_db= memcmp(print_event_info->db, db, db_len + 1);
- if (different_db)
- memcpy(print_event_info->db, db, db_len + 1);
- if (db[0] && different_db)
- if (my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter))
- goto err;
- }
-
- end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
- if (when_sec_part && when_sec_part <= TIME_MAX_SECOND_PART)
- {
- *end++= '.';
- end=int10_to_str(when_sec_part, end, 10);
- }
- end= strmov(end, print_event_info->delimiter);
- *end++='\n';
- if (my_b_write(file, (uchar*) buff, (uint) (end-buff)))
- goto err;
- if ((!print_event_info->thread_id_printed ||
- ((flags & LOG_EVENT_THREAD_SPECIFIC_F) &&
- thread_id != print_event_info->thread_id)))
- {
- // If --short-form, print deterministic value instead of pseudo_thread_id.
- if (my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
- short_form ? 999999999 : (ulong)thread_id,
- print_event_info->delimiter))
- goto err;
- print_event_info->thread_id= thread_id;
- print_event_info->thread_id_printed= 1;
- }
-
- /*
- If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to
- print (remember we don't produce mixed relay logs so there cannot be
- 5.0 events before that one so there is nothing to reset).
- */
- if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */
- {
- /* tmp is a bitmask of bits which have changed. */
- if (likely(print_event_info->flags2_inited))
- /* All bits which have changed */
- tmp= (print_event_info->flags2) ^ flags2;
- else /* that's the first Query event we read */
- {
- print_event_info->flags2_inited= 1;
- tmp= ~((uint32)0); /* all bits have changed */
- }
-
- if (unlikely(tmp)) /* some bits have changed */
- {
- bool need_comma= 0;
- if (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,
- "@@session.sql_auto_is_null", &need_comma) ||
- print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2,
- "@@session.unique_checks", &need_comma) ||
- print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2,
- "@@session.autocommit", &need_comma) ||
- print_set_option(file, tmp, OPTION_NO_CHECK_CONSTRAINT_CHECKS,
- ~flags2,
- "@@session.check_constraint_checks", &need_comma) ||
- my_b_printf(file,"%s\n", print_event_info->delimiter))
- goto err;
- print_event_info->flags2= flags2;
- }
- }
-
- /*
- Now the session variables;
- it's more efficient to pass SQL_MODE as a number instead of a
- comma-separated list.
- FOREIGN_KEY_CHECKS, SQL_AUTO_IS_NULL, UNIQUE_CHECKS are session-only
- variables (they have no global version; they're not listed in
- sql_class.h), The tests below work for pure binlogs or pure relay
- logs. Won't work for mixed relay logs but we don't create mixed
- relay logs (that is, there is no relay log with a format change
- except within the 3 first events, which mysqlbinlog handles
- gracefully). So this code should always be good.
- */
-
- if (likely(sql_mode_inited) &&
- (unlikely(print_event_info->sql_mode != sql_mode ||
- !print_event_info->sql_mode_inited)))
- {
- char llbuff[22];
- if (my_b_printf(file,"SET @@session.sql_mode=%s%s\n",
- ullstr(sql_mode, llbuff), print_event_info->delimiter))
- goto err;
- print_event_info->sql_mode= sql_mode;
- print_event_info->sql_mode_inited= 1;
- }
- if (print_event_info->auto_increment_increment != auto_increment_increment ||
- print_event_info->auto_increment_offset != auto_increment_offset)
- {
- if (my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n",
- auto_increment_increment,auto_increment_offset,
- print_event_info->delimiter))
- goto err;
- print_event_info->auto_increment_increment= auto_increment_increment;
- print_event_info->auto_increment_offset= auto_increment_offset;
- }
-
- /* TODO: print the catalog when we feature SET CATALOG */
-
- if (likely(charset_inited) &&
- (unlikely(!print_event_info->charset_inited ||
- memcmp(print_event_info->charset, charset, 6))))
- {
- CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
- if (cs_info)
- {
- /* for mysql client */
- if (my_b_printf(file, "/*!\\C %s */%s\n",
- cs_info->csname, print_event_info->delimiter))
- goto err;
- }
- if (my_b_printf(file,"SET "
- "@@session.character_set_client=%d,"
- "@@session.collation_connection=%d,"
- "@@session.collation_server=%d"
- "%s\n",
- uint2korr(charset),
- uint2korr(charset+2),
- uint2korr(charset+4),
- print_event_info->delimiter))
- goto err;
- memcpy(print_event_info->charset, charset, 6);
- print_event_info->charset_inited= 1;
- }
- if (time_zone_len)
- {
- if (memcmp(print_event_info->time_zone_str,
- time_zone_str, time_zone_len+1))
- {
- if (my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
- time_zone_str, print_event_info->delimiter))
- goto err;
- memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
- }
- }
- if (lc_time_names_number != print_event_info->lc_time_names_number)
- {
- if (my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
- lc_time_names_number, print_event_info->delimiter))
- goto err;
- print_event_info->lc_time_names_number= lc_time_names_number;
- }
- if (charset_database_number != print_event_info->charset_database_number)
- {
- if (charset_database_number)
- {
- if (my_b_printf(file, "SET @@session.collation_database=%d%s\n",
- charset_database_number, print_event_info->delimiter))
- goto err;
- }
- else if (my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
- print_event_info->delimiter))
- goto err;
- print_event_info->charset_database_number= charset_database_number;
- }
- return 0;
-
-err:
- return 1;
-}
-
-
-bool Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
-{
- Write_on_release_cache cache(&print_event_info->head_cache, file, 0, this);
-
- /**
- reduce the size of io cache so that the write function is called
- for every call to my_b_write().
- */
- DBUG_EXECUTE_IF ("simulate_file_write_error",
- {(&cache)->write_pos= (&cache)->write_end- 500;});
- if (print_query_header(&cache, print_event_info))
- goto err;
- if (!is_flashback)
- {
- if (my_b_write(&cache, (uchar*) query, q_len) ||
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter))
- goto err;
- }
- else // is_flashback == 1
- {
- if (strcmp("BEGIN", query) == 0)
- {
- if (my_b_write(&cache, (uchar*) "COMMIT", 6) ||
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter))
- goto err;
- }
- else if (strcmp("COMMIT", query) == 0)
- {
- if (my_b_printf(&cache, "START TRANSACTION\n%s\n", print_event_info->delimiter))
- goto err;
- }
- }
- return cache.flush_data();
-err:
- return 1;
-}
-#endif /* MYSQL_CLIENT */
-
-
-/*
- Query_log_event::do_apply_event()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-int Query_log_event::do_apply_event(rpl_group_info *rgi)
-{
- return do_apply_event(rgi, query, q_len);
-}
-
-/**
- Compare if two errors should be regarded as equal.
- This is to handle the case when you can get slightly different errors
- on master and slave for the same thing.
- @param
- expected_error Error we got on master
- actual_error Error we got on slave
-
- @return
- 1 Errors are equal
- 0 Errors are different
-*/
-
-bool test_if_equal_repl_errors(int expected_error, int actual_error)
-{
- if (expected_error == actual_error)
- return 1;
- switch (expected_error) {
- case ER_DUP_ENTRY:
- case ER_DUP_ENTRY_WITH_KEY_NAME:
- case ER_DUP_KEY:
- case ER_AUTOINC_READ_FAILED:
- return (actual_error == ER_DUP_ENTRY ||
- actual_error == ER_DUP_ENTRY_WITH_KEY_NAME ||
- actual_error == ER_DUP_KEY ||
- 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;
- }
- return 0;
-}
-
-
-/**
- @todo
- Compare the values of "affected rows" around here. Something
- like:
- @code
- if ((uint32) affected_in_event != (uint32) affected_on_slave)
- {
- sql_print_error("Slave: did not get the expected number of affected \
- rows running query from master - expected %d, got %d (this numbers \
- should have matched modulo 4294967296).", 0, ...);
- thd->query_error = 1;
- }
- @endcode
- We may also want an option to tell the slave to ignore "affected"
- 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(rpl_group_info *rgi,
- const char *query_arg, uint32 q_len_arg)
-{
- int expected_error,actual_error= 0;
- Schema_specification_st db_options;
- uint64 sub_id= 0;
- void *hton= NULL;
- rpl_gtid gtid;
- Relay_log_info const *rli= rgi->rli;
- Rpl_filter *rpl_filter= rli->mi->rpl_filter;
- bool current_stmt_is_commit;
- DBUG_ENTER("Query_log_event::do_apply_event");
-
- /*
- Colleagues: please never free(thd->catalog) in MySQL. This would
- lead to bugs as here thd->catalog is a part of an alloced block,
- not an entire alloced block (see
- Query_log_event::do_apply_event()). Same for thd->db. Thank
- you.
- */
- thd->catalog= catalog_len ? (char *) catalog : (char *)"";
-
- size_t valid_len= Well_formed_prefix(system_charset_info,
- db, db_len, NAME_LEN).length();
-
- if (valid_len != db_len)
- {
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Invalid database name in Query event.");
- thd->is_slave_error= true;
- goto end;
- }
-
- set_thd_db(thd, rpl_filter, db, db_len);
-
- /*
- Setting the character set and collation of the current database thd->db.
- */
- load_db_opt_by_name(thd, thd->db.str, &db_options);
- if (db_options.default_table_charset)
- thd->db_charset= db_options.default_table_charset;
- thd->variables.auto_increment_increment= auto_increment_increment;
- thd->variables.auto_increment_offset= auto_increment_offset;
-
- DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
-
- thd->clear_error(1);
- current_stmt_is_commit= is_commit();
-
- DBUG_ASSERT(!current_stmt_is_commit || !rgi->tables_to_lock);
- rgi->slave_close_thread_tables(thd);
-
- /*
- Note: We do not need to execute reset_one_shot_variables() if this
- db_ok() test fails.
- Reason: The db stored in binlog events is the same for SET and for
- its companion query. If the SET is ignored because of
- db_ok(), the companion query will also be ignored, and if
- the companion query is ignored in the db_ok() test of
- ::do_apply_event(), then the companion SET also have so
- we don't need to reset_one_shot_variables().
- */
- if (is_trans_keyword() || rpl_filter->db_ok(thd->db.str))
- {
- thd->set_time(when, when_sec_part);
- thd->set_query_and_id((char*)query_arg, q_len_arg,
- thd->charset(), next_query_id());
- thd->variables.pseudo_thread_id= thread_id; // for temp tables
- DBUG_PRINT("query",("%s", thd->query()));
-
- if (unlikely(!(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
- must take their value from flags2.
- */
- thd->variables.option_bits= flags2|(thd->variables.option_bits & ~OPTIONS_WRITTEN_TO_BIN_LOG);
- /*
- else, we are in a 3.23/4.0 binlog; we previously received a
- Rotate_log_event which reset thd->variables.option_bits and sql_mode etc, so
- nothing to do.
- */
- /*
- 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
- 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)
- thd->variables.sql_mode=
- (sql_mode_t) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) |
- (sql_mode & ~(sql_mode_t) MODE_NO_DIR_IN_CREATE));
- if (charset_inited)
- {
- 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=
- get_charset(uint2korr(charset), MYF(MY_WME))) ||
- !(thd->variables.collation_connection=
- get_charset(uint2korr(charset+2), MYF(MY_WME))) ||
- !(thd->variables.collation_server=
- get_charset(uint2korr(charset+4), MYF(MY_WME))))
- {
- /*
- We updated the thd->variables with nonsensical values (0). Let's
- set them to something safe (i.e. which avoids crash), and we'll
- stop with EE_UNKNOWN_CHARSET in compare_errors (unless set to
- ignore this error).
- */
- set_slave_thread_default_charset(thd, rgi);
- goto compare_errors;
- }
- thd->update_charset(); // for the charset change to take effect
- /*
- Reset thd->query_string.cs to the newly set value.
- Note, there is a small flaw here. For a very short time frame
- if the new charset is different from the old charset and
- if another thread executes "SHOW PROCESSLIST" after
- the above thd->set_query_and_id() and before this thd->set_query(),
- and if the current query has some non-ASCII characters,
- the another thread may see some '?' marks in the PROCESSLIST
- result. This should be acceptable now. This is a reminder
- to fix this if any refactoring happens here sometime.
- */
- thd->set_query((char*) query_arg, q_len_arg, thd->charset());
- }
- }
- if (time_zone_len)
- {
- String tmp(time_zone_str, time_zone_len, &my_charset_bin);
- if (!(thd->variables.time_zone= my_tz_find(thd, &tmp)))
- {
- my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
- thd->variables.time_zone= global_system_variables.time_zone;
- goto compare_errors;
- }
- }
- if (lc_time_names_number)
- {
- if (!(thd->variables.lc_time_names=
- my_locale_by_number(lc_time_names_number)))
- {
- my_printf_error(ER_UNKNOWN_ERROR,
- "Unknown locale: '%d'", MYF(0), lc_time_names_number);
- thd->variables.lc_time_names= &my_locale_en_US;
- goto compare_errors;
- }
- }
- else
- thd->variables.lc_time_names= &my_locale_en_US;
- if (charset_database_number)
- {
- CHARSET_INFO *cs;
- if (!(cs= get_charset(charset_database_number, MYF(0))))
- {
- char buf[20];
- int10_to_str((int) charset_database_number, buf, -10);
- my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);
- goto compare_errors;
- }
- thd->variables.collation_database= cs;
- }
- else
- thd->variables.collation_database= thd->db_charset;
-
- {
- const CHARSET_INFO *cs= thd->charset();
- /*
- We cannot ask for parsing a statement using a character set
- without state_maps (parser internal data).
- */
- if (!cs->state_map)
- {
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "character_set cannot be parsed");
- thd->is_slave_error= true;
- goto end;
- }
- }
-
- /*
- 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 (unlikely(rpl_global_gtid_slave_state->record_gtid(thd, &gtid,
- sub_id,
- true, false,
- &hton)))
- {
- 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);
- /*
- Flag if we need to rollback the statement transaction on
- slave if it by chance succeeds.
- If we expected a non-zero error code and get nothing and,
- it is a concurrency issue or ignorable issue, effects
- of the statement should be rolled back.
- */
- if (unlikely(expected_error) &&
- (ignored_error_code(expected_error) ||
- 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.str, 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);
-
- if (thd->slave_thread)
- {
- /*
- To be compatible with previous releases, the slave thread uses the global
- log_slow_disabled_statements value, wich can be changed dynamically, so we
- have to set the sql_log_slow respectively.
- */
- thd->variables.sql_log_slow= !MY_TEST(global_system_variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE);
- }
-
- mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
- FALSE, FALSE);
- /* Finalize server status flags after executing a statement. */
- thd->update_server_status();
- log_slow_statement(thd);
- thd->lex->restore_set_statement_var();
- }
-
- thd->variables.option_bits&= ~OPTION_MASTER_SQL_ERROR;
- }
- else
- {
- /*
- The query got a really bad error on the master (thread killed etc),
- which could be inconsistent. Parse it to test the table names: if the
- replicate-*-do|ignore-table rules say "this query must be ignored" then
- we exit gracefully; otherwise we warn about the bad error and tell DBA
- to check/fix it.
- */
- if (mysql_test_parse_for_slave(thd, thd->query(), thd->query_length()))
- thd->clear_error(1);
- else
- {
- 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 \
-point. If you are sure that your master is ok, run this query manually on the \
-slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; \
-START SLAVE; . Query: '%s'", expected_error, thd->query());
- thd->is_slave_error= 1;
- }
- goto end;
- }
-
- /* If the query was not ignored, it is printed to the general log */
- if (likely(!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
- {
- /*
- Bug#54201: If we skip an INSERT query that uses auto_increment, then we
- should reset any @@INSERT_ID set by an Intvar_log_event associated with
- the query; otherwise the @@INSERT_ID will linger until the next INSERT
- that uses auto_increment and may affect extra triggers on the slave etc.
-
- We reset INSERT_ID unconditionally; it is probably cheaper than
- checking if it is necessary.
- */
- thd->auto_inc_intervals_forced.empty();
- }
-
-compare_errors:
- /*
- In the slave thread, we may sometimes execute some DROP / * 40005
- TEMPORARY * / TABLE that come from parts of binlogs (likely if we
- use RESET SLAVE or CHANGE MASTER TO), while the temporary table
- has already been dropped. To ignore such irrelevant "table does
- not exist errors", we silently clear the error if TEMPORARY was used.
- */
- if ((thd->lex->sql_command == SQLCOM_DROP_TABLE ||
- thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) &&
- thd->lex->tmp_table() &&
- thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR &&
- !expected_error)
- 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->get_stmt_da()->sql_errno() : 0;
- DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
- expected_error, actual_error));
-
- if ((unlikely(expected_error) &&
- !test_if_equal_repl_errors(expected_error, actual_error) &&
- !concurrency_error_code(expected_error)) &&
- !ignored_error_code(actual_error) &&
- !ignored_error_code(expected_error))
- {
- 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_THD(thd, expected_error),
- expected_error,
- actual_error ? thd->get_stmt_da()->message() : "no error",
- actual_error,
- print_slave_db_safe(db), query_arg);
- thd->is_slave_error= 1;
- }
- /*
- If we get the same error code as expected and it is not a concurrency
- issue, or should be ignored.
- */
- else if ((test_if_equal_repl_errors(expected_error, actual_error) &&
- !concurrency_error_code(expected_error)) ||
- ignored_error_code(actual_error))
- {
- DBUG_PRINT("info",("error ignored"));
- thd->clear_error(1);
- 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 (unlikely(thd->is_slave_error || thd->is_fatal_error))
- {
- 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"),
- thd->get_db(), query_arg);
- thd->is_slave_error= 1;
-#ifdef WITH_WSREP
- if (wsrep_thd_is_toi(thd) && wsrep_must_ignore_error(thd))
- {
- thd->clear_error(1);
- thd->killed= NOT_KILLED;
- thd->wsrep_has_ignored_error= true;
- }
-#endif /* WITH_WSREP */
- }
-
- /*
- TODO: compare the values of "affected rows" around here. Something
- like:
- if ((uint32) affected_in_event != (uint32) affected_on_slave)
- {
- sql_print_error("Slave: did not get the expected number of affected \
- rows running query from master - expected %d, got %d (this numbers \
- should have matched modulo 4294967296).", 0, ...);
- thd->is_slave_error = 1;
- }
- We may also want an option to tell the slave to ignore "affected"
- mismatch. This mismatch could be implemented with a new ER_ code, and
- to ignore it you would use --slave-skip-errors...
-
- To do the comparison we need to know the value of "affected" which the
- above mysql_parse() computed. And we need to know the value of
- "affected" in the master's binlog. Both will be implemented later. The
- important thing is that we now have the format ready to log the values
- of "affected" in the binlog. So we can release 5.0.0 before effectively
- logging "affected" and effectively comparing it.
- */
- } /* End of if (db_ok(... */
-
- {
- /**
- The following failure injecion works in cooperation with tests
- setting @@global.debug= 'd,stop_slave_middle_group'.
- The sql thread receives the killed status and will proceed
- to shutdown trying to finish incomplete events group.
- */
- DBUG_EXECUTE_IF("stop_slave_middle_group",
- 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;
- };);
- }
-
-end:
- if (unlikely(sub_id && !thd->is_slave_error))
- rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, 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
- probably, so data_buf will be freed, so the thd->... listed above will be
- pointers to freed memory.
- So we must set them to 0, so that those bad pointers values are not later
- used. Note that "cleanup" queries like automatic DROP TEMPORARY TABLE
- don't suffer from these assignments to 0 as DROP TEMPORARY
- TABLE uses the db.table syntax.
- */
- thd->catalog= 0;
- thd->set_db(&null_clex_str); /* 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
- to replace the THD::stmt_depends_on_first_successful_insert_id_in_prev_stmt
- variable by (THD->first_successful_insert_id_in_prev_stmt > 0) ; with the
- resetting below we are ready to support that.
- */
- thd->first_successful_insert_id_in_prev_stmt_for_binlog= 0;
- thd->first_successful_insert_id_in_prev_stmt= 0;
- thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- DBUG_RETURN(thd->is_slave_error);
-}
-
-Log_event::enum_skip_reason
-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_ASSERT(query && q_len > 0);
- DBUG_ASSERT(thd == rgi->thd);
-
- /*
- 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)
- DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
-
- if (rli->slave_skip_counter > 0)
- {
- if (is_begin())
- {
- thd->variables.option_bits|= OPTION_BEGIN | OPTION_GTID_BEGIN;
- DBUG_RETURN(Log_event::continue_group(rgi));
- }
-
- if (is_commit() || is_rollback())
- {
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
- DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
- }
- }
-#ifdef WITH_WSREP
- else if (WSREP(thd) && wsrep_mysql_replication_bundle && opt_slave_domain_parallel_threads == 0 &&
- thd->wsrep_mysql_replicated > 0 &&
- (is_begin() || is_commit()))
- {
- if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
- {
- WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
- DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
- }
- else
- {
- thd->wsrep_mysql_replicated = 0;
- }
- }
-#endif /* WITH_WSREP */
- DBUG_RETURN(Log_event::do_shall_skip(rgi));
-}
-
-
-bool
-Query_log_event::peek_is_commit_rollback(const char *event_start,
- size_t event_len,
- enum enum_binlog_checksum_alg 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
-
-
/**************************************************************************
Start_log_event_v3 methods
**************************************************************************/
-#ifndef MYSQL_CLIENT
-Start_log_event_v3::Start_log_event_v3()
- :Log_event(), created(0), binlog_version(BINLOG_VERSION),
- dont_set_created(0)
-{
- memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
-}
-#endif
-
-/*
- Start_log_event_v3::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Start_log_event_v3::pack_info(Protocol *protocol)
-{
- char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos;
- pos= strmov(buf, "Server ver: ");
- pos= strmov(pos, server_version);
- pos= strmov(pos, ", Binlog ver: ");
- pos= int10_to_str(binlog_version, pos, 10);
- protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
-}
-#endif
-
-
-/*
- Start_log_event_v3::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
-{
- DBUG_ENTER("Start_log_event_v3::print");
-
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F);
-
- if (!print_event_info->short_form)
- {
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ",
- binlog_version, server_version) ||
- print_timestamp(&cache))
- goto err;
- if (created)
- if (my_b_printf(&cache," at startup"))
- goto err;
- if (my_b_printf(&cache, "\n"))
- goto err;
- if (flags & LOG_EVENT_BINLOG_IN_USE_F)
- if (my_b_printf(&cache,
- "# Warning: this binlog is either in use or was not "
- "closed properly.\n"))
- goto err;
- }
- if (!is_artificial_event() && created)
- {
-#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
- /*
- This is for mysqlbinlog: like in replication, we want to delete the stale
- tmp files left by an unclean shutdown of mysqld (temporary tables)
- and rollback unfinished transaction.
- Probably this can be done with RESET CONNECTION (syntax to be defined).
- */
- if (my_b_printf(&cache,"RESET CONNECTION%s\n",
- print_event_info->delimiter))
- goto err;
-#else
- if (my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter))
- goto err;
-#endif
- }
- if (temp_buf &&
- print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
- !print_event_info->short_form)
- {
- /* BINLOG is matched with the delimiter below on the same level */
- bool do_print_encoded=
- print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS;
- if (do_print_encoded)
- my_b_printf(&cache, "BINLOG '\n");
-
- if (print_base64(&cache, print_event_info, do_print_encoded))
- goto err;
-
- if (do_print_encoded)
- my_b_printf(&cache, "'%s\n", print_event_info->delimiter);
-
- print_event_info->printed_fd_event= TRUE;
- }
- DBUG_RETURN(cache.flush_data());
-err:
- DBUG_RETURN(1);
-}
-#endif /* MYSQL_CLIENT */
-
-/*
- Start_log_event_v3::Start_log_event_v3()
-*/
Start_log_event_v3::Start_log_event_v3(const char* buf, uint event_len,
const Format_description_log_event
@@ -6138,108 +2007,6 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, uint event_len,
}
-/*
- Start_log_event_v3::write()
-*/
-
-#ifndef MYSQL_CLIENT
-bool Start_log_event_v3::write()
-{
- char buff[START_V3_HEADER_LEN];
- int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
- memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
- if (!dont_set_created)
- created= get_time(); // this sets when and when_sec_part as a side effect
- int4store(buff + ST_CREATED_OFFSET,created);
- return write_header(sizeof(buff)) ||
- write_data(buff, sizeof(buff)) ||
- write_footer();
-}
-#endif
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-/**
- Start_log_event_v3::do_apply_event() .
- The master started
-
- IMPLEMENTATION
- - To handle the case where the master died without having time to write
- DROP TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is
- TODO), we clean up all temporary tables that we got, if we are sure we
- can (see below).
-
- @todo
- - Remove all active user locks.
- Guilhem 2003-06: this is true but not urgent: the worst it can cause is
- the use of a bit of memory for a user lock which will not be used
- anymore. If the user lock is later used, the old one will be released. In
- other words, no deadlock problem.
-*/
-
-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:
- case 4:
- /*
- This can either be 4.x (then a Start_log_event_v3 is only at master
- startup so we are sure the master has restarted and cleared his temp
- tables; the event always has 'created'>0) or 5.0 (then we have to test
- 'created').
- */
- if (created)
- {
- rli->close_temporary_tables();
-
- /*
- The following is only false if we get here with a BINLOG statement
- */
- if (rli->mi)
- cleanup_load_tmpdir(&rli->mi->cmp_connection_name);
- }
- break;
-
- /*
- Now the older formats; in that case load_tmpdir is cleaned up by the I/O
- thread.
- */
- case 1:
- if (strncmp(rli->relay_log.description_event_for_exec->server_version,
- "3.23.57",7) >= 0 && created)
- {
- /*
- Can distinguish, based on the value of 'created': this event was
- generated at master startup.
- */
- rli->close_temporary_tables();
- }
- /*
- Otherwise, can't distinguish a Start_log_event generated at
- master startup and one generated by master FLUSH LOGS, so cannot
- be sure temp tables have to be dropped. So do nothing.
- */
- break;
- default:
- /*
- This case is not expected. It can be either an event corruption or an
- unsupported binary log version.
- */
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Binlog version not supported");
- DBUG_RETURN(1);
- }
- DBUG_RETURN(error);
-}
-#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
/***************************************************************************
Format_description_log_event methods
****************************************************************************/
@@ -6274,7 +2041,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
common_header_len= LOG_EVENT_HEADER_LEN;
number_of_event_types= LOG_EVENT_TYPES;
/* we'll catch my_malloc() error in is_valid() */
- post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8)
+ post_header_len=(uint8*) my_malloc(PSI_INSTRUMENT_ME,
+ number_of_event_types*sizeof(uint8)
+ BINLOG_CHECKSUM_ALG_DESC_LEN,
MYF(0));
/*
@@ -6307,6 +2075,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -6400,8 +2169,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
make the slave detect less corruptions).
*/
number_of_event_types= FORMAT_DESCRIPTION_EVENT - 1;
- post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
- MYF(0));
+ post_header_len=(uint8*) my_malloc(PSI_INSTRUMENT_ME,
+ number_of_event_types*sizeof(uint8), MYF(0));
if (post_header_len)
{
post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
@@ -6469,7 +2238,8 @@ Format_description_log_event(const char* buf,
common_header_len, number_of_event_types));
/* If alloc fails, we'll detect it in is_valid() */
- post_header_len= (uint8*) my_memdup((uchar*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
+ post_header_len= (uint8*) my_memdup(PSI_INSTRUMENT_ME,
+ buf+ST_COMMON_HEADER_LEN_OFFSET+1,
number_of_event_types*
sizeof(*post_header_len),
MYF(0));
@@ -6489,159 +2259,6 @@ Format_description_log_event(const char* buf,
DBUG_VOID_RETURN;
}
-#ifndef MYSQL_CLIENT
-bool Format_description_log_event::write()
-{
- bool ret;
- bool no_checksum;
- /*
- We don't call Start_log_event_v3::write() because this would make 2
- my_b_safe_write().
- */
- 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]= common_header_len;
- /*
- if checksum is requested
- record the checksum-algorithm descriptor next to
- post_header_len vector which will be followed by the checksum value.
- Master is supposed to trigger checksum computing by binlog_checksum_options,
- slave does it via marking the event according to
- FD_queue checksum_alg value.
- */
- compile_time_assert(BINLOG_CHECKSUM_ALG_DESC_LEN == 1);
-#ifdef DBUG_ASSERT_EXISTS
- data_written= 0; // to prepare for need_checksum assert
-#endif
- uint8 checksum_byte= (uint8)
- (need_checksum() ? checksum_alg : BINLOG_CHECKSUM_ALG_OFF);
- /*
- FD of checksum-aware server is always checksum-equipped, (V) is in,
- regardless of @@global.binlog_checksum policy.
- Thereby a combination of (A) == 0, (V) != 0 means
- it's the checksum-aware server's FD event that heads checksum-free binlog
- file.
- Here 0 stands for checksumming OFF to evaluate (V) as 0 is that case.
- A combination of (A) != 0, (V) != 0 denotes FD of the checksum-aware server
- heading the checksummed binlog.
- (A), (V) presence in FD of the checksum-aware server makes the event
- 1 + 4 bytes bigger comparing to the former FD.
- */
-
- if ((no_checksum= (checksum_alg == BINLOG_CHECKSUM_ALG_OFF)))
- {
- checksum_alg= BINLOG_CHECKSUM_ALG_CRC32; // Forcing (V) room to fill anyway
- }
- ret= write_header(rec_size) ||
- write_data(buff, sizeof(buff)) ||
- write_data(post_header_len, number_of_event_types) ||
- write_data(&checksum_byte, sizeof(checksum_byte)) ||
- write_footer();
- if (no_checksum)
- checksum_alg= BINLOG_CHECKSUM_ALG_OFF;
- return ret;
-}
-#endif
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Format_description_log_event::do_apply_event(rpl_group_info *rgi)
-{
- int ret= 0;
- Relay_log_info *rli= rgi->rli;
- DBUG_ENTER("Format_description_log_event::do_apply_event");
-
- /*
- As a transaction NEVER spans on 2 or more binlogs:
- if we have an active transaction at this point, the master died
- while writing the transaction to the binary log, i.e. while
- flushing the binlog cache to the binlog. XA guarantees that master has
- rolled back. So we roll back.
- Note: this event could be sent by the master to inform us of the
- format of its binlog; in other words maybe it is not at its
- original place when it comes to us; we'll know this by checking
- log_pos ("artificial" events have log_pos == 0).
- */
- 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, 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.");
- rgi->cleanup_context(thd, 1);
- }
-
- /*
- If this event comes from ourselves, there is no cleaning task to
- 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) global_system_variables.server_id)
- {
- /*
- If the event was not requested by the slave i.e. the master sent
- it while the slave asked for a position >4, the event will make
- rli->group_master_log_pos advance. Say that the slave asked for
- position 1000, and the Format_desc event's end is 96. Then in
- the beginning of replication rli->group_master_log_pos will be
- 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(rgi);
- }
-
- if (!ret)
- {
- /* Save the information describing this binlog */
- copy_crypto_data(rli->relay_log.description_event_for_exec);
- delete rli->relay_log.description_event_for_exec;
- rli->relay_log.description_event_for_exec= this;
- }
-
- DBUG_RETURN(ret);
-}
-
-int Format_description_log_event::do_update_pos(rpl_group_info *rgi)
-{
- if (server_id == (uint32) global_system_variables.server_id)
- {
- /*
- We only increase the relay log position if we are skipping
- events and do not touch any group_* variables, nor flush the
- relay log info. If there is a crash, we will have to re-skip
- the events again, but that is a minor issue.
-
- If we do not skip stepping the group log position (and the
- server id was changed when restarting the server), it might well
- be that we start executing at a position that is invalid, e.g.,
- at a Rows_log_event or a Query_log_event preceded by a
- Intvar_log_event instead of starting at a Table_map_log_event or
- the Intvar_log_event respectively.
- */
- rgi->inc_event_relay_log_pos();
- return 0;
- }
- else
- {
- return Log_event::do_update_pos(rgi);
- }
-}
-
-Log_event::enum_skip_reason
-Format_description_log_event::do_shall_skip(rpl_group_info *rgi)
-{
- return Log_event::EVENT_SKIP_NOT;
-}
-
-#endif
-
bool Format_description_log_event::start_decryption(Start_encryption_log_event* sele)
{
DBUG_ASSERT(crypto_data.scheme == 0);
@@ -6732,7 +2349,7 @@ Format_description_log_event::is_version_before_checksum(const master_version_sp
@return the version-safe checksum alg descriptor where zero
designates no checksum, 255 - the orginator is
- checksum-unaware (effectively no checksum) and the actual
+ checksum-unaware (effectively no checksum) and the actuall
[1-254] range alg descriptor.
*/
enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
@@ -6777,42 +2394,7 @@ Start_encryption_log_event::Start_encryption_log_event(
crypto_scheme= ~0; // invalid
}
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Start_encryption_log_event::do_apply_event(rpl_group_info* rgi)
-{
- return rgi->rli->relay_log.description_event_for_exec->start_decryption(this);
-}
-
-int Start_encryption_log_event::do_update_pos(rpl_group_info *rgi)
-{
- /*
- master never sends Start_encryption_log_event, any SELE that a slave
- might see was created locally in MYSQL_BIN_LOG::open() on the slave
- */
- rgi->inc_event_relay_log_pos();
- return 0;
-}
-
-#endif
-#ifndef MYSQL_SERVER
-bool Start_encryption_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- Write_on_release_cache cache(&print_event_info->head_cache, file);
- StringBuffer<1024> buf;
- buf.append(STRING_WITH_LEN("# Encryption scheme: "));
- buf.append_ulonglong(crypto_scheme);
- buf.append(STRING_WITH_LEN(", key_version: "));
- buf.append_ulonglong(key_version);
- buf.append(STRING_WITH_LEN(", nonce: "));
- buf.append_hex(nonce, BINLOG_NONCE_LENGTH);
- buf.append(STRING_WITH_LEN("\n# The rest of the binlog is encrypted!\n"));
- if (my_b_write(&cache, (uchar*)buf.ptr(), buf.length()))
- return 1;
- return (cache.flush_data());
-}
-#endif
/**************************************************************************
Load_log_event methods
General note about Load_log_event: the binlogging of LOAD DATA INFILE is
@@ -6830,252 +2412,6 @@ bool Start_encryption_log_event::print(FILE* file,
positions displayed in SHOW SLAVE STATUS then are fine too).
**************************************************************************/
-/*
- Load_log_event::print_query()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-bool Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
- String *buf, my_off_t *fn_start,
- my_off_t *fn_end, const char *qualify_db)
-{
- if (need_db && db && db_len)
- {
- buf->append(STRING_WITH_LEN("use "));
- append_identifier(thd, buf, db, db_len);
- buf->append(STRING_WITH_LEN("; "));
- }
-
- buf->append(STRING_WITH_LEN("LOAD DATA "));
-
- if (is_concurrent)
- buf->append(STRING_WITH_LEN("CONCURRENT "));
-
- if (fn_start)
- *fn_start= buf->length();
-
- if (check_fname_outside_temp_buf())
- buf->append(STRING_WITH_LEN("LOCAL "));
- buf->append(STRING_WITH_LEN("INFILE '"));
- buf->append_for_single_quote(fname, fname_len);
- buf->append(STRING_WITH_LEN("' "));
-
- if (sql_ex.opt_flags & REPLACE_FLAG)
- buf->append(STRING_WITH_LEN("REPLACE "));
- else if (sql_ex.opt_flags & IGNORE_FLAG)
- buf->append(STRING_WITH_LEN("IGNORE "));
-
- buf->append(STRING_WITH_LEN("INTO"));
-
- if (fn_end)
- *fn_end= buf->length();
-
- buf->append(STRING_WITH_LEN(" TABLE "));
- if (qualify_db)
- {
- append_identifier(thd, buf, qualify_db, strlen(qualify_db));
- buf->append(STRING_WITH_LEN("."));
- }
- append_identifier(thd, buf, table_name, table_name_len);
-
- if (cs != NULL)
- {
- buf->append(STRING_WITH_LEN(" CHARACTER SET "));
- buf->append(cs, strlen(cs));
- }
-
- /* We have to create all optional fields as the default is not empty */
- buf->append(STRING_WITH_LEN(" FIELDS TERMINATED BY "));
- pretty_print_str(buf, sql_ex.field_term, sql_ex.field_term_len);
- if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
- buf->append(STRING_WITH_LEN(" OPTIONALLY "));
- buf->append(STRING_WITH_LEN(" ENCLOSED BY "));
- pretty_print_str(buf, sql_ex.enclosed, sql_ex.enclosed_len);
-
- buf->append(STRING_WITH_LEN(" ESCAPED BY "));
- pretty_print_str(buf, sql_ex.escaped, sql_ex.escaped_len);
-
- buf->append(STRING_WITH_LEN(" LINES TERMINATED BY "));
- pretty_print_str(buf, sql_ex.line_term, sql_ex.line_term_len);
- if (sql_ex.line_start_len)
- {
- buf->append(STRING_WITH_LEN(" STARTING BY "));
- pretty_print_str(buf, sql_ex.line_start, sql_ex.line_start_len);
- }
-
- if ((long) skip_lines > 0)
- {
- buf->append(STRING_WITH_LEN(" IGNORE "));
- buf->append_ulonglong(skip_lines);
- buf->append(STRING_WITH_LEN(" LINES "));
- }
-
- if (num_fields)
- {
- uint i;
- const char *field= fields;
- buf->append(STRING_WITH_LEN(" ("));
- for (i = 0; i < num_fields; i++)
- {
- if (i)
- {
- /*
- Yes, the space and comma is reversed here. But this is mostly dead
- code, at most used when reading really old binlogs from old servers,
- so better just leave it as is...
- */
- buf->append(STRING_WITH_LEN(" ,"));
- }
- append_identifier(thd, buf, field, field_lens[i]);
- field+= field_lens[i] + 1;
- }
- buf->append(STRING_WITH_LEN(")"));
- }
- return 0;
-}
-
-
-void Load_log_event::pack_info(Protocol *protocol)
-{
- char query_buffer[1024];
- String query_str(query_buffer, sizeof(query_buffer), system_charset_info);
-
- query_str.length(0);
- print_query(protocol->thd, TRUE, NULL, &query_str, 0, 0, NULL);
- protocol->store(query_str.ptr(), query_str.length(), &my_charset_bin);
-}
-#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
-
-#ifndef MYSQL_CLIENT
-
-/*
- Load_log_event::write_data_header()
-*/
-
-bool Load_log_event::write_data_header()
-{
- char buf[LOAD_HEADER_LEN];
- int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
- int4store(buf + L_EXEC_TIME_OFFSET, exec_time);
- int4store(buf + L_SKIP_LINES_OFFSET, skip_lines);
- buf[L_TBL_LEN_OFFSET] = (char)table_name_len;
- buf[L_DB_LEN_OFFSET] = (char)db_len;
- int4store(buf + L_NUM_FIELDS_OFFSET, num_fields);
- return write_data(buf, LOAD_HEADER_LEN) != 0;
-}
-
-
-/*
- Load_log_event::write_data_body()
-*/
-
-bool Load_log_event::write_data_body()
-{
- if (sql_ex.write_data(writer))
- return 1;
- if (num_fields && fields && field_lens)
- {
- if (write_data(field_lens, num_fields) ||
- write_data(fields, field_block_len))
- return 1;
- }
- return (write_data(table_name, table_name_len + 1) ||
- write_data(db, db_len + 1) ||
- write_data(fname, fname_len));
-}
-
-
-/*
- Load_log_event::Load_log_event()
-*/
-
-Load_log_event::Load_log_event(THD *thd_arg, const sql_exchange *ex,
- const char *db_arg, const char *table_name_arg,
- List<Item> &fields_arg,
- bool is_concurrent_arg,
- enum enum_duplicates handle_dup,
- bool ignore, bool using_trans)
- :Log_event(thd_arg,
- thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0,
- using_trans),
- thread_id(thd_arg->thread_id),
- slave_proxy_id((ulong)thd_arg->variables.pseudo_thread_id),
- num_fields(0),fields(0),
- field_lens(0),field_block_len(0),
- table_name(table_name_arg ? table_name_arg : ""),
- db(db_arg), fname(ex->file_name), local_fname(FALSE),
- is_concurrent(is_concurrent_arg)
-{
- time_t end_time;
- time(&end_time);
- exec_time = (ulong) (end_time - thd_arg->start_time);
- /* db can never be a zero pointer in 4.0 */
- db_len = (uint32) strlen(db);
- table_name_len = (uint32) strlen(table_name);
- fname_len = (fname) ? (uint) strlen(fname) : 0;
- sql_ex.field_term = ex->field_term->ptr();
- sql_ex.field_term_len = (uint8) ex->field_term->length();
- sql_ex.enclosed = ex->enclosed->ptr();
- sql_ex.enclosed_len = (uint8) ex->enclosed->length();
- sql_ex.line_term = ex->line_term->ptr();
- sql_ex.line_term_len = (uint8) ex->line_term->length();
- sql_ex.line_start = ex->line_start->ptr();
- sql_ex.line_start_len = (uint8) ex->line_start->length();
- sql_ex.escaped = ex->escaped->ptr();
- sql_ex.escaped_len = (uint8) ex->escaped->length();
- sql_ex.opt_flags = 0;
- sql_ex.cached_new_format = -1;
-
- if (ex->dumpfile)
- sql_ex.opt_flags|= DUMPFILE_FLAG;
- if (ex->opt_enclosed)
- sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
-
- sql_ex.empty_flags= 0;
-
- switch (handle_dup) {
- case DUP_REPLACE:
- sql_ex.opt_flags|= REPLACE_FLAG;
- break;
- case DUP_UPDATE: // Impossible here
- case DUP_ERROR:
- break;
- }
- if (ignore)
- sql_ex.opt_flags|= IGNORE_FLAG;
-
- if (!ex->field_term->length())
- sql_ex.empty_flags |= FIELD_TERM_EMPTY;
- if (!ex->enclosed->length())
- sql_ex.empty_flags |= ENCLOSED_EMPTY;
- if (!ex->line_term->length())
- sql_ex.empty_flags |= LINE_TERM_EMPTY;
- if (!ex->line_start->length())
- sql_ex.empty_flags |= LINE_START_EMPTY;
- if (!ex->escaped->length())
- sql_ex.empty_flags |= ESCAPED_EMPTY;
-
- skip_lines = ex->skip_lines;
-
- List_iterator<Item> li(fields_arg);
- field_lens_buf.length(0);
- fields_buf.length(0);
- Item* item;
- while ((item = li++))
- {
- num_fields++;
- uchar len= (uchar) item->name.length;
- field_block_len += len + 1;
- fields_buf.append(item->name.str, len + 1);
- field_lens_buf.append((char*)&len, 1);
- }
-
- field_lens = (const uchar*)field_lens_buf.ptr();
- fields = fields_buf.ptr();
-}
-#endif /* !MYSQL_CLIENT */
-
/**
@note
@@ -7173,531 +2509,10 @@ err:
}
-/*
- Load_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
-{
- return print(file, print_event_info, 0);
-}
-
-
-bool Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
- bool commented)
-{
- Write_on_release_cache cache(&print_event_info->head_cache, file_arg);
- bool different_db= 1;
- DBUG_ENTER("Load_log_event::print");
-
- if (!print_event_info->short_form)
- {
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
- thread_id, exec_time))
- goto err;
- }
-
- if (db)
- {
- /*
- If the database is different from the one of the previous statement, we
- need to print the "use" command, and we update the last_db.
- But if commented, the "use" is going to be commented so we should not
- update the last_db.
- */
- if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) &&
- !commented)
- memcpy(print_event_info->db, db, db_len + 1);
- }
-
- if (db && db[0] && different_db)
- if (my_b_printf(&cache, "%suse %`s%s\n",
- commented ? "# " : "",
- db, print_event_info->delimiter))
- goto err;
-
- if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
- if (my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
- commented ? "# " : "", (ulong)thread_id,
- print_event_info->delimiter))
- goto err;
- if (my_b_printf(&cache, "%sLOAD DATA ",
- commented ? "# " : ""))
- goto err;
- if (check_fname_outside_temp_buf())
- if (my_b_write_string(&cache, "LOCAL "))
- goto err;
- if (my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname))
- goto err;
-
- if (sql_ex.opt_flags & REPLACE_FLAG)
- {
- if (my_b_write_string(&cache, "REPLACE "))
- goto err;
- }
- else if (sql_ex.opt_flags & IGNORE_FLAG)
- if (my_b_write_string(&cache, "IGNORE "))
- goto err;
-
- if (my_b_printf(&cache, "INTO TABLE `%s`", table_name) ||
- my_b_write_string(&cache, " FIELDS TERMINATED BY ") ||
- pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len))
- goto err;
-
- if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
- if (my_b_write_string(&cache, " OPTIONALLY "))
- goto err;
- if (my_b_write_string(&cache, " ENCLOSED BY ") ||
- pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len) ||
- my_b_write_string(&cache, " ESCAPED BY ") ||
- pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len) ||
- my_b_write_string(&cache, " LINES TERMINATED BY ") ||
- pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len))
- goto err;
-
- if (sql_ex.line_start)
- {
- if (my_b_write_string(&cache," STARTING BY ") ||
- pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len))
- goto err;
- }
- if ((long) skip_lines > 0)
- if (my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines))
- goto err;
-
- if (num_fields)
- {
- uint i;
- const char* field = fields;
- if (my_b_write_string(&cache, " ("))
- goto err;
- for (i = 0; i < num_fields; i++)
- {
- if (i)
- if (my_b_write_byte(&cache, ','))
- goto err;
- if (my_b_printf(&cache, "%`s", field))
- goto err;
- field += field_lens[i] + 1;
- }
- if (my_b_write_byte(&cache, ')'))
- goto err;
- }
-
- if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
- goto err;
- DBUG_RETURN(cache.flush_data());
-err:
- DBUG_RETURN(1);
-}
-#endif /* MYSQL_CLIENT */
-
-#ifndef MYSQL_CLIENT
-
-/**
- Load_log_event::set_fields()
-
- @note
- This function can not use the member variable
- for the database, since LOAD DATA INFILE on the slave
- can be for a different database than the current one.
- This is the reason for the affected_db argument to this method.
-*/
-
-void Load_log_event::set_fields(const char* affected_db,
- List<Item> &field_list,
- Name_resolution_context *context)
-{
- uint i;
- const char* field = fields;
- for (i= 0; i < num_fields; i++)
- {
- LEX_CSTRING field_name= {field, field_lens[i] };
- field_list.push_back(new (thd->mem_root)
- Item_field(thd, context, affected_db, table_name,
- &field_name),
- thd->mem_root);
- field+= field_lens[i] + 1;
- }
-}
-#endif /* !MYSQL_CLIENT */
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-/**
- Does the data loading job when executing a LOAD DATA on the slave.
-
- @param net
- @param rli
- @param use_rli_only_for_errors If set to 1, rli is provided to
- Load_log_event::exec_event only for this
- function to have RPL_LOG_NAME and
- rli->last_slave_error, both being used by
- error reports. rli's position advancing
- is skipped (done by the caller which is
- Execute_load_log_event::exec_event).
- If set to 0, rli is provided for full use,
- i.e. for error reports and position
- advancing.
-
- @todo
- fix this; this can be done by testing rules in
- Create_file_log_event::exec_event() and then discarding Append_block and
- al.
- @todo
- this is a bug - this needs to be moved to the I/O thread
-
- @retval
- 0 Success
- @retval
- 1 Failure
-*/
-
-int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
- bool use_rli_only_for_errors)
-{
- Relay_log_info const *rli= rgi->rli;
- Rpl_filter *rpl_filter= rli->mi->rpl_filter;
- DBUG_ENTER("Load_log_event::do_apply_event");
-
- DBUG_ASSERT(thd->query() == 0);
- set_thd_db(thd, rpl_filter, db, db_len);
- thd->clear_error(1);
-
- /* see Query_log_event::do_apply_event() and BUG#13360 */
- 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().
- */
- lex_start(thd);
- thd->lex->local_file= local_fname;
- thd->reset_for_next_command(0); // Errors are cleared above
-
- /*
- 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
- now. So it is possible that we did a lot of disk writes for
- nothing. In other words, a big LOAD DATA INFILE on the master will
- still consume a lot of space on the slave (space in the relay log
- + space of temp files: twice the space of the file to load...)
- even if it will finally be ignored. TODO: fix this; this can be
- done by testing rules in Create_file_log_event::do_apply_event()
- and then discarding Append_block and al. Another way is do the
- filtering in the I/O thread (more efficient: no disk writes at
- all).
-
-
- Note: We do not need to execute reset_one_shot_variables() if this
- db_ok() test fails.
- Reason: The db stored in binlog events is the same for SET and for
- its companion query. If the SET is ignored because of
- db_ok(), the companion query will also be ignored, and if
- the companion query is ignored in the db_ok() test of
- ::do_apply_event(), then the companion SET also have so
- we don't need to reset_one_shot_variables().
- */
- if (rpl_filter->db_ok(thd->db.str))
- {
- thd->set_time(when, when_sec_part);
- thd->set_query_id(next_query_id());
- thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
-
- TABLE_LIST tables;
- LEX_CSTRING db_name= { thd->strmake(thd->db.str, thd->db.length), thd->db.length };
- if (lower_case_table_names)
- my_casedn_str(system_charset_info, (char *)table_name);
- LEX_CSTRING tbl_name= { table_name, strlen(table_name) };
- tables.init_one_table(&db_name, &tbl_name, 0, TL_WRITE);
- tables.updating= 1;
-
- // the table will be opened in mysql_load
- if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db.str, &tables))
- {
- // TODO: this is a bug - this needs to be moved to the I/O thread
- if (net)
- skip_load_data_infile(net);
- }
- else
- {
- enum enum_duplicates handle_dup;
- bool ignore= 0;
- char query_buffer[1024];
- String query_str(query_buffer, sizeof(query_buffer), system_charset_info);
- char *load_data_query;
-
- query_str.length(0);
- /*
- Forge LOAD DATA INFILE query which will be used in SHOW PROCESS LIST
- and written to slave's binlog if binlogging is on.
- */
- print_query(thd, FALSE, NULL, &query_str, NULL, NULL, NULL);
- if (!(load_data_query= (char *)thd->strmake(query_str.ptr(),
- query_str.length())))
- {
- /*
- This will set thd->fatal_error in case of OOM. So we surely will notice
- that something is wrong.
- */
- goto error;
- }
-
- thd->set_query(load_data_query, (uint) (query_str.length()));
-
- if (sql_ex.opt_flags & REPLACE_FLAG)
- handle_dup= DUP_REPLACE;
- else if (sql_ex.opt_flags & IGNORE_FLAG)
- {
- ignore= 1;
- handle_dup= DUP_ERROR;
- }
- else
- {
- /*
- When replication is running fine, if it was DUP_ERROR on the
- master then we could choose IGNORE here, because if DUP_ERROR
- succeeded on master, and data is identical on the master and slave,
- then there should be no uniqueness errors on slave, so IGNORE is
- the same as DUP_ERROR. But in the unlikely case of uniqueness errors
- (because the data on the master and slave happen to be different
- (user error or bug), we want LOAD DATA to print an error message on
- the slave to discover the problem.
-
- If reading from net (a 3.23 master), mysql_load() will change this
- to IGNORE.
- */
- handle_dup= DUP_ERROR;
- }
- /*
- We need to set thd->lex->sql_command and thd->lex->duplicates
- since InnoDB tests these variables to decide if this is a LOAD
- DATA ... REPLACE INTO ... statement even though mysql_parse()
- is not called. This is not needed in 5.0 since there the LOAD
- DATA ... statement is replicated using mysql_parse(), which
- sets the thd->lex fields correctly.
- */
- thd->lex->sql_command= SQLCOM_LOAD;
- thd->lex->duplicates= handle_dup;
-
- sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
- String field_term(sql_ex.field_term,sql_ex.field_term_len,log_cs);
- String enclosed(sql_ex.enclosed,sql_ex.enclosed_len,log_cs);
- String line_term(sql_ex.line_term,sql_ex.line_term_len,log_cs);
- String line_start(sql_ex.line_start,sql_ex.line_start_len,log_cs);
- String escaped(sql_ex.escaped,sql_ex.escaped_len, log_cs);
- ex.field_term= &field_term;
- ex.enclosed= &enclosed;
- ex.line_term= &line_term;
- ex.line_start= &line_start;
- ex.escaped= &escaped;
-
- ex.opt_enclosed = (sql_ex.opt_flags & OPT_ENCLOSED_FLAG);
- if (sql_ex.empty_flags & FIELD_TERM_EMPTY)
- ex.field_term->length(0);
-
- ex.skip_lines = skip_lines;
- List<Item> field_list;
- thd->lex->first_select_lex()->context.resolve_in_table_list_only(&tables);
- set_fields(tables.db.str,
- field_list, &thd->lex->first_select_lex()->context);
- thd->variables.pseudo_thread_id= thread_id;
- if (net)
- {
- // mysql_load will use thd->net to read the file
- thd->net.vio = net->vio;
- // Make sure the client does not get confused about the packet sequence
- thd->net.pkt_nr = net->pkt_nr;
- }
- /*
- It is safe to use tmp_list twice because we are not going to
- update it inside mysql_load().
- */
- List<Item> tmp_list;
- if (thd->open_temporary_tables(&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)
- {
- /* log_pos is the position of the LOAD event in the master log */
- sql_print_warning("Slave: load data infile on table '%s' at "
- "log position %llu in log '%s' produced %ld "
- "warning(s). Default database: '%s'",
- (char*) table_name, log_pos, RPL_LOG_NAME,
- (ulong) thd->cuted_fields,
- thd->get_db());
- }
- if (net)
- net->pkt_nr= thd->net.pkt_nr;
- }
- }
- else
- {
- /*
- We will just ask the master to send us /dev/null if we do not
- want to load the data.
- TODO: this a bug - needs to be done in I/O thread
- */
- if (net)
- skip_load_data_infile(net);
- }
-
-error:
- thd->net.vio = 0;
- const char *remember_db= thd->get_db();
- thd->catalog= 0;
- thd->set_db(&null_clex_str); /* will free the current database */
- thd->reset_query();
- thd->get_stmt_da()->set_overwrite_status(true);
- thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- 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
- perform it and release metadata locks.
- - If inside a multi-statement transaction,
- defer the release of metadata locks until the current
- transaction is either committed or rolled back. This prevents
- other statements from modifying the table for the entire
- duration of this transaction. This provides commit ordering
- and guarantees serializability across multiple transactions.
- - If in autocommit mode, or outside a transactional context,
- automatically release metadata locks of the current statement.
- */
- if (thd->transaction_rollback_request)
- {
- trans_rollback_implicit(thd);
- thd->mdl_context.release_transactional_locks();
- }
- else if (! thd->in_multi_stmt_transaction_mode())
- thd->mdl_context.release_transactional_locks();
- else
- thd->mdl_context.release_statement_locks();
-
- DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
- thd->is_slave_error= 0; thd->is_fatal_error= 1;);
-
- if (unlikely(thd->is_slave_error))
- {
- /* this err/sql_errno code is copy-paste from net_send_error() */
- const char *err;
- int sql_errno;
- if (thd->is_error())
- {
- err= thd->get_stmt_da()->message();
- sql_errno= thd->get_stmt_da()->sql_errno();
- }
- else
- {
- sql_errno=ER_UNKNOWN_ERROR;
- err= ER_THD(thd, 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, remember_db);
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- DBUG_RETURN(1);
- }
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
-
- if (unlikely(thd->is_fatal_error))
- {
- char buf[256];
- my_snprintf(buf, sizeof(buf),
- "Running LOAD DATA INFILE on table '%-.64s'."
- " Default database: '%-.64s'",
- (char*)table_name,
- remember_db);
-
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
- ER_THD(thd, ER_SLAVE_FATAL_ERROR), buf);
- DBUG_RETURN(1);
- }
-
- DBUG_RETURN( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rgi) );
-}
-#endif
-
-
/**************************************************************************
Rotate_log_event methods
**************************************************************************/
-/*
- Rotate_log_event::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Rotate_log_event::pack_info(Protocol *protocol)
-{
- StringBuffer<256> tmp(log_cs);
- tmp.length(0);
- tmp.append(new_log_ident, ident_len);
- tmp.append(STRING_WITH_LEN(";pos="));
- tmp.append_ulonglong(pos);
- protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
-}
-#endif
-
-
-/*
- Rotate_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- char buf[22];
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F);
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_write_string(&cache, "\tRotate to "))
- goto err;
- if (new_log_ident)
- if (my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len))
- goto err;
- if (my_b_printf(&cache, " pos: %s\n", llstr(pos, buf)))
- goto err;
- return cache.flush_data();
-err:
- return 1;
-}
-#endif /* MYSQL_CLIENT */
-
-
-
-/*
- Rotate_log_event::Rotate_log_event() (2 constructors)
-*/
-
-
-#ifndef MYSQL_CLIENT
-Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
- uint ident_len_arg, ulonglong pos_arg,
- uint flags_arg)
- :Log_event(), new_log_ident(new_log_ident_arg),
- pos(pos_arg),ident_len(ident_len_arg ? ident_len_arg :
- (uint) strlen(new_log_ident_arg)), flags(flags_arg)
-{
- DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
- DBUG_PRINT("enter",("new_log_ident: %s pos: %llu flags: %lu", new_log_ident_arg,
- pos_arg, (ulong) flags));
- cache_type= EVENT_NO_CACHE;
- if (flags & DUP_NAME)
- new_log_ident= my_strndup(new_log_ident_arg, ident_len, MYF(MY_WME));
- if (flags & RELAY_LOG)
- set_relay_log_event();
- DBUG_VOID_RETURN;
-}
-#endif
-
-
Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event) ,new_log_ident(0), flags(DUP_NAME)
@@ -7713,197 +2528,16 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
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));
+ new_log_ident= my_strndup(PSI_INSTRUMENT_ME, buf + ident_offset, (uint) ident_len, MYF(MY_WME));
DBUG_PRINT("debug", ("new_log_ident: '%s'", new_log_ident));
DBUG_VOID_RETURN;
}
-/*
- Rotate_log_event::write()
-*/
-
-#ifndef MYSQL_CLIENT
-bool Rotate_log_event::write()
-{
- char buf[ROTATE_HEADER_LEN];
- int8store(buf + R_POS_OFFSET, pos);
- return (write_header(ROTATE_HEADER_LEN + ident_len) ||
- write_data(buf, ROTATE_HEADER_LEN) ||
- write_data(new_log_ident, (uint) ident_len) ||
- write_footer());
-}
-#endif
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-
-/*
- Got a rotate log event from the master.
-
- This is mainly used so that we can later figure out the logname and
- position for the master.
-
- We can't rotate the slave's BINlog as this will cause infinitive rotations
- in a A -> B -> A setup.
- The NOTES below is a wrong comment which will disappear when 4.1 is merged.
-
- This must only be called from the Slave SQL thread, since it calls
- Relay_log_info::flush().
-
- @retval
- 0 ok
- 1 error
-*/
-int Rotate_log_event::do_update_pos(rpl_group_info *rgi)
-{
- int error= 0;
- Relay_log_info *rli= rgi->rli;
- DBUG_ENTER("Rotate_log_event::do_update_pos");
-
- DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
- (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: %llu", this->pos));
-
- /*
- If we are in a transaction or in a group: the only normal case is
- when the I/O thread was copying a big transaction, then it was
- stopped and restarted: we have this in the relay log:
-
- BEGIN
- ...
- ROTATE (a fake one)
- ...
- COMMIT or ROLLBACK
-
- In that case, we don't want to touch the coordinates which
- 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 != global_system_variables.server_id ||
- rli->replicate_same_server_id) &&
- !is_relay_log_event() &&
- !rli->is_in_group() &&
- !rgi->is_parallel_exec)
- {
- mysql_mutex_lock(&rli->data_lock);
- DBUG_PRINT("info", ("old group_master_log_name: '%s' "
- "old group_master_log_pos: %lu",
- rli->group_master_log_name,
- (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, 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);
- error= rli->flush();
-
- /*
- 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, rgi);
- thd->variables.sql_mode= global_system_variables.sql_mode;
- thd->variables.auto_increment_increment=
- thd->variables.auto_increment_offset= 1;
- }
- else
- rgi->inc_event_relay_log_pos();
-
- DBUG_RETURN(error);
-}
-
-
-Log_event::enum_skip_reason
-Rotate_log_event::do_shall_skip(rpl_group_info *rgi)
-{
- enum_skip_reason reason= Log_event::do_shall_skip(rgi);
-
- switch (reason) {
- case Log_event::EVENT_SKIP_NOT:
- case Log_event::EVENT_SKIP_COUNT:
- return Log_event::EVENT_SKIP_NOT;
-
- case Log_event::EVENT_SKIP_IGNORE:
- return Log_event::EVENT_SKIP_IGNORE;
- }
- DBUG_ASSERT(0);
- return Log_event::EVENT_SKIP_NOT; // To keep compiler happy
-}
-
-#endif
-
-
/**************************************************************************
Binlog_checkpoint_log_event methods
**************************************************************************/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Binlog_checkpoint_log_event::pack_info(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
-bool Binlog_checkpoint_log_event::print(FILE *file,
- PRINT_EVENT_INFO *print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F);
-
- if (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'))
- return 1;
- return cache.flush_data();
-}
-#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)
@@ -7921,25 +2555,12 @@ Binlog_checkpoint_log_event::Binlog_checkpoint_log_event(
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,
+ binlog_file_name= my_strndup(PSI_INSTRUMENT_ME, buf + post_header_len, binlog_file_len,
MYF(MY_WME));
return;
}
-#ifndef MYSQL_CLIENT
-bool Binlog_checkpoint_log_event::write()
-{
- uchar buf[BINLOG_CHECKPOINT_HEADER_LEN];
- int4store(buf, binlog_file_len);
- return write_header(BINLOG_CHECKPOINT_HEADER_LEN + binlog_file_len) ||
- write_data(buf, BINLOG_CHECKPOINT_HEADER_LEN) ||
- write_data(binlog_file_name, binlog_file_len) ||
- write_footer();
-}
-#endif /* MYSQL_CLIENT */
-
-
/**************************************************************************
Global transaction ID stuff
**************************************************************************/
@@ -7959,7 +2580,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7967,339 +2588,24 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
}
-}
-
-
-#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;
- bool is_tmp_table= thd_arg->lex->stmt_accessed_temp_table();
- if (thd_arg->transaction.stmt.trans_did_wait() ||
- thd_arg->transaction.all.trans_did_wait())
- flags2|= FL_WAITED;
- if (thd_arg->transaction.stmt.trans_did_ddl() ||
- thd_arg->transaction.stmt.has_created_dropped_temp_table() ||
- thd_arg->transaction.all.trans_did_ddl() ||
- thd_arg->transaction.all.has_created_dropped_temp_table())
- flags2|= FL_DDL;
- else if (is_transactional && !is_tmp_table)
- flags2|= FL_TRANSACTIONAL;
- if (!(thd_arg->variables.option_bits & OPTION_RPL_SKIP_PARALLEL))
- flags2|= FL_ALLOW_PARALLEL;
- /* Preserve any DDL or WAITED flag in the slave's binlog. */
- if (thd_arg->rgi_slave)
- flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
-}
-
-
-/*
- Used to record GTID while sending binlog to slave, without having to
- fully construct every Gtid_log_event() needlessly.
-*/
-bool
-Gtid_log_event::peek(const char *event_start, size_t event_len,
- enum enum_binlog_checksum_alg 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()
-{
- 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(write_len) ||
- write_data(buf, write_len) ||
- write_footer();
-}
-
-
-/*
- 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,
- enum enum_binlog_checksum_alg 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(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)
-{
- ulonglong bits= thd->variables.option_bits;
- thd->variables.server_id= this->server_id;
- thd->variables.gtid_domain_id= this->domain_id;
- thd->variables.gtid_seq_no= this->seq_no;
- rgi->gtid_ev_flags2= flags2;
- thd->reset_for_next_command();
-
- 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((bits & OPTION_GTID_BEGIN) == 0);
-
- Master_info *mi=rgi->rli->mi;
- switch (flags2 & (FL_DDL | FL_TRANSACTIONAL))
- {
- case FL_TRANSACTIONAL:
- mi->total_trans_groups++;
- break;
- case FL_DDL:
- mi->total_ddl_groups++;
- break;
- default:
- mi->total_non_trans_groups++;
- }
-
- if (flags2 & FL_STANDALONE)
- return 0;
-
- /* Execute this like a BEGIN query event. */
- bits|= OPTION_GTID_BEGIN;
- if (flags2 & FL_ALLOW_PARALLEL)
- bits&= ~(ulonglong)OPTION_RPL_SKIP_PARALLEL;
- else
- bits|= (ulonglong)OPTION_RPL_SKIP_PARALLEL;
- thd->variables.option_bits= bits;
- 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 */
-
-bool
-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, this);
- char buf[21];
- char buf2[21];
-
- if (!print_event_info->short_form && !is_flashback)
+ if (flags2 & (FL_PREPARED_XA | FL_COMPLETED_XA))
{
- print_header(&cache, print_event_info, FALSE);
- longlong10_to_str(seq_no, buf, 10);
- if (my_b_printf(&cache, "\tGTID %u-%u-%s", domain_id, server_id, buf))
- goto err;
- if (flags2 & FL_GROUP_COMMIT_ID)
- {
- longlong10_to_str(commit_id, buf2, 10);
- if (my_b_printf(&cache, " cid=%s", buf2))
- goto err;
- }
- if (flags2 & FL_DDL)
- if (my_b_write_string(&cache, " ddl"))
- goto err;
- if (flags2 & FL_TRANSACTIONAL)
- if (my_b_write_string(&cache, " trans"))
- goto err;
- if (flags2 & FL_WAITED)
- if (my_b_write_string(&cache, " waited"))
- goto err;
- if (my_b_printf(&cache, "\n"))
- goto err;
-
- if (!print_event_info->allow_parallel_printed ||
- print_event_info->allow_parallel != !!(flags2 & FL_ALLOW_PARALLEL))
- {
- if (my_b_printf(&cache,
- "/*!100101 SET @@session.skip_parallel_replication=%u*/%s\n",
- !(flags2 & FL_ALLOW_PARALLEL),
- print_event_info->delimiter))
- goto err;
- print_event_info->allow_parallel= !!(flags2 & FL_ALLOW_PARALLEL);
- print_event_info->allow_parallel_printed= true;
- }
-
- if (!print_event_info->domain_id_printed ||
- print_event_info->domain_id != domain_id)
- {
- if (my_b_printf(&cache,
- "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n",
- domain_id, print_event_info->delimiter))
- goto err;
- print_event_info->domain_id= domain_id;
- print_event_info->domain_id_printed= true;
- }
+ xid.formatID= uint4korr(buf);
+ buf+= 4;
- if (!print_event_info->server_id_printed ||
- print_event_info->server_id != server_id)
- {
- if (my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n",
- server_id, print_event_info->delimiter))
- goto err;
- print_event_info->server_id= server_id;
- print_event_info->server_id_printed= true;
- }
+ xid.gtrid_length= (long) buf[0];
+ xid.bqual_length= (long) buf[1];
+ buf+= 2;
- if (!is_flashback)
- if (my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
- buf, print_event_info->delimiter))
- goto err;
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" :
- "START TRANSACTION\n%s\n", print_event_info->delimiter))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
}
-#endif /* MYSQL_SERVER */
-
/* GTID list. */
@@ -8321,8 +2627,8 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
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)))))
+ (!(list= (rpl_gtid *)my_malloc(PSI_INSTRUMENT_ME,
+ count*sizeof(*list) + (count == 0), MYF(MY_WME)))))
return;
for (i= 0; i < count; ++i)
@@ -8339,7 +2645,8 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
if ((gl_flags & FLAG_IGN_GTIDS))
{
uint32 i;
- if (!(sub_id_list= (uint64 *)my_malloc(count*sizeof(uint64), MYF(MY_WME))))
+ if (!(sub_id_list= (uint64 *)my_malloc(PSI_INSTRUMENT_ME,
+ count*sizeof(uint64), MYF(MY_WME))))
{
my_free(list);
list= NULL;
@@ -8362,216 +2669,9 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
}
-#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()
-{
- char buf[128];
- String packet(buf, sizeof(buf), system_charset_info);
-
- packet.length(0);
- if (to_packet(&packet))
- return true;
- return write_header(get_data_size()) ||
- write_data(packet.ptr(), packet.length()) ||
- write_footer();
-}
-
-
-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)
- {
- void *hton= NULL;
- 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, &hton)))
- return ret;
- rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i],
- hton, 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;
- }
- free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC));
- 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(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 */
-
-bool
-Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F);
- char buf[21];
- uint32 i;
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\tGtid list ["))
- goto err;
-
- for (i= 0; i < count; ++i)
- {
- longlong10_to_str(list[i].seq_no, buf, 10);
- if (my_b_printf(&cache, "%u-%u-%s", list[i].domain_id,
- list[i].server_id, buf))
- goto err;
- if (i < count-1)
- if (my_b_printf(&cache, ",\n# "))
- goto err;
- }
- if (my_b_printf(&cache, "]\n"))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-}
-
-#endif /* MYSQL_SERVER */
-
-
/*
Used to record gtid_list event while sending binlog to slave, without having to
- fully construct the event object.
+ fully contruct the event object.
*/
bool
Gtid_list_log_event::peek(const char *event_start, size_t event_len,
@@ -8603,8 +2703,8 @@ Gtid_list_log_event::peek(const char *event_start, size_t event_len,
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))))
+ if (!(gtid_list= (rpl_gtid *)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(rpl_gtid)*count + (count == 0), MYF(MY_WME))))
return true;
*out_gtid_list= gtid_list;
*out_list_len= count;
@@ -8628,22 +2728,6 @@ Gtid_list_log_event::peek(const char *event_start, size_t event_len,
**************************************************************************/
/*
- Intvar_log_event::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Intvar_log_event::pack_info(Protocol *protocol)
-{
- char buf[256], *pos;
- pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
- *pos++= '=';
- pos= longlong10_to_str(val, pos, -10);
- protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
-}
-#endif
-
-
-/*
Intvar_log_event::Intvar_log_event()
*/
@@ -8673,135 +2757,10 @@ const char* Intvar_log_event::get_var_type_name()
}
-/*
- Intvar_log_event::write()
-*/
-
-#ifndef MYSQL_CLIENT
-bool Intvar_log_event::write()
-{
- uchar buf[9];
- buf[I_TYPE_OFFSET]= (uchar) type;
- int8store(buf + I_VAL_OFFSET, val);
- return write_header(sizeof(buf)) ||
- write_data(buf, sizeof(buf)) ||
- write_footer();
-}
-#endif
-
-
-/*
- Intvar_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
-{
- char llbuff[22];
- const char *UNINIT_VAR(msg);
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F);
-
- if (!print_event_info->short_form)
- {
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_write_string(&cache, "\tIntvar\n"))
- goto err;
- }
-
- if (my_b_printf(&cache, "SET "))
- goto err;
- switch (type) {
- case LAST_INSERT_ID_EVENT:
- msg="LAST_INSERT_ID";
- break;
- case INSERT_ID_EVENT:
- msg="INSERT_ID";
- break;
- case INVALID_INT_EVENT:
- default: // cannot happen
- msg="INVALID_INT";
- break;
- }
- if (my_b_printf(&cache, "%s=%s%s\n",
- msg, llstr(val,llbuff), print_event_info->delimiter))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif
-
-
-#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT)
-
-/*
- Intvar_log_event::do_apply_event()
-*/
-
-int Intvar_log_event::do_apply_event(rpl_group_info *rgi)
-{
- DBUG_ENTER("Intvar_log_event::do_apply_event");
- if (rgi->deferred_events_collecting)
- {
- DBUG_PRINT("info",("deferring event"));
- DBUG_RETURN(rgi->deferred_events->add(this));
- }
-
- switch (type) {
- case LAST_INSERT_ID_EVENT:
- thd->first_successful_insert_id_in_prev_stmt= val;
- DBUG_PRINT("info",("last_insert_id_event: %ld", (long) val));
- break;
- case INSERT_ID_EVENT:
- thd->force_one_auto_inc_interval(val);
- break;
- }
- DBUG_RETURN(0);
-}
-
-int Intvar_log_event::do_update_pos(rpl_group_info *rgi)
-{
- rgi->inc_event_relay_log_pos();
- return 0;
-}
-
-
-Log_event::enum_skip_reason
-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
- 2 when recovering from an insert which used a auto increment,
- rand, or user var. Therefore, if the slave skip counter is 1, we
- just say that this event should be skipped by ignoring it, meaning
- 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(rgi);
-}
-
-#endif
-
-
/**************************************************************************
Rand_log_event methods
**************************************************************************/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Rand_log_event::pack_info(Protocol *protocol)
-{
- char buf1[256], *pos;
- pos= strmov(buf1,"rand_seed1=");
- pos= int10_to_str((long) seed1, pos, 10);
- pos= strmov(pos, ",rand_seed2=");
- pos= int10_to_str((long) seed2, pos, 10);
- protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
-}
-#endif
-
-
Rand_log_event::Rand_log_event(const char* buf,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
@@ -8814,118 +2773,10 @@ Rand_log_event::Rand_log_event(const char* buf,
}
-#ifndef MYSQL_CLIENT
-bool Rand_log_event::write()
-{
- uchar buf[16];
- int8store(buf + RAND_SEED1_OFFSET, seed1);
- int8store(buf + RAND_SEED2_OFFSET, seed2);
- return write_header(sizeof(buf)) ||
- write_data(buf, sizeof(buf)) ||
- write_footer();
-}
-#endif
-
-
-#ifdef MYSQL_CLIENT
-bool Rand_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 llbuff[22],llbuff2[22];
- if (!print_event_info->short_form)
- {
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_write_string(&cache, "\tRand\n"))
- goto err;
- }
- if (my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
- llstr(seed1, llbuff),llstr(seed2, llbuff2),
- print_event_info->delimiter))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif /* MYSQL_CLIENT */
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Rand_log_event::do_apply_event(rpl_group_info *rgi)
-{
- 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(rpl_group_info *rgi)
-{
- rgi->inc_event_relay_log_pos();
- return 0;
-}
-
-
-Log_event::enum_skip_reason
-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
- 2 when recovering from an insert which used a auto increment,
- rand, or user var. Therefore, if the slave skip counter is 1, we
- just say that this event should be skipped by ignoring it, meaning
- 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(rgi);
-}
-
-/**
- Exec deferred Int-, Rand- and User- var events prefixing
- a Query-log-event event.
-
- @param thd THD handle
-
- @return false on success, true if a failure in an event applying occurred.
-*/
-bool slave_execute_deferred_events(THD *thd)
-{
- bool res= false;
- rpl_group_info *rgi= thd->rgi_slave;
-
- DBUG_ASSERT(rgi && (!rgi->deferred_events_collecting || rgi->deferred_events));
-
- if (!rgi->deferred_events_collecting || rgi->deferred_events->is_empty())
- return res;
-
- res= rgi->deferred_events->execute(rgi);
- rgi->deferred_events->rewind();
-
- return res;
-}
-
-#endif /* !MYSQL_CLIENT */
-
-
/**************************************************************************
Xid_log_event methods
**************************************************************************/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Xid_log_event::pack_info(Protocol *protocol)
-{
- char buf[128], *pos;
- pos= strmov(buf, "COMMIT /* xid=");
- pos= longlong10_to_str(xid, pos, 10);
- pos= strmov(pos, " */");
- protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
-}
-#endif
-
/**
@note
It's ok not to use int8store here,
@@ -8938,7 +2789,7 @@ void Xid_log_event::pack_info(Protocol *protocol)
Xid_log_event::
Xid_log_event(const char* buf,
const Format_description_log_event *description_event)
- :Log_event(buf, description_event)
+ :Xid_apply_log_event(buf, description_event)
{
/* The Post-Header is empty. The Variable Data part begins immediately. */
buf+= description_event->common_header_len +
@@ -8946,273 +2797,48 @@ Xid_log_event(const char* buf,
memcpy((char*) &xid, buf, sizeof(xid));
}
-
-#ifndef MYSQL_CLIENT
-bool Xid_log_event::write()
-{
- DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
- return write_header(sizeof(xid)) ||
- write_data((uchar*)&xid, sizeof(xid)) ||
- write_footer();
-}
-#endif
-
-
-#ifdef MYSQL_CLIENT
-bool Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Xid_apply_log_event(buf, description_event)
{
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F, this);
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ one_phase= * (bool *) buf;
+ buf+= 1;
- if (!print_event_info->short_form)
+ m_xid.formatID= uint4korr(buf);
+ buf+= 4;
+ m_xid.gtrid_length= uint4korr(buf);
+ buf+= 4;
+ // Todo: validity here and elsewhere checks to be replaced by MDEV-21839 fixes
+ if (m_xid.gtrid_length <= 0 || m_xid.gtrid_length > MAXGTRIDSIZE)
{
- char buf[64];
- longlong10_to_str(xid, buf, 10);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\tXid = %s\n", buf))
- goto err;
+ m_xid.formatID= -1;
+ return;
}
- if (my_b_printf(&cache, is_flashback ? "START TRANSACTION%s\n" : "COMMIT%s\n",
- print_event_info->delimiter))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif /* MYSQL_CLIENT */
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-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;
- void *hton= NULL;
-
- /*
- 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
- THD::reset_for_next_command() as if starting a new statement.
- */
- thd->reset_for_next_command();
- /*
- Record any GTID in the same transaction, so slave state is transactionally
- consistent.
- */
-#ifdef WITH_WSREP
- thd->wsrep_affected_rows= 0;
-#endif
-
- if (rgi->gtid_pending)
+ m_xid.bqual_length= uint4korr(buf);
+ buf+= 4;
+ if (m_xid.bqual_length < 0 || m_xid.bqual_length > MAXBQUALSIZE)
{
- 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, &hton);
- if (unlikely(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;
- });
+ m_xid.formatID= -1;
+ return;
}
+ DBUG_ASSERT(m_xid.gtrid_length + m_xid.bqual_length <= XIDDATASIZE);
- /* 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();
-#ifdef WITH_WSREP
- if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data);
- if ((!res || (WSREP(thd) && thd->wsrep_trx().state() == wsrep::transaction::s_must_replay )) && sub_id)
-#else
- if (likely(!res) && sub_id)
-#endif /* WITH_WSREP */
- rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, rgi);
-#ifdef WITH_WSREP
- if (WSREP(thd)) mysql_mutex_unlock(&thd->LOCK_thd_data);
-#endif /* WITH_WSREP */
- /*
- Increment the global status commit count variable
- */
- status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
- return res;
+ xid= NULL;
}
-Log_event::enum_skip_reason
-Xid_log_event::do_shall_skip(rpl_group_info *rgi)
-{
- DBUG_ENTER("Xid_log_event::do_shall_skip");
- 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
- else if (WSREP(thd) && wsrep_mysql_replication_bundle &&
- opt_slave_domain_parallel_threads == 0)
- {
- if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
- {
- WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
- DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
- }
- else
- {
- thd->wsrep_mysql_replicated = 0;
- }
- }
-#endif
- DBUG_RETURN(Log_event::do_shall_skip(rgi));
-}
-#endif /* !MYSQL_CLIENT */
-
/**************************************************************************
User_var_log_event methods
**************************************************************************/
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-static bool
-user_var_append_name_part(THD *thd, String *buf,
- const char *name, size_t name_len)
-{
- return buf->append("@") ||
- append_identifier(thd, buf, name, name_len) ||
- buf->append("=");
-}
-
-void User_var_log_event::pack_info(Protocol* protocol)
-{
- if (is_null)
- {
- char buf_mem[FN_REFLEN+7];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- buf.length(0);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
- buf.append("NULL"))
- return;
- protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
- }
- else
- {
- switch (type) {
- case REAL_RESULT:
- {
- double real_val;
- char buf2[MY_GCVT_MAX_FIELD_WIDTH+1];
- char buf_mem[FN_REFLEN + MY_GCVT_MAX_FIELD_WIDTH + 1];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- float8get(real_val, val);
- buf.length(0);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
- buf.append(buf2, my_gcvt(real_val, MY_GCVT_ARG_DOUBLE,
- MY_GCVT_MAX_FIELD_WIDTH, buf2, NULL)))
- return;
- protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
- break;
- }
- case INT_RESULT:
- {
- char buf2[22];
- char buf_mem[FN_REFLEN + 22];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- buf.length(0);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
- buf.append(buf2,
- longlong10_to_str(uint8korr(val), buf2,
- ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10))-buf2))
- return;
- protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
- break;
- }
- case DECIMAL_RESULT:
- {
- char buf_mem[FN_REFLEN + DECIMAL_MAX_STR_LENGTH];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- char buf2[DECIMAL_MAX_STR_LENGTH+1];
- String str(buf2, sizeof(buf2), &my_charset_bin);
- buf.length(0);
- my_decimal((const uchar *) (val + 2), val[0], val[1]).to_string(&str);
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
- buf.append(buf2))
- return;
- protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
- break;
- }
- case STRING_RESULT:
- {
- /* 15 is for 'COLLATE' and other chars */
- char buf_mem[FN_REFLEN + 512 + 1 + 2*MY_CS_NAME_SIZE+15];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- CHARSET_INFO *cs;
- buf.length(0);
- if (!(cs= get_charset(charset_number, MYF(0))))
- {
- if (buf.append("???"))
- return;
- }
- else
- {
- size_t old_len;
- char *beg, *end;
- if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
- buf.append("_") ||
- buf.append(cs->csname) ||
- buf.append(" "))
- return;
- old_len= buf.length();
- if (buf.reserve(old_len + val_len * 2 + 3 + sizeof(" COLLATE ") +
- MY_CS_NAME_SIZE))
- return;
- beg= const_cast<char *>(buf.ptr()) + old_len;
- end= str_to_hex(beg, val, val_len);
- buf.length(old_len + (end - beg));
- if (buf.append(" COLLATE ") ||
- buf.append(cs->name))
- return;
- }
- protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
- break;
- }
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
- return;
- }
- }
-}
-#endif /* !MYSQL_CLIENT */
-
-
User_var_log_event::
User_var_log_event(const char* buf, uint event_len,
const Format_description_log_event* description_event)
@@ -9305,434 +2931,6 @@ err:
}
-#ifndef MYSQL_CLIENT
-bool User_var_log_event::write()
-{
- 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[MY_MAX(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
- uint unsigned_len= 0;
- uint buf1_length;
- size_t event_length;
-
- int4store(buf, name_len);
-
- if ((buf1[0]= is_null))
- {
- buf1_length= 1;
- val_len= 0; // Length of 'pos'
- }
- else
- {
- buf1[1]= type;
- int4store(buf1 + 2, charset_number);
-
- switch (type) {
- case REAL_RESULT:
- float8store(buf2, *(double*) val);
- break;
- case INT_RESULT:
- int8store(buf2, *(longlong*) val);
- unsigned_len= 1;
- break;
- case DECIMAL_RESULT:
- {
- my_decimal *dec= (my_decimal *)val;
- dec->fix_buffer_pointer();
- buf2[0]= (char)(dec->intg + dec->frac);
- buf2[1]= (char)dec->frac;
- decimal2bin((decimal_t*)val, buf2+2, buf2[0], buf2[1]);
- val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
- break;
- }
- case STRING_RESULT:
- pos= (uchar*) val;
- break;
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
- return 0;
- }
- int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
- buf1_length= 10;
- }
-
- /* Length of the whole event */
- event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len;
-
- return write_header(event_length) ||
- write_data(buf, sizeof(buf)) ||
- write_data(name, name_len) ||
- write_data(buf1, buf1_length) ||
- write_data(pos, val_len) ||
- write_data(&flags, unsigned_len) ||
- write_footer();
-}
-#endif
-
-
-/*
- User_var_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool User_var_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)
- {
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_write_string(&cache, "\tUser_var\n"))
- goto err;
- }
-
- if (my_b_write_string(&cache, "SET @") ||
- my_b_write_backtick_quote(&cache, name, name_len))
- goto err;
-
- if (is_null)
- {
- if (my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter))
- goto err;
- }
- else
- {
- switch (type) {
- case REAL_RESULT:
- double real_val;
- char real_buf[FMT_G_BUFSIZE(14)];
- float8get(real_val, val);
- sprintf(real_buf, "%.14g", real_val);
- if (my_b_printf(&cache, ":=%s%s\n", real_buf,
- print_event_info->delimiter))
- goto err;
- break;
- case INT_RESULT:
- char int_buf[22];
- longlong10_to_str(uint8korr(val), int_buf,
- ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10));
- if (my_b_printf(&cache, ":=%s%s\n", int_buf,
- print_event_info->delimiter))
- goto err;
- break;
- case DECIMAL_RESULT:
- {
- char str_buf[200];
- int str_len= sizeof(str_buf) - 1;
- int precision= (int)val[0];
- int scale= (int)val[1];
- decimal_digit_t dec_buf[10];
- decimal_t dec;
- dec.len= 10;
- dec.buf= dec_buf;
-
- bin2decimal((uchar*) val+2, &dec, precision, scale);
- decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
- str_buf[str_len]= 0;
- if (my_b_printf(&cache, ":=%s%s\n", str_buf,
- print_event_info->delimiter))
- goto err;
- break;
- }
- case STRING_RESULT:
- {
- /*
- Let's express the string in hex. That's the most robust way. If we
- print it in character form instead, we need to escape it with
- character_set_client which we don't know (we will know it in 5.0, but
- in 4.1 we don't know it easily when we are printing
- User_var_log_event). Explanation why we would need to bother with
- character_set_client (quoting Bar):
- > Note, the parser doesn't switch to another unescaping mode after
- > it has met a character set introducer.
- > For example, if an SJIS client says something like:
- > SET @a= _ucs2 \0a\0b'
- > the string constant is still unescaped according to SJIS, not
- > according to UCS2.
- */
- char *hex_str;
- CHARSET_INFO *cs;
- bool error;
-
- // 2 hex digits / byte
- hex_str= (char *) my_malloc(2 * val_len + 1 + 3, MYF(MY_WME));
- if (!hex_str)
- goto err;
- str_to_hex(hex_str, val, val_len);
- /*
- For proper behaviour when mysqlbinlog|mysql, we need to explicitly
- specify the variable's collation. It will however cause problems when
- people want to mysqlbinlog|mysql into another server not supporting the
- character set. But there's not much to do about this and it's unlikely.
- */
- if (!(cs= get_charset(charset_number, MYF(0))))
- { /*
- Generate an unusable command (=> syntax error) is probably the best
- thing we can do here.
- */
- error= my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
- }
- else
- error= my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
- cs->csname, hex_str, cs->name,
- print_event_info->delimiter);
- my_free(hex_str);
- if (unlikely(error))
- goto err;
- break;
- }
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
- break;
- }
- }
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif
-
-
-/*
- User_var_log_event::do_apply_event()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-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 (rgi->deferred_events_collecting)
- {
- set_deferred(current_thd->query_id);
- DBUG_RETURN(rgi->deferred_events->add(this));
- }
- else if (is_deferred())
- {
- sav_query_id= current_thd->query_id;
- current_thd->query_id= query_id; /* recreating original time context */
- }
-
- if (!(charset= get_charset(charset_number, MYF(MY_WME))))
- {
- rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Invalid character set for User var event");
- DBUG_RETURN(1);
- }
- LEX_CSTRING user_var_name;
- user_var_name.str= name;
- user_var_name.length= name_len;
- double real_val;
- longlong int_val;
-
- if (is_null)
- {
- it= new (thd->mem_root) Item_null(thd);
- }
- else
- {
- switch (type) {
- case REAL_RESULT:
- if (val_len != 8)
- {
- rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Invalid variable length at User var event");
- return 1;
- }
- float8get(real_val, val);
- it= new (thd->mem_root) Item_float(thd, real_val, 0);
- val= (char*) &real_val; // Pointer to value in native format
- val_len= 8;
- break;
- case INT_RESULT:
- if (val_len != 8)
- {
- rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Invalid variable length at User var event");
- return 1;
- }
- int_val= (longlong) uint8korr(val);
- it= new (thd->mem_root) Item_int(thd, int_val);
- val= (char*) &int_val; // Pointer to value in native format
- val_len= 8;
- break;
- case DECIMAL_RESULT:
- {
- if (val_len < 3)
- {
- rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
- ER_THD(thd, ER_SLAVE_FATAL_ERROR),
- "Invalid variable length at User var event");
- return 1;
- }
- Item_decimal *dec= new (thd->mem_root) Item_decimal(thd, (uchar*) val+2, val[0], val[1]);
- it= dec;
- val= (char *)dec->val_decimal(NULL);
- val_len= sizeof(my_decimal);
- break;
- }
- case STRING_RESULT:
- it= new (thd->mem_root) Item_string(thd, val, (uint)val_len, charset);
- break;
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
- DBUG_RETURN(0);
- }
- }
-
- Item_func_set_user_var *e= new (thd->mem_root) Item_func_set_user_var(thd, &user_var_name, it);
- /*
- Item_func_set_user_var can't substitute something else on its place =>
- 0 can be passed as last argument (reference on item)
-
- Fix_fields() can fail, in which case a call of update_hash() might
- crash the server, so if fix fields fails, we just return with an
- error.
- */
- if (e->fix_fields(thd, 0))
- DBUG_RETURN(1);
-
- /*
- A variable can just be considered as a table with
- a single record and with a single column. Thus, like
- a column value, it could always have IMPLICIT derivation.
- */
- e->update_hash((void*) val, val_len, type, charset,
- (flags & User_var_log_event::UNSIGNED_F));
- if (!is_deferred())
- free_root(thd->mem_root, 0);
- else
- current_thd->query_id= sav_query_id; /* restore current query's context */
-
- DBUG_RETURN(0);
-}
-
-int User_var_log_event::do_update_pos(rpl_group_info *rgi)
-{
- rgi->inc_event_relay_log_pos();
- return 0;
-}
-
-Log_event::enum_skip_reason
-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
- of 2 when recovering from an insert which used a auto increment,
- rand, or user var. Therefore, if the slave skip counter is 1, we
- just say that this event should be skipped by ignoring it, meaning
- 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(rgi);
-}
-#endif /* !MYSQL_CLIENT */
-
-#ifdef HAVE_REPLICATION
-#ifdef MYSQL_CLIENT
-bool Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file_arg);
-
- if (what != ENCRYPTED)
- {
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\n# Unknown event\n"))
- goto err;
- }
- else if (my_b_printf(&cache, "# Encrypted event\n"))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif
-
-/**************************************************************************
- Stop_log_event methods
-**************************************************************************/
-
-/*
- Stop_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F, this);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_write_string(&cache, "\tStop\n"))
- return 1;
- return cache.flush_data();
-}
-#endif /* MYSQL_CLIENT */
-
-
-#ifndef MYSQL_CLIENT
-/*
- The master stopped. We used to clean up all temporary tables but
- this is useless as, as the master has shut down properly, it has
- written all DROP TEMPORARY TABLE (prepared statements' deletion is
- TODO only when we binlog prep stmts). We used to clean up
- slave_load_tmpdir, but this is useless as it has been cleared at the
- end of LOAD DATA INFILE. So we have nothing to do here. The place
- were we must do this cleaning is in
- Start_log_event_v3::do_apply_event(), not here. Because if we come
- here, the master was sane.
-
- This must only be called from the Slave SQL thread, since it calls
- Relay_log_info::flush().
-*/
-
-int Stop_log_event::do_update_pos(rpl_group_info *rgi)
-{
- int error= 0;
- 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.
- If we updated it, we will have incorrect master coordinates and this
- could give false triggers in MASTER_POS_WAIT() that we have reached
- the target position when in fact we have not.
- */
- if (rli->get_flag(Relay_log_info::IN_TRANSACTION))
- rgi->inc_event_relay_log_pos();
- else if (!rgi->is_parallel_exec)
- {
- rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi);
- rli->inc_group_relay_log_pos(0, rgi);
- if (rli->flush())
- error= 1;
- }
- DBUG_RETURN(error);
-}
-
-#endif /* !MYSQL_CLIENT */
-#endif /* HAVE_REPLICATION */
-
-
/**************************************************************************
Create_file_log_event methods
**************************************************************************/
@@ -9741,75 +2939,6 @@ int Stop_log_event::do_update_pos(rpl_group_info *rgi)
Create_file_log_event ctor
*/
-#ifndef MYSQL_CLIENT
-Create_file_log_event::
-Create_file_log_event(THD* thd_arg, sql_exchange* ex,
- const char* db_arg, const char* table_name_arg,
- List<Item>& fields_arg,
- bool is_concurrent_arg,
- enum enum_duplicates handle_dup,
- bool ignore,
- uchar* block_arg, uint block_len_arg, bool using_trans)
- :Load_log_event(thd_arg, ex, db_arg, table_name_arg, fields_arg,
- is_concurrent_arg,
- handle_dup, ignore, using_trans),
- fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
- file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
-{
- DBUG_ENTER("Create_file_log_event");
- sql_ex.force_new_format();
- DBUG_VOID_RETURN;
-}
-
-
-/*
- Create_file_log_event::write_data_body()
-*/
-
-bool Create_file_log_event::write_data_body()
-{
- bool res;
- if ((res= Load_log_event::write_data_body()) || fake_base)
- return res;
- return write_data("", 1) ||
- write_data(block, block_len);
-}
-
-
-/*
- Create_file_log_event::write_data_header()
-*/
-
-bool Create_file_log_event::write_data_header()
-{
- bool res;
- uchar buf[CREATE_FILE_HEADER_LEN];
- if ((res= Load_log_event::write_data_header()) || fake_base)
- return res;
- int4store(buf + CF_FILE_ID_OFFSET, file_id);
- return write_data(buf, CREATE_FILE_HEADER_LEN) != 0;
-}
-
-
-/*
- Create_file_log_event::write_base()
-*/
-
-bool Create_file_log_event::write_base()
-{
- bool res;
- fake_base= 1; // pretend we are Load event
- res= write();
- fake_base= 0;
- return res;
-}
-
-#endif /* !MYSQL_CLIENT */
-
-/*
- Create_file_log_event ctor
-*/
-
Create_file_log_event::Create_file_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Load_log_event(buf,0,description_event),fake_base(0),block(0),inited_from_old(0)
@@ -9819,7 +2948,7 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
uint header_len= description_event->common_header_len;
uint8 load_header_len= description_event->post_header_len[LOAD_EVENT-1];
uint8 create_file_header_len= description_event->post_header_len[CREATE_FILE_EVENT-1];
- if (!(event_buf= (char*) my_memdup(buf, len, MYF(MY_WME))) ||
+ if (!(event_buf= (char*) my_memdup(PSI_INSTRUMENT_ME, buf, len, MYF(MY_WME))) ||
copy_log_event(event_buf,len,
(((uchar)buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
load_header_len + header_len :
@@ -9861,172 +2990,6 @@ Create_file_log_event::Create_file_log_event(const char* buf, uint len,
}
-/*
- Create_file_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Create_file_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info,
- bool enable_local)
-{
- if (print_event_info->short_form)
- {
- if (enable_local && check_fname_outside_temp_buf())
- return Load_log_event::print(file, print_event_info);
- return 0;
- }
-
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
- if (enable_local)
- {
- if (Load_log_event::print(file, print_event_info,
- !check_fname_outside_temp_buf()))
- goto err;
-
- /**
- reduce the size of io cache so that the write function is called
- for every call to my_b_printf().
- */
- DBUG_EXECUTE_IF ("simulate_create_event_write_error",
- {(&cache)->write_pos= (&cache)->write_end;
- DBUG_SET("+d,simulate_file_write_error");});
- /*
- That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
- SHOW BINLOG EVENTS we don't.
- */
- if (my_b_write_byte(&cache, '#'))
- goto err;
- }
-
- if (my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-
-}
-
-
-bool Create_file_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- return print(file, print_event_info, 0);
-}
-#endif /* MYSQL_CLIENT */
-
-
-/*
- Create_file_log_event::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Create_file_log_event::pack_info(Protocol *protocol)
-{
- char buf[SAFE_NAME_LEN*2 + 30 + 21*2], *pos;
- pos= strmov(buf, "db=");
- memcpy(pos, db, db_len);
- pos= strmov(pos + db_len, ";table=");
- memcpy(pos, table_name, table_name_len);
- pos= strmov(pos + table_name_len, ";file_id=");
- pos= int10_to_str((long) file_id, pos, 10);
- pos= strmov(pos, ";block_len=");
- pos= int10_to_str((long) block_len, pos, 10);
- protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
-}
-#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
-
-/**
- Create_file_log_event::do_apply_event()
- Constructor for Create_file_log_event to instantiate an event
- from the relay log on the slave.
-
- @retval
- 0 Success
- @retval
- 1 Failure
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
-{
- char fname_buf[FN_REFLEN];
- char *ext;
- int fd = -1;
- IO_CACHE file;
- Log_event_writer lew(&file, 0);
- 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));
- 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,
- fname_buf, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0 ||
- 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, rgi->gtid_info(),
- "Error in Create_file event: could not open file '%s'",
- fname_buf);
- goto err;
- }
-
- // a trick to avoid allocating another buffer
- fname= fname_buf;
- fname_len= (uint) (strmov(ext, ".data") - fname);
- writer= &lew;
- if (write_base())
- {
- strmov(ext, ".info"); // to have it right in the error message
- rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
- "Error in Create_file event: could not write to file '%s'",
- fname_buf);
- goto err;
- }
- end_io_cache(&file);
- mysql_file_close(fd, MYF(0));
-
- // fname_buf now already has .data, not .info, because we did our trick
- /* old copy may exist already */
- mysql_file_delete(key_file_log_event_data, fname_buf, MYF(0));
- if ((fd= mysql_file_create(key_file_log_event_data,
- fname_buf, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
- {
- 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, rgi->gtid_info(),
- "Error in Create_file event: write to '%s' failed",
- fname_buf);
- goto err;
- }
- error=0; // Everything is ok
-
-err:
- if (unlikely(error))
- end_io_cache(&file);
- if (likely(fd >= 0))
- mysql_file_close(fd, MYF(0));
- return error != 0;
-}
-#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
-
/**************************************************************************
Append_block_log_event methods
**************************************************************************/
@@ -10035,23 +2998,6 @@ err:
Append_block_log_event ctor
*/
-#ifndef MYSQL_CLIENT
-Append_block_log_event::Append_block_log_event(THD *thd_arg,
- const char *db_arg,
- uchar *block_arg,
- uint block_len_arg,
- bool using_trans)
- :Log_event(thd_arg,0, using_trans), block(block_arg),
- block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
-{
-}
-#endif
-
-
-/*
- Append_block_log_event ctor
-*/
-
Append_block_log_event::Append_block_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event),block(0)
@@ -10070,140 +3016,6 @@ Append_block_log_event::Append_block_log_event(const char* buf, uint len,
}
-/*
- Append_block_log_event::write()
-*/
-
-#ifndef MYSQL_CLIENT
-bool Append_block_log_event::write()
-{
- uchar buf[APPEND_BLOCK_HEADER_LEN];
- int4store(buf + AB_FILE_ID_OFFSET, file_id);
- return write_header(APPEND_BLOCK_HEADER_LEN + block_len) ||
- write_data(buf, APPEND_BLOCK_HEADER_LEN) ||
- write_data(block, block_len) ||
- write_footer();
-}
-#endif
-
-
-/*
- Append_block_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Append_block_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\n#%s: file_id: %d block_len: %d\n",
- get_type_str(), file_id, block_len))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif /* MYSQL_CLIENT */
-
-
-/*
- Append_block_log_event::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Append_block_log_event::pack_info(Protocol *protocol)
-{
- char buf[256];
- uint length;
- length= (uint) sprintf(buf, ";file_id=%u;block_len=%u", file_id, block_len);
- protocol->store(buf, length, &my_charset_bin);
-}
-
-
-/*
- Append_block_log_event::get_create_or_append()
-*/
-
-int Append_block_log_event::get_create_or_append() const
-{
- return 0; /* append to the file, fail if not exists */
-}
-
-/*
- Append_block_log_event::do_apply_event()
-*/
-
-int Append_block_log_event::do_apply_event(rpl_group_info *rgi)
-{
- 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");
-
- 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())
- {
- /*
- Usually lex_start() is called by mysql_parse(), but we need it here
- as the present method does not call mysql_parse().
- */
- lex_start(thd);
- thd->reset_for_next_command();
- /* old copy may exist already */
- mysql_file_delete(key_file_log_event_data, fname, MYF(0));
- if ((fd= mysql_file_create(key_file_log_event_data,
- fname, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
- {
- rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
- "Error in %s event: could not create file '%s'",
- get_type_str(), fname);
- goto err;
- }
- }
- else if ((fd= mysql_file_open(key_file_log_event_data,
- fname,
- O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
- MYF(MY_WME))) < 0)
- {
- rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
- "Error in %s event: could not open file '%s'",
- get_type_str(), fname);
- goto err;
- }
-
- DBUG_EXECUTE_IF("remove_slave_load_file_before_write",
- {
- my_delete(fname, MYF(0));
- });
-
- if (mysql_file_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
- {
- rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
- "Error in %s event: write to '%s' failed",
- get_type_str(), fname);
- goto err;
- }
- error=0;
-
-err:
- if (fd >= 0)
- mysql_file_close(fd, MYF(0));
- DBUG_RETURN(error);
-}
-#endif
-
-
/**************************************************************************
Delete_file_log_event methods
**************************************************************************/
@@ -10212,18 +3024,6 @@ err:
Delete_file_log_event ctor
*/
-#ifndef MYSQL_CLIENT
-Delete_file_log_event::Delete_file_log_event(THD *thd_arg, const char* db_arg,
- bool using_trans)
- :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
-{
-}
-#endif
-
-/*
- Delete_file_log_event ctor
-*/
-
Delete_file_log_event::Delete_file_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event),file_id(0)
@@ -10236,76 +3036,6 @@ Delete_file_log_event::Delete_file_log_event(const char* buf, uint len,
}
-/*
- Delete_file_log_event::write()
-*/
-
-#ifndef MYSQL_CLIENT
-bool Delete_file_log_event::write()
-{
- uchar buf[DELETE_FILE_HEADER_LEN];
- int4store(buf + DF_FILE_ID_OFFSET, file_id);
- return write_header(sizeof(buf)) ||
- write_data(buf, sizeof(buf)) ||
- write_footer();
-}
-#endif
-
-
-/*
- Delete_file_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Delete_file_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id))
- return 1;
-
- return cache.flush_data();
-}
-#endif /* MYSQL_CLIENT */
-
-/*
- Delete_file_log_event::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Delete_file_log_event::pack_info(Protocol *protocol)
-{
- char buf[64];
- uint length;
- length= (uint) sprintf(buf, ";file_id=%u", (uint) file_id);
- protocol->store(buf, (int32) length, &my_charset_bin);
-}
-#endif
-
-/*
- Delete_file_log_event::do_apply_event()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Delete_file_log_event::do_apply_event(rpl_group_info *rgi)
-{
- char fname[FN_REFLEN+10];
- 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));
- return 0;
-}
-#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
-
/**************************************************************************
Execute_load_log_event methods
**************************************************************************/
@@ -10314,20 +3044,6 @@ int Delete_file_log_event::do_apply_event(rpl_group_info *rgi)
Execute_load_log_event ctor
*/
-#ifndef MYSQL_CLIENT
-Execute_load_log_event::Execute_load_log_event(THD *thd_arg,
- const char* db_arg,
- bool using_trans)
- :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
-{
-}
-#endif
-
-
-/*
- Execute_load_log_event ctor
-*/
-
Execute_load_log_event::Execute_load_log_event(const char* buf, uint len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event), file_id(0)
@@ -10340,167 +3056,10 @@ Execute_load_log_event::Execute_load_log_event(const char* buf, uint len,
}
-/*
- Execute_load_log_event::write()
-*/
-
-#ifndef MYSQL_CLIENT
-bool Execute_load_log_event::write()
-{
- uchar buf[EXEC_LOAD_HEADER_LEN];
- int4store(buf + EL_FILE_ID_OFFSET, file_id);
- return write_header(sizeof(buf)) ||
- write_data(buf, sizeof(buf)) ||
- write_footer();
-}
-#endif
-
-
-/*
- Execute_load_log_event::print()
-*/
-
-#ifdef MYSQL_CLIENT
-bool Execute_load_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
- file_id))
- return 1;
-
- return cache.flush_data();
-}
-#endif
-
-/*
- Execute_load_log_event::pack_info()
-*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Execute_load_log_event::pack_info(Protocol *protocol)
-{
- char buf[64];
- uint length;
- length= (uint) sprintf(buf, ";file_id=%u", (uint) file_id);
- protocol->store(buf, (int32) length, &my_charset_bin);
-}
-
-
-/*
- Execute_load_log_event::do_apply_event()
-*/
-
-int Execute_load_log_event::do_apply_event(rpl_group_info *rgi)
-{
- char fname[FN_REFLEN+10];
- char *ext;
- int fd;
- 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",
- &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, rgi->gtid_info(),
- "Error in Exec_load event: could not open file '%s'",
- fname);
- goto err;
- }
- if (!(lev= (Load_log_event*)
- Log_event::read_log_event(&file,
- rli->relay_log.description_event_for_exec,
- opt_slave_sql_verify_checksum)) ||
- lev->get_type_code() != NEW_LOAD_EVENT)
- {
- rli->report(ERROR_LEVEL, 0, rgi->gtid_info(), "Error in Exec_load event: "
- "file '%s' appears corrupted", fname);
- goto err;
- }
- lev->thd = thd;
- /*
- lev->do_apply_event should use rli only for errors i.e. should
- not advance rli's position.
-
- lev->do_apply_event is the place where the table is loaded (it
- calls mysql_load()).
- */
-
- if (lev->do_apply_event(0,rgi,1))
- {
- /*
- We want to indicate the name of the file that could not be loaded
- (SQL_LOADxxx).
- But as we are here we are sure the error is in rli->last_slave_error and
- rli->last_slave_errno (example of error: duplicate entry for key), so we
- don't want to overwrite it with the filename.
- What we want instead is add the filename to the current error message.
- */
- char *tmp= my_strdup(rli->last_error().message, MYF(MY_WME));
- if (tmp)
- {
- rli->report(ERROR_LEVEL, rli->last_error().number, rgi->gtid_info(),
- "%s. Failed executing load from '%s'", tmp, fname);
- my_free(tmp);
- }
- goto err;
- }
- /*
- We have an open file descriptor to the .info file; we need to close it
- or Windows will refuse to delete the file in mysql_file_delete().
- */
- if (fd >= 0)
- {
- mysql_file_close(fd, MYF(0));
- end_io_cache(&file);
- fd= -1;
- }
- mysql_file_delete(key_file_log_event_info, fname, MYF(MY_WME));
- memcpy(ext, ".data", 6);
- mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
- error = 0;
-
-err:
- delete lev;
- if (fd >= 0)
- {
- mysql_file_close(fd, MYF(0));
- end_io_cache(&file);
- }
- return error;
-}
-
-#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
-
/**************************************************************************
Begin_load_query_log_event methods
**************************************************************************/
-#ifndef MYSQL_CLIENT
-Begin_load_query_log_event::
-Begin_load_query_log_event(THD* thd_arg, const char* db_arg, uchar* block_arg,
- uint block_len_arg, bool using_trans)
- :Append_block_log_event(thd_arg, db_arg, block_arg, block_len_arg,
- using_trans)
-{
- file_id= thd_arg->file_id= mysql_bin_log.next_file_id();
-}
-#endif
-
-
Begin_load_query_log_event::
Begin_load_query_log_event(const char* buf, uint len,
const Format_description_log_event* desc_event)
@@ -10509,49 +3068,11 @@ Begin_load_query_log_event(const char* buf, uint len,
}
-#if defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Begin_load_query_log_event::get_create_or_append() const
-{
- return 1; /* create the file */
-}
-#endif /* defined( HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-Log_event::enum_skip_reason
-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(rgi);
-}
-#endif
-
-
/**************************************************************************
Execute_load_query_log_event methods
**************************************************************************/
-#ifndef MYSQL_CLIENT
-Execute_load_query_log_event::
-Execute_load_query_log_event(THD *thd_arg, const char* query_arg,
- ulong query_length_arg, uint fn_pos_start_arg,
- uint fn_pos_end_arg,
- enum_load_dup_handling dup_handling_arg,
- bool using_trans, bool direct, bool suppress_use,
- int errcode):
- Query_log_event(thd_arg, query_arg, query_length_arg, using_trans, direct,
- suppress_use, errcode),
- file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg),
- fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg)
-{
-}
-#endif /* !MYSQL_CLIENT */
-
-
Execute_load_query_log_event::
Execute_load_query_log_event(const char* buf, uint event_len,
const Format_description_log_event* desc_event):
@@ -10581,165 +3102,6 @@ ulong Execute_load_query_log_event::get_post_header_size_for_derived()
}
-#ifndef MYSQL_CLIENT
-bool
-Execute_load_query_log_event::write_post_header_for_derived()
-{
- uchar buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
- int4store(buf, file_id);
- int4store(buf + 4, fn_pos_start);
- int4store(buf + 4 + 4, fn_pos_end);
- *(buf + 4 + 4 + 4)= (uchar) dup_handling;
- return write_data(buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
-}
-#endif
-
-
-#ifdef MYSQL_CLIENT
-bool Execute_load_query_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info)
-{
- return print(file, print_event_info, 0);
-}
-
-/**
- Prints the query as LOAD DATA LOCAL and with rewritten filename.
-*/
-bool Execute_load_query_log_event::print(FILE* file,
- PRINT_EVENT_INFO* print_event_info,
- const char *local_fname)
-{
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
- if (print_query_header(&cache, print_event_info))
- goto err;
-
- /**
- reduce the size of io cache so that the write function is called
- for every call to my_b_printf().
- */
- DBUG_EXECUTE_IF ("simulate_execute_event_write_error",
- {(&cache)->write_pos= (&cache)->write_end;
- DBUG_SET("+d,simulate_file_write_error");});
-
- if (local_fname)
- {
- if (my_b_write(&cache, (uchar*) query, fn_pos_start) ||
- my_b_write_string(&cache, " LOCAL INFILE ") ||
- pretty_print_str(&cache, local_fname, (int)strlen(local_fname)))
- goto err;
-
- if (dup_handling == LOAD_DUP_REPLACE)
- if (my_b_write_string(&cache, " REPLACE"))
- goto err;
-
- if (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))
- goto err;
- }
- else
- {
- if (my_b_write(&cache, (uchar*) query, q_len) ||
- my_b_printf(&cache, "\n%s\n", print_event_info->delimiter))
- goto err;
- }
-
- if (!print_event_info->short_form)
- my_b_printf(&cache, "# file_id: %d \n", file_id);
-
- return cache.flush_data();
-err:
- return 1;
-}
-#endif
-
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Execute_load_query_log_event::pack_info(Protocol *protocol)
-{
- char buf_mem[1024];
- String buf(buf_mem, sizeof(buf_mem), system_charset_info);
- buf.real_alloc(9 + db_len + q_len + 10 + 21);
- if (db && db_len)
- {
- if (buf.append(STRING_WITH_LEN("use ")) ||
- append_identifier(protocol->thd, &buf, db, db_len) ||
- buf.append(STRING_WITH_LEN("; ")))
- return;
- }
- if (query && q_len && buf.append(query, q_len))
- return;
- if (buf.append(" ;file_id=") ||
- buf.append_ulonglong(file_id))
- return;
- protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
-}
-
-
-int
-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));
-
- DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", my_free(buf); buf= NULL;);
-
- /* Replace filename and LOCAL keyword in query before executing it */
- if (buf == NULL)
- {
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
- ER_THD(rgi->thd, ER_SLAVE_FATAL_ERROR), "Not enough memory");
- return 1;
- }
-
- p= buf;
- 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",
- &rli->mi->cmp_connection_name);
- fname_end= p= strend(p); // Safer than p=p+5
- *(p++)='\'';
- switch (dup_handling) {
- case LOAD_DUP_IGNORE:
- p= strmake(p, STRING_WITH_LEN(" IGNORE"));
- break;
- case LOAD_DUP_REPLACE:
- p= strmake(p, STRING_WITH_LEN(" REPLACE"));
- break;
- default:
- /* Ordinary load data */
- break;
- }
- 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(rgi, buf, (uint32)(p-buf));
-
- /* Forging file name for deletion in same buffer */
- *fname_end= 0;
-
- /*
- If there was an error the slave is going to stop, leave the
- file so that we can re-execute this event at START SLAVE.
- */
- if (unlikely(!error))
- mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
-
- my_free(buf);
- return error;
-}
-#endif
-
-
/**************************************************************************
sql_ex_info methods
**************************************************************************/
@@ -10796,105 +3158,12 @@ const char *sql_ex_info::init(const char *buf, const char *buf_end,
return buf;
}
-#ifndef MYSQL_CLIENT
-/*
- write_str()
-*/
-
-static bool write_str(Log_event_writer *writer, const char *str, uint length)
-{
- uchar tmp[1];
- tmp[0]= (uchar) length;
- return (writer->write_data(tmp, sizeof(tmp)) ||
- writer->write_data((uchar*) str, length));
-}
-
-/*
- sql_ex_info::write_data()
-*/
-
-bool sql_ex_info::write_data(Log_event_writer *writer)
-{
- if (new_format())
- {
- return write_str(writer, field_term, field_term_len) ||
- write_str(writer, enclosed, enclosed_len) ||
- write_str(writer, line_term, line_term_len) ||
- write_str(writer, line_start, line_start_len) ||
- write_str(writer, escaped, escaped_len) ||
- writer->write_data((uchar*) &opt_flags, 1);
- }
- else
- {
- uchar old_ex[7];
- old_ex[0]= *field_term;
- old_ex[1]= *enclosed;
- old_ex[2]= *line_term;
- old_ex[3]= *line_start;
- old_ex[4]= *escaped;
- old_ex[5]= opt_flags;
- old_ex[6]= empty_flags;
- return writer->write_data(old_ex, sizeof(old_ex));
- }
-}
-
/**************************************************************************
Rows_log_event member functions
**************************************************************************/
-Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
- 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_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),
- master_had_triggers(0)
-#endif
-{
- /*
- We allow a special form of dummy event when the table, and cols
- are null and the table id is ~0UL. This is a temporary
- solution, to be able to terminate a started statement in the
- binary log: the extraneous events will be removed in the future.
- */
- DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
- (!tbl_arg && !cols && tid == ~0UL));
-
- if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)
- set_flags(NO_FOREIGN_KEY_CHECKS_F);
- if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS)
- set_flags(RELAXED_UNIQUE_CHECKS_F);
- if (thd_arg->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS)
- set_flags(NO_CHECK_CONSTRAINT_CHECKS_F);
- /* 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)))
- {
- /* Cols can be zero if this is a dummy binrows event */
- if (likely(cols != NULL))
- {
- memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
- create_last_word_mask(&m_cols);
- }
- }
- else
- {
- // 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,
const Format_description_log_event
@@ -10983,7 +3252,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
/* 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,
+ m_extra_row_data= (uchar*) my_malloc(PSI_INSTRUMENT_ME, infoLen,
MYF(MY_WME));
if (likely(m_extra_row_data != NULL))
{
@@ -11072,7 +3341,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
DBUG_PRINT("info",("m_table_id: %llu m_flags: %d m_width: %lu data_size: %lu",
m_table_id, m_flags, m_width, (ulong) data_size));
- m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
+ m_rows_buf= (uchar*) my_malloc(PSI_INSTRUMENT_ME, data_size, MYF(MY_WME));
if (likely((bool)m_rows_buf))
{
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -11095,7 +3364,7 @@ void Rows_log_event::uncompress_buf()
if (!un_len)
return;
- uchar *new_buf= (uchar*) my_malloc(ALIGN_SIZE(un_len), MYF(MY_WME));
+ uchar *new_buf= (uchar*) my_malloc(PSI_INSTRUMENT_ME, ALIGN_SIZE(un_len), MYF(MY_WME));
if (new_buf)
{
if(!binlog_buf_uncompress((char *)m_rows_buf, (char *)new_buf,
@@ -11164,1153 +3433,10 @@ int Rows_log_event::get_data_size()
}
-#ifndef MYSQL_CLIENT
-int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
-{
- /*
- When the table has a primary key, we would probably want, by default, to
- log only the primary key value instead of the entire "before image". This
- would save binlog space. TODO
- */
- DBUG_ENTER("Rows_log_event::do_add_row_data");
- DBUG_PRINT("enter", ("row_data:%p length: %lu", row_data,
- (ulong) length));
-
- /*
- If length is zero, there is nothing to write, so we just
- return. Note that this is not an optimization, since calling
- realloc() with size 0 means free().
- */
- if (length == 0)
- {
- m_row_count++;
- DBUG_RETURN(0);
- }
-
- /*
- Don't print debug messages when running valgrind since they can
- trigger false warnings.
- */
-#ifndef HAVE_valgrind
- DBUG_DUMP("row_data", row_data, MY_MIN(length, 32));
-#endif
-
- DBUG_ASSERT(m_rows_buf <= m_rows_cur);
- DBUG_ASSERT(!m_rows_buf || (m_rows_end && m_rows_buf < m_rows_end));
- DBUG_ASSERT(m_rows_cur <= m_rows_end);
-
- /* The cast will always work since m_rows_cur <= m_rows_end */
- if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
- {
- size_t const block_size= 1024;
- size_t cur_size= m_rows_cur - m_rows_buf;
- DBUG_EXECUTE_IF("simulate_too_big_row_case1",
- cur_size= UINT_MAX32 - (block_size * 10);
- length= UINT_MAX32 - (block_size * 10););
- DBUG_EXECUTE_IF("simulate_too_big_row_case2",
- cur_size= UINT_MAX32 - (block_size * 10);
- length= block_size * 10;);
- DBUG_EXECUTE_IF("simulate_too_big_row_case3",
- cur_size= block_size * 10;
- length= UINT_MAX32 - (block_size * 10););
- DBUG_EXECUTE_IF("simulate_too_big_row_case4",
- cur_size= UINT_MAX32 - (block_size * 10);
- length= (block_size * 10) - block_size + 1;);
- size_t remaining_space= UINT_MAX32 - cur_size;
- /* Check that the new data fits within remaining space and we can add
- block_size without wrapping.
- */
- if (cur_size > UINT_MAX32 || length > remaining_space ||
- ((length + block_size) > remaining_space))
- {
- sql_print_error("The row data is greater than 4GB, which is too big to "
- "write to the binary log.");
- DBUG_RETURN(ER_BINLOG_ROW_LOGGING_FAILED);
- }
- size_t const new_alloc=
- block_size * ((cur_size + length + block_size - 1) / block_size);
-
- uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, new_alloc,
- MYF(MY_ALLOW_ZERO_PTR|MY_WME));
- if (unlikely(!new_buf))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-
- /* If the memory moved, we need to move the pointers */
- if (new_buf != m_rows_buf)
- {
- m_rows_buf= new_buf;
- m_rows_cur= m_rows_buf + cur_size;
- }
-
- /*
- The end pointer should always be changed to point to the end of
- the allocated memory.
- */
- m_rows_end= m_rows_buf + new_alloc;
- }
-
- DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
- memcpy(m_rows_cur, row_data, length);
- m_rows_cur+= length;
- m_row_count++;
- DBUG_RETURN(0);
-}
-#endif
-
-#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)
-{
- 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;
-}
-
-
-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;
- /*
- If m_table_id == ~0ULL, then we have a dummy event that does not
- contain any data. In that case, we just remove all tables in the
- tables_to_lock list, close the thread tables, and return with
- success.
- */
- if (m_table_id == ~0ULL)
- {
- /*
- This one is supposed to be set: just an extra check so that
- nothing strange has happened.
- */
- DBUG_ASSERT(get_flags(STMT_END_F));
-
- rgi->slave_close_thread_tables(thd);
- thd->clear_error();
- DBUG_RETURN(0);
- }
-
- /*
- 'thd' has been set by exec_relay_log_event(), just before calling
- do_apply_event(). We still check here to prevent future coding
- errors.
- */
- DBUG_ASSERT(rgi->thd == thd);
-
- /*
- If there is no locks taken, this is the first binrow event seen
- after the table map events. We should then lock all the tables
- used in the transaction and proceed with execution of the actual
- event.
- */
- if (!thd->lock)
- {
- /*
- Lock_tables() reads the contents of thd->lex, so they must be
- initialized.
-
- We also call the THD::reset_for_next_command(), since this
- is the logical start of the next "statement". Note that this
- 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);
- thd->reset_for_next_command();
- /*
- The current statement is just about to begin and
- has not yet modified anything. Note, all.modified is reset
- by THD::reset_for_next_command().
- */
- thd->transaction.stmt.modified_non_trans_table= FALSE;
- thd->transaction.stmt.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
- /*
- This is a row injection, so we flag the "statement" as
- such. Note that this code is called both when the slave does row
- injections and when the BINLOG statement is used to do row
- injections.
- */
- thd->lex->set_stmt_row_injection();
-
- /*
- There are a few flags that are replicated with each row event.
- Make sure to set/clear them before executing the main body of
- the event.
- */
- if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
- thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
- else
- thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
-
- if (get_flags(RELAXED_UNIQUE_CHECKS_F))
- thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
- else
- thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
-
- if (get_flags(NO_CHECK_CONSTRAINT_CHECKS_F))
- thd->variables.option_bits|= OPTION_NO_CHECK_CONSTRAINT_CHECKS;
- else
- thd->variables.option_bits&= ~OPTION_NO_CHECK_CONSTRAINT_CHECKS;
-
- /* A small test to verify that objects have consistent types */
- DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
-
- DBUG_EXECUTE_IF("rows_log_event_before_open_table",
- {
- const char action[] = "now SIGNAL before_open_table WAIT_FOR go_ahead_sql";
- DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(action)));
- };);
-
- 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 (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0)))
- {
-#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->get_stmt_da()->sql_errno(),
- thd->is_fatal_error,
- thd->wsrep_cs().mode(),
- thd->wsrep_trx().state(),
- (long long) wsrep_thd_trx_seqno(thd));
- }
-#endif /* WITH_WSREP */
- if (thd->is_error() &&
- !is_parallel_retry_error(rgi, error= thd->get_stmt_da()->sql_errno()))
- {
- /*
- Error reporting borrowed from Query_log_event with many excessive
- simplifications.
- We should not honour --slave-skip-errors at this point as we are
- having severe errors which should not be skipped.
- */
- rli->report(ERROR_LEVEL, error, rgi->gtid_info(),
- "Error executing row event: '%s'",
- (error ? thd->get_stmt_da()->message() :
- "unexpected success or fatal error"));
- thd->is_slave_error= 1;
- }
- /* remove trigger's tables */
- goto err;
- }
-
- /*
- When the open and locking succeeded, we check all tables to
- ensure that they still have the correct type.
- */
-
- {
- DBUG_PRINT("debug", ("Checking compatibility of tables to lock - tables_to_lock: %p",
- rgi->tables_to_lock));
-
- /**
- When using RBR and MyISAM MERGE tables the base tables that make
- up the MERGE table can be appended to the list of tables to lock.
-
- Thus, we just check compatibility for those that tables that have
- a correspondent table map event (ie, those that are actually going
- to be accessed while applying the event). That's why the loop stops
- at rli->tables_to_lock_count .
-
- NOTE: The base tables are added here are removed when
- close_thread_tables is called.
- */
- TABLE_LIST *table_list_ptr= rgi->tables_to_lock;
- for (uint i=0 ; table_list_ptr && (i < rgi->tables_to_lock_count);
- table_list_ptr= table_list_ptr->next_global, i++)
- {
- /*
- Below if condition takes care of skipping base tables that
- make up the MERGE table (which are added by open_tables()
- call). They are added next to the merge table in the list.
- For eg: If RPL_TABLE_LIST is t3->t1->t2 (where t1 and t2
- are base tables for merge table 't3'), open_tables will modify
- the list by adding t1 and t2 again immediately after t3 in the
- list (*not at the end of the list*). New table_to_lock list will
- look like t3->t1'->t2'->t1->t2 (where t1' and t2' are TABLE_LIST
- objects added by open_tables() call). There is no flag(or logic) in
- open_tables() that can skip adding these base tables to the list.
- So the logic here should take care of skipping them.
-
- tables_to_lock_count logic will take care of skipping base tables
- that are added at the end of the list.
- For eg: If RPL_TABLE_LIST is t1->t2->t3, open_tables will modify
- the list into t1->t2->t3->t1'->t2'. t1' and t2' will be skipped
- because tables_to_lock_count logic in this for loop.
- */
- if (table_list_ptr->parent_l)
- continue;
- /*
- We can use a down cast here since we know that every table added
- to the tables_to_lock is a RPL_TABLE_LIST (or child table which is
- skipped above).
- */
- RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(table_list_ptr);
- DBUG_ASSERT(ptr->m_tabledef_valid);
- 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,
- ptr->table->s->table_name.str));
- /*
- We should not honour --slave-skip-errors at this point as we are
- having severe errors which should not be skipped.
- */
- thd->is_slave_error= 1;
- /* remove trigger's tables */
- error= ERR_BAD_TABLE_DEF;
- goto err;
- }
- DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
- " - conv_table: %p",
- ptr->table->s->db.str,
- ptr->table->s->table_name.str, conv_table));
- ptr->m_conv_table= conv_table;
- }
- }
-
- /*
- ... and then we add all the tables to the table map and but keep
- them in the tables to lock list.
-
- We also invalidate the query cache for all the tables, since
- they will now be changed.
-
- TODO [/Matz]: Maybe the query cache should not be invalidated
- here? It might be that a table is not changed, even though it
- was locked for the statement. We do know that each
- Rows_log_event contain at least one row, so after processing one
- Rows_log_event, we can invalidate the query cache for the
- associated table.
- */
- TABLE_LIST *ptr= rgi->tables_to_lock;
- for (uint i=0 ; ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
- {
- /*
- Please see comment in above 'for' loop to know the reason
- for this if condition
- */
- if (ptr->parent_l)
- continue;
- 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
- /*
- Moved invalidation right before the call to rows_event_stmt_cleanup(),
- to avoid query cache being polluted with stale entries,
- */
- if (! (WSREP(thd) && wsrep_thd_is_applying(thd)))
- {
-#endif /* WITH_WSREP */
- query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
-#endif
- }
-
- table= m_table= rgi->m_table_map.get_table(m_table_id);
-
- DBUG_PRINT("debug", ("m_table:%p, m_table_id: %llu%s",
- 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
- (this was set up by Table_map_log_event::do_apply_event()
- which tested replicate-* rules).
- */
-
- /*
- It's not needed to set_time() but
- 1) it continues the property that "Time" in SHOW PROCESSLIST shows how
- much slave is behind
- 2) it will be needed when we allow replication from a table with no
- TIMESTAMP column to a table with one.
- So we call set_time(), like in SBR. Presently it changes nothing.
- */
- thd->set_time(when, when_sec_part);
-
- if (m_width == table->s->fields && bitmap_is_set_all(&m_cols))
- set_flags(COMPLETE_ROWS_F);
-
- /*
- Set tables write and read sets.
-
- Read_set contains all slave columns (in case we are going to fetch
- a complete record from slave)
-
- Write_set equals the m_cols bitmap sent from master but it can be
- longer if slave has extra columns.
- */
-
- DBUG_PRINT_BITSET("debug", "Setting table's read_set from: %s", &m_cols);
-
- bitmap_set_all(table->read_set);
- if (get_general_type_code() == DELETE_ROWS_EVENT ||
- get_general_type_code() == UPDATE_ROWS_EVENT)
- bitmap_intersect(table->read_set,&m_cols);
-
- bitmap_set_all(table->write_set);
- table->rpl_write_set= table->write_set;
-
- /* WRITE ROWS EVENTS store the bitmap in m_cols instead of m_cols_ai */
- MY_BITMAP *after_image= ((get_general_type_code() == UPDATE_ROWS_EVENT) ?
- &m_cols_ai : &m_cols);
- bitmap_intersect(table->write_set, after_image);
-
- this->slave_exec_mode= slave_exec_mode_options; // fix the mode
-
- // Do event specific preparations
- error= do_before_row_operations(rli);
-
- /*
- Bug#56662 Assertion failed: next_insert_id == 0, file handler.cc
- Don't allow generation of auto_increment value when processing
- rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'. The exception
- to this rule happens when the auto_inc column exists on some
- extra columns on the slave. In that case, do not force
- MODE_NO_AUTO_VALUE_ON_ZERO.
- */
- sql_mode_t saved_sql_mode= thd->variables.sql_mode;
- if (!is_auto_inc_in_extra_columns())
- thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO;
-
- // row processing loop
-
- /*
- set the initial time of this ROWS statement if it was not done
- before in some other ROWS event.
- */
- rgi->set_row_stmt_start_timestamp();
-
- THD_STAGE_INFO(thd, stage_executing);
- do
- {
- /* in_use can have been set to NULL in close_tables_for_reopen */
- THD* old_thd= table->in_use;
- if (!table->in_use)
- table->in_use= thd;
-
- error= do_exec_row(rgi);
-
- if (unlikely(error))
- DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
- DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
-
- table->in_use = old_thd;
-
- if (unlikely(error))
- {
- int actual_error= convert_handler_error(error, thd, table);
- bool idempotent_error= (idempotent_error_code(error) &&
- (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT));
- bool ignored_error= (idempotent_error == 0 ?
- ignored_error_code(actual_error) : 0);
-
-#ifdef WITH_WSREP
- if (WSREP(thd) && wsrep_ignored_error_code(this, actual_error))
- {
- idempotent_error= true;
- thd->wsrep_has_ignored_error= true;
- }
-#endif /* WITH_WSREP */
- if (idempotent_error || ignored_error)
- {
- if (global_system_variables.log_warnings)
- slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
- get_type_str(),
- RPL_LOG_NAME, log_pos);
- thd->clear_error(1);
- error= 0;
- if (idempotent_error == 0)
- break;
- }
- }
-
- /*
- If m_curr_row_end was not set during event execution (e.g., because
- of errors) we can't proceed to the next row. If the error is transient
- (i.e., error==0 at this point) we must call unpack_current_row() to set
- m_curr_row_end.
- */
-
- DBUG_PRINT("info", ("curr_row: %p; curr_row_end: %p; rows_end:%p",
- m_curr_row, m_curr_row_end, m_rows_end));
-
- if (!m_curr_row_end && likely(!error))
- error= unpack_current_row(rgi);
-
- m_curr_row= m_curr_row_end;
-
- if (likely(error == 0) && !transactional_table)
- thd->transaction.all.modified_non_trans_table=
- thd->transaction.stmt.modified_non_trans_table= TRUE;
- } // row processing loop
- while (error == 0 && (m_curr_row != m_rows_end));
-
- /*
- Restore the sql_mode after the rows event is processed.
- */
- thd->variables.sql_mode= saved_sql_mode;
-
- {/**
- The following failure injecion works in cooperation with tests
- setting @@global.debug= 'd,stop_slave_middle_group'.
- The sql thread receives the killed status and will proceed
- to shutdown trying to finish incomplete events group.
- */
- DBUG_EXECUTE_IF("stop_slave_middle_group",
- if (thd->transaction.all.modified_non_trans_table)
- const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
- }
-
- if (unlikely(error= do_after_row_operations(rli, error)) &&
- ignored_error_code(convert_handler_error(error, thd, table)))
- {
-
- if (global_system_variables.log_warnings)
- slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
- get_type_str(),
- RPL_LOG_NAME, log_pos);
- thd->clear_error(1);
- error= 0;
- }
- } // if (table)
-
-
- if (unlikely(error))
- {
- slave_rows_error_report(ERROR_LEVEL, error, rgi, thd, table,
- get_type_str(),
- RPL_LOG_NAME, log_pos);
- /*
- @todo We should probably not call
- reset_current_stmt_binlog_format_row() from here.
-
- Note: this applies to log_event_old.cc too.
- /Sven
- */
- thd->reset_current_stmt_binlog_format_row();
- thd->is_slave_error= 1;
- /* 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 (WSREP(thd) && wsrep_thd_is_applying(thd))
- {
- query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
- }
-#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
-
- if (unlikely(get_flags(STMT_END_F) &&
- (error= rows_event_stmt_cleanup(rgi, thd))))
- slave_rows_error_report(ERROR_LEVEL,
- thd->is_error() ? 0 : error,
- rgi, thd, table,
- get_type_str(),
- RPL_LOG_NAME, 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(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 (rgi->rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
- return Log_event::EVENT_SKIP_IGNORE;
- else
- return Log_event::do_shall_skip(rgi);
-}
-
-/**
- The function is called at Rows_log_event statement commit time,
- normally from Rows_log_event::do_update_pos() and possibly from
- Query_log_event::do_apply_event() of the COMMIT.
- The function commits the last statement for engines, binlog and
- releases resources have been allocated for the statement.
-
- @retval 0 Ok.
- @retval non-zero Error at the commit.
- */
-
-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
- unlock) the tables we opened when processing the
- Table_map_log_event starting the statement.
-
- OBSERVER. This will clear *all* mappings, not only those that
- are open for the table. There is not good handle for on-close
- actions for tables.
-
- NOTE. Even if we have no table ('table' == 0) we still need to be
- here, so that we increase the group relay log position. If we didn't, we
- could have a group relay log position which lags behind "forever"
- (assume the last master's transaction is ignored by the slave because of
- replicate-ignore rules).
- */
- error= thd->binlog_flush_pending_rows_event(TRUE);
-
- /*
- If this event is not in a transaction, the call below will, if some
- transactional storage engines are involved, commit the statement into
- them and flush the pending event to binlog.
- If this event is in a transaction, the call will do nothing, but a
- Xid_log_event will come next which will, if some transactional engines
- are involved, commit the transaction and flush the pending event to the
- binlog.
- If there was a deadlock the transaction should have been rolled back
- already. So there should be no need to rollback the transaction.
- */
- DBUG_ASSERT(! thd->transaction_rollback_request);
- error|= (int)(error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
-
- /*
- Now what if this is not a transactional engine? we still need to
- flush the pending event to the binlog; we did it with
- thd->binlog_flush_pending_rows_event(). Note that we imitate
- what is done for real queries: a call to
- ha_autocommit_or_rollback() (sometimes only if involves a
- transactional engine), and a call to be sure to have the pending
- event flushed.
- */
-
- /*
- @todo We should probably not call
- reset_current_stmt_binlog_format_row() from here.
-
- Note: this applies to log_event_old.cc too
-
- Btw, the previous comment about transactional engines does not
- seem related to anything that happens here.
- /Sven
- */
- thd->reset_current_stmt_binlog_format_row();
-
- /*
- 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;
- thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
- }
-
- rgi->cleanup_context(thd, 0);
- }
- DBUG_RETURN(error);
-}
-
-/**
- The method either increments the relay log position or
- commits the current statement and increments the master group
- position if the event is STMT_END_F flagged and
- the statement corresponds to the autocommit query (i.e replicated
- without wrapping in BEGIN/COMMIT)
-
- @retval 0 Success
- @retval non-zero Error in the statement commit
- */
-int
-Rows_log_event::do_update_pos(rpl_group_info *rgi)
-{
- Relay_log_info *rli= rgi->rli;
- int error= 0;
- DBUG_ENTER("Rows_log_event::do_update_pos");
-
- DBUG_PRINT("info", ("flags: %s",
- get_flags(STMT_END_F) ? "STMT_END_F " : ""));
-
- if (get_flags(STMT_END_F))
- {
- /*
- Indicate that a statement is finished.
- Step the group log position if we are not in a transaction,
- otherwise increase the event log position.
- */
- error= 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
- thd->net.last_err* are allowed. Examples of errors are "key not
- found", which is produced in the test case rpl_row_conflicts.test
- */
- thd->clear_error();
- }
- else
- {
- rgi->inc_event_relay_log_pos();
- }
-
- DBUG_RETURN(error);
-}
-
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
-
-#ifndef MYSQL_CLIENT
-bool Rows_log_event::write_data_header()
-{
- uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer
- DBUG_ASSERT(m_table_id != ~0ULL);
- DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
- {
- int4store(buf + 0, m_table_id);
- int2store(buf + 4, m_flags);
- return (write_data(buf, 6));
- });
- int6store(buf + RW_MAPID_OFFSET, m_table_id);
- int2store(buf + RW_FLAGS_OFFSET, m_flags);
- return write_data(buf, ROWS_HEADER_LEN);
-}
-
-bool Rows_log_event::write_data_body()
-{
- /*
- Note that this should be the number of *bits*, not the number of
- bytes.
- */
- 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);
- DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
-
- DBUG_DUMP("m_width", sbuf, (size_t) (sbuf_end - sbuf));
- res= res || write_data(sbuf, (size_t) (sbuf_end - sbuf));
-
- DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
- res= res || write_data((uchar*)m_cols.bitmap, no_bytes_in_map(&m_cols));
- /*
- TODO[refactor write]: Remove the "down cast" here (and elsewhere).
- */
- 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));
- res= res || write_data((uchar*)m_cols_ai.bitmap,
- no_bytes_in_map(&m_cols_ai));
- }
- DBUG_DUMP("rows", m_rows_buf, data_size);
- res= res || write_data(m_rows_buf, (size_t) data_size);
-
- return res;
-
-}
-
-bool Rows_log_event::write_compressed()
-{
- uchar *m_rows_buf_tmp = m_rows_buf;
- uchar *m_rows_cur_tmp = m_rows_cur;
- bool ret = true;
- uint32 comlen, alloc_size;
- comlen= alloc_size= binlog_get_compress_len((uint32)(m_rows_cur_tmp - m_rows_buf_tmp));
- m_rows_buf = (uchar *)my_safe_alloca(alloc_size);
- if(m_rows_buf &&
- !binlog_buf_compress((const char *)m_rows_buf_tmp, (char *)m_rows_buf,
- (uint32)(m_rows_cur_tmp - m_rows_buf_tmp), &comlen))
- {
- m_rows_cur= comlen + m_rows_buf;
- ret= Log_event::write();
- }
- my_safe_afree(m_rows_buf, alloc_size);
- m_rows_buf= m_rows_buf_tmp;
- m_rows_cur= m_rows_cur_tmp;
- return ret;
-}
-#endif
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Rows_log_event::pack_info(Protocol *protocol)
-{
- char buf[256];
- char const *const flagstr=
- get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
- size_t bytes= my_snprintf(buf, sizeof(buf),
- "table_id: %llu%s", m_table_id, flagstr);
- protocol->store(buf, bytes, &my_charset_bin);
-}
-#endif
-
-#ifdef MYSQL_CLIENT
-
-const char str_binlog[]= "\nBINLOG '\n";
-const char fmt_delim[]= "'%s\n";
-const char fmt_n_delim[]= "\n'%s";
-const char fmt_frag[]= "\nSET @binlog_fragment_%d ='\n";
-const char fmt_binlog2[]= "BINLOG @binlog_fragment_0, @binlog_fragment_1%s\n";
-
-/**
- Print an event "body" cache to @c file possibly in two fragments.
- Each fragement is optionally per @c do_wrap to produce an SQL statement.
-
- @param file a file to print to
- @param body the "body" IO_CACHE of event
- @param do_wrap whether to wrap base64-encoded strings with
- SQL cover.
- @param delimiter delimiter string
-
- @param is_verbose MDEV-10362 workraround parameter to pass
- info on presence of verbose printout in cache encoded data
-
- The function signals on any error through setting @c body->error to -1.
-*/
-bool copy_cache_to_file_wrapped(IO_CACHE *body,
- FILE *file,
- bool do_wrap,
- const char *delimiter,
- bool is_verbose /*TODO: remove */)
-{
- const my_off_t cache_size= my_b_tell(body);
-
- if (reinit_io_cache(body, READ_CACHE, 0L, FALSE, FALSE))
- goto err;
-
- if (!do_wrap)
- {
- my_b_copy_to_file(body, file, SIZE_T_MAX);
- }
- else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) >
- opt_binlog_rows_event_max_encoded_size)
- {
- /*
- 2 fragments can always represent near 1GB row-based
- base64-encoded event as two strings each of size less than
- max(max_allowed_packet). Greater number of fragments does not
- save from potential need to tweak (increase) @@max_allowed_packet
- before to process the fragments. So 2 is safe and enough.
-
- Split the big query when its packet size's estimation exceeds a
- limit. The estimate includes the maximum packet header
- contribution of non-compressed packet.
- */
- my_fprintf(file, fmt_frag, 0);
- if (my_b_copy_to_file(body, file, (size_t) cache_size/2 + 1))
- goto err;
- my_fprintf(file, fmt_n_delim, delimiter);
-
- my_fprintf(file, fmt_frag, 1);
- if (my_b_copy_to_file(body, file, SIZE_T_MAX))
- goto err;
- my_fprintf(file, fmt_delim, delimiter);
-
- my_fprintf(file, fmt_binlog2, delimiter);
- }
- else
- {
- my_fprintf(file, str_binlog);
- if (my_b_copy_to_file(body, file, SIZE_T_MAX))
- goto err;
- my_fprintf(file, fmt_delim, delimiter);
- }
- reinit_io_cache(body, WRITE_CACHE, 0, FALSE, TRUE);
-
- return false;
-
-err:
- body->error = -1;
- return true;
-}
-
-
-/**
- Print an event "body" cache to @c file possibly in two fragments.
- Each fragment is optionally per @c do_wrap to produce an SQL statement.
-
- @param file a file to print to
- @param body the "body" IO_CACHE of event
- @param do_wrap whether to wrap base64-encoded strings with
- SQL cover.
- @param delimiter delimiter string
-
- The function signals on any error through setting @c body->error to -1.
-*/
-bool copy_cache_to_string_wrapped(IO_CACHE *cache,
- LEX_STRING *to,
- bool do_wrap,
- const char *delimiter,
- bool is_verbose)
-{
- const my_off_t cache_size= my_b_tell(cache);
- // contribution to total size estimate of formating
- const size_t fmt_size=
- sizeof(str_binlog) + 2*(sizeof(fmt_frag) + 2 /* %d */) +
- sizeof(fmt_delim) + sizeof(fmt_n_delim) +
- sizeof(fmt_binlog2) +
- 3*PRINT_EVENT_INFO::max_delimiter_size;
-
- if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE))
- goto err;
-
- if (!(to->str= (char*) my_malloc((size_t)cache->end_of_file + fmt_size,
- MYF(0))))
- {
- perror("Out of memory: can't allocate memory in "
- "copy_cache_to_string_wrapped().");
- goto err;
- }
-
- if (!do_wrap)
- {
- if (my_b_read(cache, (uchar*) to->str,
- (to->length= (size_t)cache->end_of_file)))
- goto err;
- }
- else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) >
- opt_binlog_rows_event_max_encoded_size)
- {
- /*
- 2 fragments can always represent near 1GB row-based
- base64-encoded event as two strings each of size less than
- max(max_allowed_packet). Greater number of fragments does not
- save from potential need to tweak (increase) @@max_allowed_packet
- before to process the fragments. So 2 is safe and enough.
-
- Split the big query when its packet size's estimation exceeds a
- limit. The estimate includes the maximum packet header
- contribution of non-compressed packet.
- */
- char *str= to->str;
- size_t add_to_len;
-
- str += (to->length= sprintf(str, fmt_frag, 0));
- if (my_b_read(cache, (uchar*) str, (uint32) (cache_size/2 + 1)))
- goto err;
- str += (add_to_len = (uint32) (cache_size/2 + 1));
- to->length += add_to_len;
- str += (add_to_len= sprintf(str, fmt_n_delim, delimiter));
- to->length += add_to_len;
-
- str += (add_to_len= sprintf(str, fmt_frag, 1));
- to->length += add_to_len;
- if (my_b_read(cache, (uchar*) str, uint32(cache->end_of_file - (cache_size/2 + 1))))
- goto err;
- str += (add_to_len= uint32(cache->end_of_file - (cache_size/2 + 1)));
- to->length += add_to_len;
- {
- str += (add_to_len= sprintf(str , fmt_delim, delimiter));
- to->length += add_to_len;
- }
- to->length += sprintf(str, fmt_binlog2, delimiter);
- }
- else
- {
- char *str= to->str;
-
- str += (to->length= sprintf(str, str_binlog));
- if (my_b_read(cache, (uchar*) str, (size_t)cache->end_of_file))
- goto err;
- str += cache->end_of_file;
- to->length += (size_t)cache->end_of_file;
- to->length += sprintf(str , fmt_delim, delimiter);
- }
-
- reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
-
- return false;
-
-err:
- cache->error= -1;
- return true;
-}
-
-/**
- The function invokes base64 encoder to run on the current
- event string and store the result into two caches.
- When the event ends the current statement the caches are is copied into
- the argument file.
- Copying is also concerned how to wrap the event, specifically to produce
- a valid SQL syntax.
- When the encoded data size is within max(MAX_ALLOWED_PACKET)
- a regular BINLOG query is composed. Otherwise it is build as fragmented
-
- SET @binlog_fragment_0='...';
- SET @binlog_fragment_1='...';
- BINLOG @binlog_fragment_0, @binlog_fragment_1;
-
- where fragments are represented by a pair of indexed user
- "one shot" variables.
-
- @note
- If any changes made don't forget to duplicate them to
- Old_rows_log_event as long as it's supported.
-
- @param file pointer to IO_CACHE
- @param print_event_info pointer to print_event_info specializing
- what out of and how to print the event
- @param name the name of a table that the event operates on
-
- The function signals on any error of cache access through setting
- that cache's @c error to -1.
-*/
-bool Rows_log_event::print_helper(FILE *file,
- PRINT_EVENT_INFO *print_event_info,
- char const *const name)
-{
- IO_CACHE *const head= &print_event_info->head_cache;
- IO_CACHE *const body= &print_event_info->body_cache;
- IO_CACHE *const tail= &print_event_info->tail_cache;
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- IO_CACHE *const sql= &print_event_info->review_sql_cache;
-#endif
- bool do_print_encoded=
- print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
- print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS &&
- !print_event_info->short_form;
- bool const last_stmt_event= get_flags(STMT_END_F);
-
- if (!print_event_info->short_form)
- {
- char llbuff[22];
-
- print_header(head, print_event_info, !last_stmt_event);
- if (my_b_printf(head, "\t%s: table id %s%s\n",
- name, ullstr(m_table_id, llbuff),
- last_stmt_event ? " flags: STMT_END_F" : ""))
- goto err;
- }
- if (!print_event_info->short_form || print_event_info->print_row_count)
- if (print_base64(body, print_event_info, do_print_encoded))
- goto err;
-
- if (last_stmt_event)
- {
- if (!is_flashback)
- {
- if (copy_event_cache_to_file_and_reinit(head, file) ||
- copy_cache_to_file_wrapped(body, file, do_print_encoded,
- print_event_info->delimiter,
- print_event_info->verbose) ||
- copy_event_cache_to_file_and_reinit(tail, file))
- goto err;
- }
- else
- {
- LEX_STRING tmp_str;
-
- if (copy_event_cache_to_string_and_reinit(head, &tmp_str))
- return 1;
- output_buf.append(tmp_str.str, tmp_str.length); // Not \0 terminated);
- my_free(tmp_str.str);
-
- if (copy_cache_to_string_wrapped(body, &tmp_str, do_print_encoded,
- print_event_info->delimiter,
- print_event_info->verbose))
- return 1;
- output_buf.append(tmp_str.str, tmp_str.length);
- my_free(tmp_str.str);
- if (copy_event_cache_to_string_and_reinit(tail, &tmp_str))
- return 1;
- output_buf.append(tmp_str.str, tmp_str.length);
- my_free(tmp_str.str);
-
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- if (copy_event_cache_to_string_and_reinit(sql, &tmp_str))
- return 1;
- output_buf.append(tmp_str.str, tmp_str.length);
- my_free(tmp_str.str);
-#endif
- }
- }
-
- return 0;
-err:
- return 1;
-}
-#endif
-
/**************************************************************************
Annotate_rows_log_event member functions
**************************************************************************/
-#ifndef MYSQL_CLIENT
-Annotate_rows_log_event::Annotate_rows_log_event(THD *thd,
- bool using_trans,
- bool direct)
- : Log_event(thd, 0, using_trans),
- m_save_thd_query_txt(0),
- m_save_thd_query_len(0),
- m_saved_thd_query(false),
- m_used_query_txt(0)
-{
- m_query_txt= thd->query();
- m_query_len= thd->query_length();
- if (direct)
- cache_type= Log_event::EVENT_NO_CACHE;
-}
-#endif
-
Annotate_rows_log_event::Annotate_rows_log_event(const char *buf,
uint event_len,
const Format_description_log_event *desc)
@@ -12351,103 +3477,6 @@ bool Annotate_rows_log_event::is_valid() const
return (m_query_txt != NULL && m_query_len != 0);
}
-#ifndef MYSQL_CLIENT
-bool Annotate_rows_log_event::write_data_header()
-{
- return 0;
-}
-#endif
-
-#ifndef MYSQL_CLIENT
-bool Annotate_rows_log_event::write_data_body()
-{
- return write_data(m_query_txt, m_query_len);
-}
-#endif
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-void Annotate_rows_log_event::pack_info(Protocol* protocol)
-{
- if (m_query_txt && m_query_len)
- protocol->store(m_query_txt, m_query_len, &my_charset_bin);
-}
-#endif
-
-#ifdef MYSQL_CLIENT
-bool Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
-{
- char *pbeg; // beginning of the next line
- char *pend; // end of the next line
- uint cnt= 0; // characters counter
-
- if (!pinfo->short_form)
- {
- if (print_header(&pinfo->head_cache, pinfo, TRUE) ||
- my_b_printf(&pinfo->head_cache, "\tAnnotate_rows:\n"))
- goto err;
- }
- else if (my_b_printf(&pinfo->head_cache, "# Annotate_rows:\n"))
- goto err;
-
- for (pbeg= m_query_txt; ; pbeg= pend)
- {
- // skip all \r's and \n's at the beginning of the next line
- for (;; pbeg++)
- {
- if (++cnt > m_query_len)
- return 0;
-
- if (*pbeg != '\r' && *pbeg != '\n')
- break;
- }
-
- // find end of the next line
- for (pend= pbeg + 1;
- ++cnt <= m_query_len && *pend != '\r' && *pend != '\n';
- pend++)
- ;
-
- // print next line
- if (my_b_write(&pinfo->head_cache, (const uchar*) "#Q> ", 4) ||
- my_b_write(&pinfo->head_cache, (const uchar*) pbeg, pend - pbeg) ||
- my_b_write(&pinfo->head_cache, (const uchar*) "\n", 1))
- goto err;
- }
-
- return 0;
-err:
- return 1;
-}
-#endif
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi)
-{
- rgi->free_annotate_event();
- m_save_thd_query_txt= thd->query();
- m_save_thd_query_len= thd->query_length();
- m_saved_thd_query= true;
- m_used_query_txt= 1;
- thd->set_query(m_query_txt, m_query_len);
- return 0;
-}
-#endif
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Annotate_rows_log_event::do_update_pos(rpl_group_info *rgi)
-{
- 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(rpl_group_info *rgi)
-{
- return continue_group(rgi);
-}
-#endif
/**************************************************************************
Table_map_log_event member functions and support functions
@@ -12485,142 +3514,6 @@ Annotate_rows_log_event::do_shall_skip(rpl_group_info *rgi)
type used is uint32.
*/
-#if !defined(MYSQL_CLIENT)
-/**
- Save the field metadata based on the real_type of the field.
- The metadata saved depends on the type of the field. Some fields
- store a single byte for pack_length() while others store two bytes
- for field_length (max length).
-
- @retval 0 Ok.
-
- @todo
- We may want to consider changing the encoding of the information.
- Currently, the code attempts to minimize the number of bytes written to
- the tablemap. There are at least two other alternatives; 1) using
- net_store_length() to store the data allowing it to choose the number of
- bytes that are appropriate thereby making the code much easier to
- maintain (only 1 place to change the encoding), or 2) use a fixed number
- of bytes for each field. The problem with option 1 is that net_store_length()
- will use one byte if the value < 251, but 3 bytes if it is > 250. Thus,
- for fields like CHAR which can be no larger than 255 characters, the method
- will use 3 bytes when the value is > 250. Further, every value that is
- encoded using 2 parts (e.g., pack_length, field_length) will be numerically
- > 250 therefore will use 3 bytes for eah value. The problem with option 2
- is less wasteful for space but does waste 1 byte for every field that does
- not encode 2 parts.
-*/
-int Table_map_log_event::save_field_metadata()
-{
- DBUG_ENTER("Table_map_log_event::save_field_metadata");
- int index= 0;
- for (unsigned int i= 0 ; i < m_table->s->fields ; i++)
- {
- DBUG_PRINT("debug", ("field_type: %d", m_coltype[i]));
- index+= m_table->s->field[i]->save_field_metadata(&m_field_metadata[index]);
- }
- DBUG_RETURN(index);
-}
-#endif /* !defined(MYSQL_CLIENT) */
-
-/*
- Constructor used to build an event for writing to the binary log.
- Mats says tbl->s lives longer than this event so it's ok to copy pointers
- (tbl->s->db etc) and not pointer content.
- */
-#if !defined(MYSQL_CLIENT)
-Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
- bool is_transactional)
- : Log_event(thd, 0, is_transactional),
- m_table(tbl),
- m_dbnam(tbl->s->db.str),
- m_dblen(m_dbnam ? tbl->s->db.length : 0),
- m_tblnam(tbl->s->table_name.str),
- m_tbllen(tbl->s->table_name.length),
- m_colcnt(tbl->s->fields),
- m_memory(NULL),
- m_table_id(tid),
- m_flags(TM_BIT_LEN_EXACT_F),
- m_data_size(0),
- m_field_metadata(0),
- m_field_metadata_size(0),
- m_null_bits(0),
- m_meta_memory(NULL)
-{
- uchar cbuf[MAX_INT_WIDTH];
- uchar *cbuf_end;
- DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
- DBUG_ASSERT(m_table_id != ~0ULL);
- /*
- In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
- table.cc / alloc_table_share():
- Use the fact the key is db/0/table_name/0
- As we rely on this let's assert it.
- */
- DBUG_ASSERT((tbl->s->db.str == 0) ||
- (tbl->s->db.str[tbl->s->db.length] == 0));
- DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);
-
-
- m_data_size= TABLE_MAP_HEADER_LEN;
- DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;);
- m_data_size+= m_dblen + 2; // Include length and terminating \0
- m_data_size+= m_tbllen + 2; // Include length and terminating \0
- cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
- DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
- m_data_size+= (cbuf_end - cbuf) + m_colcnt; // COLCNT and column types
-
- if (tbl->triggers)
- m_flags|= TM_BIT_HAS_TRIGGERS_F;
-
- /* 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]->binlog_type();
- }
-
- /*
- Calculate a bitmap for the results of maybe_null() for all columns.
- The bitmap is used to determine when there is a column from the master
- that is not on the slave and is null and thus not in the row data during
- replication.
- */
- uint num_null_bytes= (m_table->s->fields + 7) / 8;
- m_data_size+= num_null_bytes;
- m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
- &m_null_bits, num_null_bytes,
- &m_field_metadata, (m_colcnt * 2),
- NULL);
-
- bzero(m_field_metadata, (m_colcnt * 2));
-
- /*
- Create an array for the field metadata and store it.
- */
- m_field_metadata_size= save_field_metadata();
- DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
-
- /*
- Now set the size of the data to the size of the field metadata array
- plus one or three bytes (see pack.c:net_store_length) for number of
- elements in the field metadata array.
- */
- if (m_field_metadata_size < 251)
- m_data_size+= m_field_metadata_size + 1;
- else
- m_data_size+= m_field_metadata_size + 3;
-
- bzero(m_null_bits, num_null_bytes);
- for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
- if (m_table->field[i]->maybe_null())
- m_null_bits[(i / 8)]+= 1 << (i % 8);
-
- DBUG_VOID_RETURN;
-}
-#endif /* !defined(MYSQL_CLIENT) */
-
/*
Constructor used by slave to read the event from the binary log.
*/
@@ -12637,14 +3530,17 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
m_colcnt(0), m_coltype(0),
m_memory(NULL), m_table_id(ULONGLONG_MAX), m_flags(0),
m_data_size(0), m_field_metadata(0), m_field_metadata_size(0),
- m_null_bits(0), m_meta_memory(NULL)
+ m_null_bits(0), m_meta_memory(NULL),
+ m_optional_metadata_len(0), m_optional_metadata(NULL)
{
+ unsigned int bytes_read= 0;
DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");
uint8 common_header_len= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[TABLE_MAP_EVENT-1];
DBUG_PRINT("info",("event_len: %u common_header_len: %d post_header_len: %d",
event_len, common_header_len, post_header_len));
+
/*
Don't print debug messages when running valgrind since they can
trigger false warnings.
@@ -12653,8 +3549,8 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
DBUG_DUMP("event buffer", (uchar*) buf, event_len);
#endif
- if (event_len < (uint)(common_header_len + post_header_len))
- DBUG_VOID_RETURN;
+ if (event_len < (uint)(common_header_len + post_header_len))
+ DBUG_VOID_RETURN;
/* Read the post-header */
const char *post_start= buf + common_header_len;
@@ -12703,7 +3599,7 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
m_colcnt, (long) (ptr_colcnt-(const uchar*)vpart)));
/* Allocate mem for all fields in one go. If fails, caught in is_valid() */
- m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ m_memory= (uchar*) my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
&m_dbnam, (uint) m_dblen + 1,
&m_tblnam, (uint) m_tbllen + 1,
&m_coltype, (uint) m_colcnt,
@@ -12719,16 +3615,17 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
ptr_after_colcnt= ptr_after_colcnt + m_colcnt;
VALIDATE_BYTES_READ(ptr_after_colcnt, buf, event_len);
m_field_metadata_size= net_field_length(&ptr_after_colcnt);
- if(m_field_metadata_size <= (m_colcnt * 2))
+ if (m_field_metadata_size <= (m_colcnt * 2))
{
uint num_null_bytes= (m_colcnt + 7) / 8;
- m_meta_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
+ m_meta_memory= (uchar *)my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
&m_null_bits, num_null_bytes,
&m_field_metadata, m_field_metadata_size,
NULL);
memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
+ ptr_after_colcnt= (unsigned char*)ptr_after_colcnt + num_null_bytes;
}
else
{
@@ -12737,7 +3634,27 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
m_memory= NULL;
DBUG_VOID_RETURN;
}
+
+ bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf);
+
+ /* After null_bits field, there are some new fields for extra metadata. */
+ if (bytes_read < event_len)
+ {
+ m_optional_metadata_len= event_len - bytes_read;
+ m_optional_metadata=
+ static_cast<unsigned char*>(my_malloc(PSI_INSTRUMENT_ME, m_optional_metadata_len, MYF(MY_WME)));
+ memcpy(m_optional_metadata, ptr_after_colcnt, m_optional_metadata_len);
+ }
}
+#ifdef MYSQL_SERVER
+ if (!m_table)
+ DBUG_VOID_RETURN;
+ binlog_type_info_array= (Binlog_type_info *)thd->alloc(m_table->s->fields *
+ sizeof(Binlog_type_info));
+ for (uint i= 0; i < m_table->s->fields; i++)
+ binlog_type_info_array[i]= m_table->field[i]->binlog_type_info();
+#endif
+
DBUG_VOID_RETURN;
}
#endif
@@ -12746,1634 +3663,270 @@ Table_map_log_event::~Table_map_log_event()
{
my_free(m_meta_memory);
my_free(m_memory);
+ my_free(m_optional_metadata);
+ m_optional_metadata= NULL;
}
+/**
+ Parses SIGNEDNESS field.
-#ifdef MYSQL_CLIENT
-
-/*
- Rewrite database name for the event to name specified by new_db
- SYNOPSIS
- new_db Database name to change to
- new_len Length
- desc Event describing binlog that we're writing to.
-
- DESCRIPTION
- Reset db name. This function assumes that temp_buf member contains event
- representation taken from a binary log. It resets m_dbnam and m_dblen and
- rewrites temp_buf with new db name.
-
- RETURN
- 0 - Success
- other - Error
-*/
-
-int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
- const Format_description_log_event* desc)
-{
- DBUG_ENTER("Table_map_log_event::rewrite_db");
- DBUG_ASSERT(temp_buf);
-
- uint header_len= MY_MIN(desc->common_header_len,
- LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN;
- int len_diff;
-
- if (!(len_diff= (int)(new_len - m_dblen)))
- {
- memcpy((void*) (temp_buf + header_len + 1), new_db, m_dblen + 1);
- memcpy((void*) m_dbnam, new_db, m_dblen + 1);
- DBUG_RETURN(0);
- }
-
- // Create new temp_buf
- ulong event_cur_len= uint4korr(temp_buf + EVENT_LEN_OFFSET);
- ulong event_new_len= event_cur_len + len_diff;
- char* new_temp_buf= (char*) my_malloc(event_new_len, MYF(MY_WME));
-
- if (!new_temp_buf)
- {
- sql_print_error("Table_map_log_event::rewrite_db: "
- "failed to allocate new temp_buf (%d bytes required)",
- event_new_len);
- DBUG_RETURN(-1);
- }
-
- // Rewrite temp_buf
- char* ptr= new_temp_buf;
- size_t cnt= 0;
-
- // Copy header and change event length
- memcpy(ptr, temp_buf, header_len);
- int4store(ptr + EVENT_LEN_OFFSET, event_new_len);
- ptr += header_len;
- cnt += header_len;
-
- // Write new db name length and new name
- DBUG_ASSERT(new_len < 0xff);
- *ptr++ = (char)new_len;
- memcpy(ptr, new_db, new_len + 1);
- ptr += new_len + 1;
- cnt += m_dblen + 2;
-
- // Copy rest part
- memcpy(ptr, temp_buf + cnt, event_cur_len - cnt);
-
- // Reregister temp buf
- free_temp_buf();
- register_temp_buf(new_temp_buf, TRUE);
-
- // Reset m_dbnam and m_dblen members
- m_dblen= new_len;
-
- // m_dbnam resides in m_memory together with m_tblnam and m_coltype
- uchar* memory= m_memory;
- char const* tblnam= m_tblnam;
- uchar* coltype= m_coltype;
-
- m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
- &m_dbnam, (uint) m_dblen + 1,
- &m_tblnam, (uint) m_tbllen + 1,
- &m_coltype, (uint) m_colcnt,
- NullS);
-
- if (!m_memory)
- {
- sql_print_error("Table_map_log_event::rewrite_db: "
- "failed to allocate new m_memory (%d + %d + %d bytes required)",
- m_dblen + 1, m_tbllen + 1, m_colcnt);
- DBUG_RETURN(-1);
- }
-
- memcpy((void*)m_dbnam, new_db, m_dblen + 1);
- memcpy((void*)m_tblnam, tblnam, m_tbllen + 1);
- memcpy(m_coltype, coltype, m_colcnt);
-
- my_free(memory);
- DBUG_RETURN(0);
-}
-#endif /* MYSQL_CLIENT */
-
-
-/*
- Return value is an error code, one of:
-
- -1 Failure to open table [from open_tables()]
- 0 Success
- 1 No room for more tables [from set_table()]
- 2 Out of memory [from set_table()]
- 3 Wrong table definition
- 4 Daisy-chaining RBR with SBR not possible
+ @param[out] vec stores the signedness flags extracted from field.
+ @param[in] field SIGNEDNESS field in table_map_event.
+ @param[in] length length of the field
*/
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-
-enum enum_tbl_map_status
+static void parse_signedness(std::vector<bool> &vec,
+ unsigned char *field, unsigned int length)
{
- /* no duplicate identifier found */
- OK_TO_PROCESS= 0,
-
- /* this table map must be filtered out */
- FILTERED_OUT= 1,
-
- /* identifier mapping table with different properties */
- SAME_ID_MAPPING_DIFFERENT_TABLE= 2,
-
- /* a duplicate identifier was found mapping the same table */
- SAME_ID_MAPPING_SAME_TABLE= 3
-};
-
-/*
- Checks if this table map event should be processed or not. First
- it checks the filtering rules, and then looks for duplicate identifiers
- in the existing list of rli->tables_to_lock.
-
- It checks that there hasn't been any corruption by verifying that there
- are no duplicate entries with different properties.
-
- In some cases, some binary logs could get corrupted, showing several
- tables mapped to the same table_id, 0 (see: BUG#56226). Thus we do this
- early sanity check for such cases and avoid that the server crashes
- later.
-
- In some corner cases, the master logs duplicate table map events, i.e.,
- same id, same database name, same table name (see: BUG#37137). This is
- different from the above as it's the same table that is mapped again
- to the same identifier. Thus we cannot just check for same ids and
- assume that the event is corrupted we need to check every property.
-
- NOTE: in the event that BUG#37137 ever gets fixed, this extra check
- will still be valid because we would need to support old binary
- logs anyway.
-
- @param rli The relay log info reference.
- @param table_list A list element containing the table to check against.
- @return OK_TO_PROCESS
- if there was no identifier already in rli->tables_to_lock
-
- FILTERED_OUT
- if the event is filtered according to the filtering rules
-
- SAME_ID_MAPPING_DIFFERENT_TABLE
- if the same identifier already maps a different table in
- rli->tables_to_lock
-
- SAME_ID_MAPPING_SAME_TABLE
- if the same identifier already maps the same table in
- rli->tables_to_lock.
-*/
-static enum_tbl_map_status
-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;
- if ((rgi->thd->slave_thread /* filtering is for slave only */ ||
- IF_WSREP((WSREP(rgi->thd) && rgi->thd->wsrep_applier), 0)) &&
- (!rli->mi->rpl_filter->db_ok(table_list->db.str) ||
- (rli->mi->rpl_filter->is_on() && !rli->mi->rpl_filter->tables_ok("", table_list))))
- res= FILTERED_OUT;
- else
+ for (unsigned int i= 0; i < length; i++)
{
- 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)
- {
-
- if (cmp(&ptr->db, &table_list->db) ||
- cmp(&ptr->alias, &table_list->table_name) ||
- ptr->lock_type != TL_WRITE) // the ::do_apply_event always sets TL_WRITE
- res= SAME_ID_MAPPING_DIFFERENT_TABLE;
- else
- res= SAME_ID_MAPPING_SAME_TABLE;
-
- break;
- }
- }
+ for (unsigned char c= 0x80; c != 0; c>>= 1)
+ vec.push_back(field[i] & c);
}
-
- DBUG_PRINT("debug", ("check of table map ended up with: %u", res));
-
- DBUG_RETURN(res);
}
-int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
-{
- RPL_TABLE_LIST *table_list;
- char *db_mem, *tname_mem, *ptr;
- size_t dummy_len, db_mem_length, tname_mem_length;
- void *memory;
- Rpl_filter *filter;
- Relay_log_info const *rli= rgi->rli;
- DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
-
- /* Step the query id to mark what columns that are actually used. */
- thd->set_query_id(next_query_id());
-
- if (!(memory= my_multi_malloc(MYF(MY_WME),
- &table_list, (uint) sizeof(RPL_TABLE_LIST),
- &db_mem, (uint) NAME_LEN + 1,
- &tname_mem, (uint) NAME_LEN + 1,
- NullS)))
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-
- db_mem_length= strmov(db_mem, m_dbnam) - db_mem;
- tname_mem_length= strmov(tname_mem, m_tblnam) - tname_mem;
- if (lower_case_table_names)
- {
- my_casedn_str(files_charset_info, (char*)tname_mem);
- my_casedn_str(files_charset_info, (char*)db_mem);
- }
-
- /* call from mysql_client_binlog_statement() will not set rli->mi */
- filter= rgi->thd->slave_thread ? rli->mi->rpl_filter : global_rpl_filter;
-
- /* rewrite rules changed the database */
- if (((ptr= (char*) filter->get_rewrite_db(db_mem, &dummy_len)) != db_mem))
- db_mem_length= strmov(db_mem, ptr) - db_mem;
-
- LEX_CSTRING tmp_db_name= {db_mem, db_mem_length };
- LEX_CSTRING tmp_tbl_name= {tname_mem, tname_mem_length };
-
- table_list->init_one_table(&tmp_db_name, &tmp_tbl_name, 0, TL_WRITE);
- 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= TABLE_TYPE_NORMAL;
-
- DBUG_PRINT("debug", ("table: %s is mapped to %llu",
- table_list->table_name.str,
- table_list->table_id));
- 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));
-
- 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);
-
- /*
- Use placement new to construct the table_def instance in the
- memory allocated for it inside table_list.
-
- The memory allocated by the table_def structure (i.e., not the
- memory allocated *for* the table_def structure) is released
- inside Relay_log_info::clear_tables_to_lock() by calling the
- table_def destructor explicitly.
- */
- new (&table_list->m_tabledef)
- table_def(m_coltype, m_colcnt,
- m_field_metadata, m_field_metadata_size,
- m_null_bits, m_flags);
- table_list->m_tabledef_valid= TRUE;
- table_list->m_conv_table= NULL;
- table_list->open_type= OT_BASE_ONLY;
-
- /*
- 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= 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_*
- {
- /*
- If mapped already but with different properties, we raise an
- error.
- If mapped already but with same properties we skip the event.
- If filtered out we skip the event.
-
- In all three cases, we need to free the memory previously
- allocated.
- */
- if (tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE)
- {
- /*
- Something bad has happened. We need to stop the slave as strange things
- could happen if we proceed: slave crash, wrong table being updated, ...
- As a consequence we push an error in this case.
- */
-
- char buf[256];
-
- my_snprintf(buf, sizeof(buf),
- "Found table map event mapping table id %u which "
- "was already mapped but with different settings.",
- table_list->table_id);
-
- if (thd->slave_thread)
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
- ER_THD(thd, ER_SLAVE_FATAL_ERROR), buf);
- else
- /*
- For the cases in which a 'BINLOG' statement is set to
- execute in a user session
- */
- my_error(ER_SLAVE_FATAL_ERROR, MYF(0), buf);
- }
-
- my_free(memory);
- }
-
- DBUG_RETURN(tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE);
-}
-
-Log_event::enum_skip_reason
-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(rgi);
-}
-
-int Table_map_log_event::do_update_pos(rpl_group_info *rgi)
-{
- rgi->inc_event_relay_log_pos();
- return 0;
-}
-
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
-
-#ifndef MYSQL_CLIENT
-bool Table_map_log_event::write_data_header()
-{
- DBUG_ASSERT(m_table_id != ~0ULL);
- uchar buf[TABLE_MAP_HEADER_LEN];
- DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
- {
- int4store(buf + 0, m_table_id);
- int2store(buf + 4, m_flags);
- return (write_data(buf, 6));
- });
- int6store(buf + TM_MAPID_OFFSET, m_table_id);
- int2store(buf + TM_FLAGS_OFFSET, m_flags);
- return write_data(buf, TABLE_MAP_HEADER_LEN);
-}
-
-bool Table_map_log_event::write_data_body()
-{
- 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 <= 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[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[MAX_INT_WIDTH];
- uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
-
- return write_data(dbuf, sizeof(dbuf)) ||
- write_data(m_dbnam, m_dblen+1) ||
- write_data(tbuf, sizeof(tbuf)) ||
- write_data(m_tblnam, m_tbllen+1) ||
- write_data(cbuf, (size_t) (cbuf_end - cbuf)) ||
- write_data(m_coltype, m_colcnt) ||
- write_data(mbuf, (size_t) (mbuf_end - mbuf)) ||
- write_data(m_field_metadata, m_field_metadata_size),
- write_data(m_null_bits, (m_colcnt + 7) / 8);
- }
-#endif
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+/**
+ Parses DEFAULT_CHARSET field.
-/*
- Print some useful information for the SHOW BINARY LOG information
- field.
+ @param[out] default_charset stores collation numbers extracted from field.
+ @param[in] field DEFAULT_CHARSET field in table_map_event.
+ @param[in] length length of the field
*/
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Table_map_log_event::pack_info(Protocol *protocol)
+static void parse_default_charset(Table_map_log_event::Optional_metadata_fields::
+ Default_charset &default_charset,
+ unsigned char *field, unsigned int length)
{
- char buf[256];
- size_t bytes= my_snprintf(buf, sizeof(buf),
- "table_id: %llu (%s.%s)",
- m_table_id, m_dbnam, m_tblnam);
- protocol->store(buf, bytes, &my_charset_bin);
-}
-#endif
+ unsigned char* p= field;
-
-#endif
-
-
-#ifdef MYSQL_CLIENT
-bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
-{
- if (!print_event_info->short_form)
+ default_charset.default_charset= net_field_length(&p);
+ while (p < field + length)
{
- char llbuff[22];
-
- print_header(&print_event_info->head_cache, print_event_info, TRUE);
- if (my_b_printf(&print_event_info->head_cache,
- "\tTable_map: %`s.%`s mapped to number %s%s\n",
- m_dbnam, m_tblnam, ullstr(m_table_id, llbuff),
- ((m_flags & TM_BIT_HAS_TRIGGERS_F) ?
- " (has triggers)" : "")))
- goto err;
- }
- if (!print_event_info->short_form || print_event_info->print_row_count)
- {
- bool do_print_encoded=
- print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
- print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS &&
- !print_event_info->short_form;
-
- if (print_base64(&print_event_info->body_cache, print_event_info,
- do_print_encoded) ||
- copy_event_cache_to_file_and_reinit(&print_event_info->head_cache,
- file))
- goto err;
- }
+ unsigned int col_index= net_field_length(&p);
+ unsigned int col_charset= net_field_length(&p);
- return 0;
-err:
- return 1;
+ default_charset.charset_pairs.push_back(std::make_pair(col_index,
+ col_charset));
+ }
}
-#endif
-/**************************************************************************
- Write_rows_log_event member functions
-**************************************************************************/
+/**
+ Parses COLUMN_CHARSET field.
-/*
- Constructor used to build an event for writing to the binary log.
+ @param[out] vec stores collation numbers extracted from field.
+ @param[in] field COLUMN_CHARSET field in table_map_event.
+ @param[in] length length of the field
*/
-#if !defined(MYSQL_CLIENT)
-Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid_arg,
- bool is_transactional)
- :Rows_log_event(thd_arg, tbl_arg, tid_arg, tbl_arg->rpl_write_set,
- is_transactional, WRITE_ROWS_EVENT_V1)
+static void parse_column_charset(std::vector<unsigned int> &vec,
+ unsigned char *field, unsigned int length)
{
-}
+ unsigned char* p= field;
-Write_rows_compressed_log_event::Write_rows_compressed_log_event(
- THD *thd_arg,
- TABLE *tbl_arg,
- ulong tid_arg,
- bool is_transactional)
- : Write_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional)
-{
- m_type = WRITE_ROWS_COMPRESSED_EVENT_V1;
+ while (p < field + length)
+ vec.push_back(net_field_length(&p));
}
-bool Write_rows_compressed_log_event::write()
-{
- return Rows_log_event::write_compressed();
-}
-#endif
+/**
+ Parses COLUMN_NAME field.
-/*
- Constructor used by slave to read the event from the binary log.
+ @param[out] vec stores column names extracted from field.
+ @param[in] field COLUMN_NAME field in table_map_event.
+ @param[in] length length of the field
*/
-#ifdef HAVE_REPLICATION
-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, description_event)
-{
-}
-
-Write_rows_compressed_log_event::Write_rows_compressed_log_event(
- const char *buf, uint event_len,
- const Format_description_log_event
- *description_event)
-: Write_rows_log_event(buf, event_len, description_event)
-{
- uncompress_buf();
-}
-#endif
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int
-Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
-{
- int error= 0;
-
- /*
- Increment the global status insert count variable
- */
- if (get_flags(STMT_END_F))
- status_var_increment(thd->status_var.com_stat[SQLCOM_INSERT]);
-
- /**
- todo: to introduce a property for the event (handler?) which forces
- applying the event in the replace (idempotent) fashion.
- */
- if (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT)
- {
- /*
- We are using REPLACE semantics and not INSERT IGNORE semantics
- when writing rows, that is: new rows replace old rows. We need to
- inform the storage engine that it should use this behaviour.
- */
-
- /* Tell the storage engine that we are using REPLACE semantics. */
- thd->lex->duplicates= DUP_REPLACE;
-
- /*
- Pretend we're executing a REPLACE command: this is needed for
- InnoDB since it is not (properly) checking the lex->duplicates flag.
- */
- thd->lex->sql_command= SQLCOM_REPLACE;
- /*
- Do not raise the error flag in case of hitting to an unique attribute
- */
- m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- /*
- The following is needed in case if we have AFTER DELETE triggers.
- */
- m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
- m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
- }
- if (slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers )
- m_table->prepare_triggers_for_insert_stmt_or_event();
-
- /* Honor next number column if present */
- m_table->next_number_field= m_table->found_next_number_field;
- /*
- * Fixed Bug#45999, In RBR, Store engine of Slave auto-generates new
- * sequence numbers for auto_increment fields if the values of them are 0.
- * If generateing a sequence number is decided by the values of
- * table->auto_increment_field_not_null and SQL_MODE(if includes
- * MODE_NO_AUTO_VALUE_ON_ZERO) in update_auto_increment function.
- * SQL_MODE of slave sql thread is always consistency with master's.
- * In RBR, auto_increment fields never are NULL, except if the auto_inc
- * column exists only on the slave side (i.e., in an extra column
- * on the slave's table).
- */
- if (!is_auto_inc_in_extra_columns())
- m_table->auto_increment_field_not_null= TRUE;
- else
- {
- /*
- Here we have checked that there is an extra field
- on this server's table that has an auto_inc column.
-
- Mark that the auto_increment field is null and mark
- the read and write set bits.
-
- (There can only be one AUTO_INC column, it is always
- indexed and it cannot have a DEFAULT value).
- */
- m_table->auto_increment_field_not_null= FALSE;
- m_table->mark_auto_increment_column();
- }
-
- return error;
-}
-
-int
-Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
- int error)
+static void parse_column_name(std::vector<std::string> &vec,
+ unsigned char *field, unsigned int length)
{
- int local_error= 0;
-
- /**
- Clear the write_set bit for auto_inc field that only
- existed on the destination table as an extra column.
- */
- if (is_auto_inc_in_extra_columns())
- {
- bitmap_clear_bit(m_table->rpl_write_set,
- m_table->next_number_field->field_index);
- bitmap_clear_bit(m_table->read_set,
- m_table->next_number_field->field_index);
+ unsigned char* p= field;
- if (get_flags(STMT_END_F))
- m_table->file->ha_release_auto_increment();
- }
- m_table->next_number_field=0;
- m_table->auto_increment_field_not_null= FALSE;
- if (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT)
- {
- m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- /*
- resetting the extra with
- table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
- fires bug#27077
- explanation: file->reset() performs this duty
- ultimately. Still todo: fix
- */
- }
- if (unlikely((local_error= m_table->file->ha_end_bulk_insert())))
+ while (p < field + length)
{
- m_table->file->print_error(local_error, MYF(0));
+ unsigned len= net_field_length(&p);
+ vec.push_back(std::string(reinterpret_cast<char *>(p), len));
+ p+= len;
}
- return error? error : local_error;
-}
-
-#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)
-{
- bool result;
- DBUG_ENTER("Rows_log_event::process_triggers");
- m_table->triggers->mark_fields_used(event);
- 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);
-}
-/*
- Check if there are more UNIQUE keys after the given key.
-*/
-static int
-last_uniq_key(TABLE *table, uint keyno)
-{
- while (++keyno < table->s->keys)
- if (table->key_info[keyno].flags & HA_NOSAME)
- return 0;
- return 1;
}
/**
- Check if an error is a duplicate key error.
+ Parses SET_STR_VALUE/ENUM_STR_VALUE field.
- This function is used to check if an error code is one of the
- duplicate key error, i.e., and error code for which it is sensible
- to do a <code>get_dup_key()</code> to retrieve the duplicate key.
-
- @param errcode The error code to check.
-
- @return <code>true</code> if the error code is such that
- <code>get_dup_key()</code> will return true, <code>false</code>
- otherwise.
+ @param[out] vec stores SET/ENUM column's string values extracted from
+ field. Each SET/ENUM column's string values are stored
+ into a string separate vector. All of them are stored
+ in 'vec'.
+ @param[in] field COLUMN_NAME field in table_map_event.
+ @param[in] length length of the field
*/
-bool
-is_duplicate_key_error(int errcode)
-{
- switch (errcode)
- {
- case HA_ERR_FOUND_DUPP_KEY:
- case HA_ERR_FOUND_DUPP_UNIQUE:
- return true;
- }
- return false;
-}
-
-/**
- Write the current row into event's table.
-
- The row is located in the row buffer, pointed by @c m_curr_row member.
- Number of columns of the row is stored in @c m_width member (it can be
- different from the number of columns in the table to which we insert).
- Bitmap @c m_cols indicates which columns are present in the row. It is assumed
- that event's table is already open and pointed by @c m_table.
-
- If the same record already exists in the table it can be either overwritten
- or an error is reported depending on the value of @c overwrite flag
- (error reporting not yet implemented). Note that the matching record can be
- different from the row we insert if we use primary keys to identify records in
- the table.
-
- The row to be inserted can contain values only for selected columns. The
- missing columns are filled with default values using @c prepare_record()
- function. If a matching record is found in the table and @c overwritte is
- true, the missing columns are taken from it.
-
- @param rli Relay log info (needed for row unpacking).
- @param overwrite
- Shall we overwrite if the row already exists or signal
- error (currently ignored).
-
- @returns Error code on failure, 0 on success.
-
- This method, if successful, sets @c m_curr_row_end pointer to point at the
- next row in the rows buffer. This is done when unpacking the row to be
- inserted.
-
- @note If a matching record is found, it is either updated using
- @c ha_update_row() or first deleted and then new record written.
-*/
-
-int
-Rows_log_event::write_row(rpl_group_info *rgi,
- const bool overwrite)
+static void parse_set_str_value(std::vector<Table_map_log_event::
+ Optional_metadata_fields::str_vector> &vec,
+ unsigned char *field, unsigned int length)
{
- DBUG_ENTER("write_row");
- DBUG_ASSERT(m_table != NULL && thd != NULL);
+ unsigned char* p= field;
- 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, true);
-
- /* unpack row into table->record[0] */
- if (unlikely((error= unpack_current_row(rgi))))
- {
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
- }
-
- if (m_curr_row == m_rows_buf && !invoke_triggers)
+ while (p < field + length)
{
- /*
- 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.
- */
- /* 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 */
- DBUG_ASSERT(!(m_curr_row > m_curr_row_end));
- ha_rows estimated_rows= 0;
- if (m_curr_row < m_curr_row_end)
- estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
- else if (m_curr_row == m_curr_row_end)
- estimated_rows= 1;
-
- table->file->ha_start_bulk_insert(estimated_rows);
- }
-
- /*
- Explicitly set the auto_inc to null to make sure that
- it gets an auto_generated value.
- */
- if (is_auto_inc_in_extra_columns())
- m_table->next_number_field->set_null();
-
- DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
- DBUG_PRINT_BITSET("debug", "rpl_write_set: %s", table->rpl_write_set);
- DBUG_PRINT_BITSET("debug", "read_set: %s", table->read_set);
-
- if (invoke_triggers &&
- unlikely(process_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE, TRUE)))
- {
- DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
- }
-
- // Handle INSERT.
- if (table->versioned(VERS_TIMESTAMP))
- {
- ulong sec_part;
- bitmap_set_bit(table->read_set, table->vers_start_field()->field_index);
- table->file->column_bitmaps_signal();
- // Check whether a row came from unversioned table and fix vers fields.
- if (table->vers_start_field()->get_timestamp(&sec_part) == 0 && sec_part == 0)
- table->vers_update_fields();
- }
-
- /*
- 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
- it and repeat the whole process again.
-
- TODO: Add safety measures against infinite looping.
- */
-
- if (table->s->sequence)
- error= update_sequence();
- else while (unlikely(error= table->file->ha_write_row(table->record[0])))
- {
- if (error == HA_ERR_LOCK_DEADLOCK ||
- error == HA_ERR_LOCK_WAIT_TIMEOUT ||
- (keynum= table->file->get_dup_key(error)) < 0 ||
- !overwrite)
- {
- DBUG_PRINT("info",("get_dup_key returns %d)", keynum));
- /*
- Deadlock, waiting for lock or just an error from the handler
- such as HA_ERR_FOUND_DUPP_KEY when overwrite is false.
- Retrieval of the duplicate key number may fail
- - either because the error was not "duplicate key" error
- - or because the information which key is not available
- */
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
- }
- /*
- We need to retrieve the old row into record[1] to be able to
- either update or delete the offending record. We either:
-
- - use rnd_pos() with a row-id (available as dupp_row) to the
- offending row, if that is possible (MyISAM and Blackhole), or else
-
- - use index_read_idx() with the key that is duplicated, to
- retrieve the offending row.
- */
- if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
- {
- DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
-
- if ((error= table->file->ha_rnd_init_with_error(0)))
- {
- DBUG_RETURN(error);
- }
-
- error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
- if (unlikely(error))
- {
- DBUG_PRINT("info",("rnd_pos() returns error %d",error));
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
- }
- table->file->ha_rnd_end();
- }
- else
- {
- DBUG_PRINT("info",("Locating offending record using index_read_idx()"));
-
- if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
- {
- DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
- DBUG_RETURN(my_errno);
- }
-
- if (key.get() == NULL)
- {
- key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
- if (key.get() == NULL)
- {
- DBUG_PRINT("info",("Can't allocate key buffer"));
- DBUG_RETURN(ENOMEM);
- }
- }
-
- key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
- 0);
- error= table->file->ha_index_read_idx_map(table->record[1], keynum,
- (const uchar*)key.get(),
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT);
- if (unlikely(error))
- {
- DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error)));
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
- }
- }
-
- /*
- Now, record[1] should contain the offending row. That
- will enable us to update it or, alternatively, delete it (so
- that we can insert the new row afterwards).
- */
-
- /*
- If row is incomplete we will use the record found to fill
- missing columns.
- */
- if (!get_flags(COMPLETE_ROWS_F))
- {
- restore_record(table,record[1]);
- error= unpack_current_row(rgi);
- }
-
- DBUG_PRINT("debug",("preparing for update: before and after image"));
- DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
- DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
-
- /*
- REPLACE is defined as either INSERT or DELETE + INSERT. If
- possible, we can replace it with an UPDATE, but that will not
- work on InnoDB if FOREIGN KEY checks are necessary.
-
- I (Matz) am not sure of the reason for the last_uniq_key()
- check as, but I'm guessing that it's something along the
- following lines.
-
- Suppose that we got the duplicate key to be a key that is not
- the last unique key for the table and we perform an update:
- then there might be another key for which the unique check will
- fail, so we're better off just deleting the row and inserting
- the correct row.
-
- Additionally we don't use UPDATE if rbr triggers should be invoked -
- when triggers are used we want a simple and predictable execution path.
- */
- if (last_uniq_key(table, keynum) && !invoke_triggers &&
- !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;
+ unsigned int count= net_field_length(&p);
- case 0:
- break;
-
- default:
- DBUG_PRINT("info",("ha_update_row() returns error %d",error));
- table->file->print_error(error, MYF(0));
- }
-
- DBUG_RETURN(error);
- }
- else
+ vec.push_back(std::vector<std::string>());
+ for (unsigned int i= 0; i < count; i++)
{
- DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
- if (invoke_triggers &&
- unlikely(process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE,
- TRUE)))
- error= HA_ERR_GENERIC; // in case if error is not set yet
- else
- {
- if (unlikely((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 &&
- unlikely(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. */
+ unsigned len1= net_field_length(&p);
+ vec.back().push_back(std::string(reinterpret_cast<char *>(p), len1));
+ p+= len1;
}
}
-
- if (invoke_triggers &&
- unlikely(process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)))
- error= HA_ERR_GENERIC; // in case if error is not set yet
-
- DBUG_RETURN(error);
}
+/**
+ Parses GEOMETRY_TYPE field.
-int Rows_log_event::update_sequence()
+ @param[out] vec stores geometry column's types extracted from field.
+ @param[in] field GEOMETRY_TYPE field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_geometry_type(std::vector<unsigned int> &vec,
+ unsigned char *field, unsigned int length)
{
- TABLE *table= m_table; // pointer to event's table
+ unsigned char* p= field;
- if (!bitmap_is_set(table->rpl_write_set, MIN_VALUE_FIELD_NO))
- {
- /* This event come from a setval function executed on the master.
- Update the sequence next_number and round, like we do with setval()
- */
- my_bitmap_map *old_map= dbug_tmp_use_all_columns(table,
- table->read_set);
- longlong nextval= table->field[NEXT_FIELD_NO]->val_int();
- longlong round= table->field[ROUND_FIELD_NO]->val_int();
- dbug_tmp_restore_column_map(table->read_set, old_map);
-
- return table->s->sequence->set_value(table, nextval, round, 0) > 0;
- }
-
- /*
- Update all fields in table and update the active sequence, like with
- ALTER SEQUENCE
- */
- return table->file->ha_write_row(table->record[0]);
+ while (p < field + length)
+ vec.push_back(net_field_length(&p));
}
+/**
+ Parses SIMPLE_PRIMARY_KEY field.
-#endif
-
-int
-Write_rows_log_event::do_exec_row(rpl_group_info *rgi)
+ @param[out] vec stores primary key's column information extracted from
+ field. Each column has an index and a prefix which are
+ stored as a unit_pair. prefix is always 0 for
+ SIMPLE_PRIMARY_KEY field.
+ @param[in] field SIMPLE_PRIMARY_KEY field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_simple_pk(std::vector<Table_map_log_event::
+ Optional_metadata_fields::uint_pair> &vec,
+ unsigned char *field, unsigned int length)
{
- DBUG_ASSERT(m_table != NULL);
- const char *tmp= thd->get_proc_info();
- const char *message= "Write_rows_log_event::write_row()";
- int error;
-
-#ifdef WSREP_PROC_INFO
- my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Write_rows_log_event::write_row(%lld)",
- (long long) wsrep_thd_trx_seqno(thd));
- message= thd->wsrep_info;
-#endif /* WSREP_PROC_INFO */
-
- thd_proc_info(thd, message);
- error= write_row(rgi, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
- thd_proc_info(thd, tmp);
-
- if (unlikely(error) && unlikely(!thd->is_error()))
- {
- DBUG_ASSERT(0);
- my_error(ER_UNKNOWN_ERROR, MYF(0));
- }
+ unsigned char* p= field;
- return error;
+ while (p < field + length)
+ vec.push_back(std::make_pair(net_field_length(&p), 0));
}
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+/**
+ Parses PRIMARY_KEY_WITH_PREFIX field.
-#ifdef MYSQL_CLIENT
-bool Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
-{
- DBUG_EXECUTE_IF("simulate_cache_read_error",
- {DBUG_SET("+d,simulate_my_b_fill_error");});
- return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows");
-}
+ @param[out] vec stores primary key's column information extracted from
+ field. Each column has an index and a prefix which are
+ stored as a unit_pair.
+ @param[in] field PRIMARY_KEY_WITH_PREFIX field in table_map_event.
+ @param[in] length length of the field
+ */
-bool Write_rows_compressed_log_event::print(FILE *file,
- PRINT_EVENT_INFO* print_event_info)
+static void parse_pk_with_prefix(std::vector<Table_map_log_event::
+ Optional_metadata_fields::uint_pair> &vec,
+ unsigned char *field, unsigned int length)
{
- char *new_buf;
- ulong len;
- bool is_malloc = false;
- if(!row_log_event_uncompress(glob_description_event,
- checksum_alg == BINLOG_CHECKSUM_ALG_CRC32,
- temp_buf, UINT_MAX32, NULL, 0, &is_malloc, &new_buf, &len))
- {
- free_temp_buf();
- register_temp_buf(new_buf, true);
- if (Rows_log_event::print_helper(file, print_event_info,
- "Write_compressed_rows"))
- goto err;
- }
- else
+ unsigned char* p= field;
+
+ while (p < field + length)
{
- if (my_b_printf(&print_event_info->head_cache,
- "ERROR: uncompress write_compressed_rows failed\n"))
- goto err;
+ unsigned int col_index= net_field_length(&p);
+ unsigned int col_prefix= net_field_length(&p);
+ vec.push_back(std::make_pair(col_index, col_prefix));
}
-
- return 0;
-err:
- return 1;
}
-#endif
-
-#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
-uint8 Write_rows_log_event::get_trg_event_map()
+Table_map_log_event::Optional_metadata_fields::
+Optional_metadata_fields(unsigned char* optional_metadata,
+ unsigned int optional_metadata_len)
{
- return trg2bit(TRG_EVENT_INSERT) | trg2bit(TRG_EVENT_UPDATE) |
- trg2bit(TRG_EVENT_DELETE);
-}
-#endif
+ unsigned char* field= optional_metadata;
-/**************************************************************************
- Delete_rows_log_event member functions
-**************************************************************************/
-
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-/*
- Compares table->record[0] and table->record[1]
+ if (optional_metadata == NULL)
+ return;
- Returns TRUE if different.
-*/
-static bool record_compare(TABLE *table)
-{
- bool result= FALSE;
- /**
- Compare full record only if:
- - there are no blob fields (otherwise we would also need
- to compare blobs contents as well);
- - there are no varchar fields (otherwise we would also need
- to compare varchar contents as well);
- - there are no null fields, otherwise NULLed fields
- contents (i.e., the don't care bytes) may show arbitrary
- values, depending on how each engine handles internally.
- */
- if ((table->s->blob_fields +
- table->s->varchar_fields +
- table->s->null_fields) == 0)
+ while (field < optional_metadata + optional_metadata_len)
{
- result= cmp_record(table,record[1]);
- goto record_compare_exit;
- }
+ unsigned int len;
+ Optional_metadata_field_type type=
+ static_cast<Optional_metadata_field_type>(field[0]);
- /* Compare null bits */
- if (memcmp(table->null_flags,
- table->null_flags+table->s->rec_buff_length,
- table->s->null_bytes))
- {
- result= TRUE; // Diff in NULL value
- goto record_compare_exit;
- }
+ // Get length and move field to the value.
+ field++;
+ len= net_field_length(&field);
- /* Compare fields */
- for (Field **ptr=table->field ; *ptr ; ptr++)
- {
- if (table->versioned() && (*ptr)->vers_sys_field())
- {
- continue;
- }
- /**
- We only compare field contents that are not null.
- NULL fields (i.e., their null bits) were compared
- earlier.
- */
- if (!(*(ptr))->is_null())
+ switch(type)
{
- if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
- {
- result= TRUE;
- goto record_compare_exit;
- }
- }
- }
-
-record_compare_exit:
- return result;
-}
-
-
-/**
- Find the best key to use when locating the row in @c find_row().
-
- A primary key is preferred if it exists; otherwise a unique index is
- preferred. Else we pick the index with the smallest rec_per_key value.
-
- If a suitable key is found, set @c m_key, @c m_key_nr and @c m_key_info
- member fields appropriately.
-
- @returns Error code on failure, 0 on success.
-*/
-int Rows_log_event::find_key()
-{
- uint i, best_key_nr, last_part;
- KEY *key, *UNINIT_VAR(best_key);
- ulong UNINIT_VAR(best_rec_per_key), tmp;
- DBUG_ENTER("Rows_log_event::find_key");
- DBUG_ASSERT(m_table);
-
- best_key_nr= MAX_KEY;
-
- /*
- Keys are sorted so that any primary key is first, followed by unique keys,
- followed by any other. So we will automatically pick the primary key if
- it exists.
- */
- for (i= 0, key= m_table->key_info; i < m_table->s->keys; i++, key++)
- {
- if (!m_table->s->keys_in_use.is_set(i))
- continue;
- /*
- We cannot use a unique key with NULL-able columns to uniquely identify
- a row (but we can still select it for range scan below if nothing better
- is available).
- */
- if ((key->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
- {
- best_key_nr= i;
- best_key= key;
+ case SIGNEDNESS:
+ parse_signedness(m_signedness, field, len);
break;
+ case DEFAULT_CHARSET:
+ parse_default_charset(m_default_charset, field, len);
+ break;
+ case COLUMN_CHARSET:
+ parse_column_charset(m_column_charset, field, len);
+ break;
+ case COLUMN_NAME:
+ parse_column_name(m_column_name, field, len);
+ break;
+ case SET_STR_VALUE:
+ parse_set_str_value(m_set_str_value, field, len);
+ break;
+ case ENUM_STR_VALUE:
+ parse_set_str_value(m_enum_str_value, field, len);
+ break;
+ case GEOMETRY_TYPE:
+ parse_geometry_type(m_geometry_type, field, len);
+ break;
+ case SIMPLE_PRIMARY_KEY:
+ parse_simple_pk(m_primary_key, field, len);
+ break;
+ case PRIMARY_KEY_WITH_PREFIX:
+ parse_pk_with_prefix(m_primary_key, field, len);
+ break;
+ case ENUM_AND_SET_DEFAULT_CHARSET:
+ parse_default_charset(m_enum_and_set_default_charset, field, len);
+ break;
+ case ENUM_AND_SET_COLUMN_CHARSET:
+ parse_column_charset(m_enum_and_set_column_charset, field, len);
+ break;
+ default:
+ DBUG_ASSERT(0);
}
- /*
- We can only use a non-unique key if it allows range scans (ie. skip
- FULLTEXT indexes and such).
- */
- last_part= key->user_defined_key_parts - 1;
- DBUG_PRINT("info", ("Index %s rec_per_key[%u]= %lu",
- key->name.str, last_part, key->rec_per_key[last_part]));
- if (!(m_table->file->index_flags(i, last_part, 1) & HA_READ_NEXT))
- continue;
-
- tmp= key->rec_per_key[last_part];
- if (best_key_nr == MAX_KEY || (tmp > 0 && tmp < best_rec_per_key))
- {
- best_key_nr= i;
- best_key= key;
- best_rec_per_key= tmp;
- }
- }
-
- if (best_key_nr == MAX_KEY)
- {
- m_key_info= NULL;
- DBUG_RETURN(0);
+ // next field
+ field+= len;
}
-
- // Allocate buffer for key searches
- m_key= (uchar *) my_malloc(best_key->key_length, MYF(MY_WME));
- if (m_key == NULL)
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- m_key_info= best_key;
- m_key_nr= best_key_nr;
-
- DBUG_RETURN(0);;
}
-/*
- Check if we are already spending too much time on this statement.
- if we are, warn user that it might be because table does not have
- a PK, but only if the warning was not printed before for this STMT.
-
- @param type The event type code.
- @param table_name The name of the table that the slave is
- operating.
- @param is_index_scan States whether the slave is doing an index scan
- or not.
- @param rli The relay metadata info.
-*/
-static inline
-void issue_long_find_row_warning(Log_event_type type,
- const char *table_name,
- bool is_index_scan,
- rpl_group_info *rgi)
-{
- if ((global_system_variables.log_warnings > 1 &&
- !rgi->is_long_find_row_note_printed()))
- {
- ulonglong now= microsecond_interval_timer();
- ulonglong stmt_ts= rgi->get_row_stmt_start_timestamp();
-
- DBUG_EXECUTE_IF("inject_long_find_row_note",
- stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2*HRTIME_RESOLUTION););
-
- longlong delta= (now - stmt_ts)/HRTIME_RESOLUTION;
-
- if (delta > LONG_FIND_ROW_THRESHOLD)
- {
- rgi->set_long_find_row_note_printed();
- const char* evt_type= LOG_EVENT_IS_DELETE_ROW(type) ? " DELETE" : "n UPDATE";
- const char* scan_type= is_index_scan ? "scanning an index" : "scanning the table";
-
- sql_print_information("The slave is applying a ROW event on behalf of a%s statement "
- "on table %s and is currently taking a considerable amount "
- "of time (%lld seconds). This is due to the fact that it is %s "
- "while looking up records to be processed. Consider adding a "
- "primary key (or unique key) to the table to improve "
- "performance.",
- evt_type, table_name, (long) delta, scan_type);
- }
- }
-}
+/**************************************************************************
+ Write_rows_log_event member functions
+**************************************************************************/
/*
- HA_ERR_KEY_NOT_FOUND is a fatal error normally, but it's an expected
- error in speculate optimistic mode, so use something non-fatal instead
-*/
-static int row_not_found_error(rpl_group_info *rgi)
+ Constructor used by slave to read the event from the binary log.
+ */
+#ifdef HAVE_REPLICATION
+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, description_event)
{
- return rgi->speculation != rpl_group_info::SPECULATE_OPTIMISTIC
- ? HA_ERR_KEY_NOT_FOUND : HA_ERR_RECORD_CHANGED;
}
-/**
- Locate the current row in event's table.
-
- The current row is pointed by @c m_curr_row. Member @c m_width tells
- how many columns are there in the row (this can be different from
- the number of columns in the table). It is assumed that event's
- table is already open and pointed by @c m_table.
-
- If a corresponding record is found in the table it is stored in
- @c m_table->record[0]. Note that when record is located based on a primary
- key, it is possible that the record found differs from the row being located.
-
- If no key is specified or table does not have keys, a table scan is used to
- find the row. In that case the row should be complete and contain values for
- all columns. However, it can still be shorter than the table, i.e. the table
- can contain extra columns not present in the row. It is also possible that
- the table has fewer columns than the row being located.
-
- @returns Error code on failure, 0 on success.
-
- @post In case of success @c m_table->record[0] contains the record found.
- Also, the internal "cursor" of the table is positioned at the record found.
-
- @note If the engine allows random access of the records, a combination of
- @c position() and @c rnd_pos() will be used.
-
- Note that one MUST call ha_index_or_rnd_end() after this function if
- it returns 0 as we must leave the row position in the handler intact
- for any following update/delete command.
-*/
-
-int Rows_log_event::find_row(rpl_group_info *rgi)
+Write_rows_compressed_log_event::Write_rows_compressed_log_event(
+ const char *buf, uint event_len,
+ const Format_description_log_event
+ *description_event)
+: Write_rows_log_event(buf, event_len, description_event)
{
- DBUG_ENTER("Rows_log_event::find_row");
-
- DBUG_ASSERT(m_table && m_table->in_use != NULL);
-
- TABLE *table= m_table;
- int error= 0;
- bool is_table_scan= false, is_index_scan= false;
-
- /*
- rpl_row_tabledefs.test specifies that
- if the extra field on the slave does not have a default value
- and this is okay with Delete or Update events.
- Todo: fix wl3228 hld that requires defaults for all types of events
- */
-
- prepare_record(table, m_width, FALSE);
- error= unpack_current_row(rgi);
-
- m_vers_from_plain= false;
- if (table->versioned())
- {
- Field *row_end= table->vers_end_field();
- DBUG_ASSERT(table->read_set);
- bitmap_set_bit(table->read_set, row_end->field_index);
- // check whether master table is unversioned
- if (row_end->val_int() == 0)
- {
- bitmap_set_bit(table->write_set, row_end->field_index);
- // Plain source table may have a PRIMARY KEY. And row_end is always
- // a part of PRIMARY KEY. Set it to max value for engine to find it in
- // index. Needed for an UPDATE/DELETE cases.
- table->vers_end_field()->set_max();
- m_vers_from_plain= true;
- }
- table->file->column_bitmaps_signal();
- }
-
- DBUG_PRINT("info",("looking for the following record"));
- DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
-
- if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
- table->s->primary_key < MAX_KEY)
- {
- /*
- Use a more efficient method to fetch the record given by
- table->record[0] if the engine allows it. We first compute a
- row reference using the position() member function (it will be
- stored in table->file->ref) and the use rnd_pos() to position
- the "cursor" (i.e., record[0] in this case) at the correct row.
-
- TODO: Add a check that the correct record has been fetched by
- comparing with the original record. Take into account that the
- record on the master and slave can be of different
- length. Something along these lines should work:
-
- ADD>>> store_record(table,record[1]);
- int error= table->file->ha_rnd_pos(table->record[0],
- table->file->ref);
- ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
- table->s->reclength) == 0);
-
- */
- int error;
- DBUG_PRINT("info",("locating record using primary key (position)"));
-
- error= table->file->ha_rnd_pos_by_record(table->record[0]);
- if (unlikely(error))
- {
- DBUG_PRINT("info",("rnd_pos returns error %d",error));
- if (error == HA_ERR_KEY_NOT_FOUND)
- error= row_not_found_error(rgi);
- table->file->print_error(error, MYF(0));
- }
- DBUG_RETURN(error);
- }
-
- // We can't use position() - try other methods.
-
- /*
- We need to retrieve all fields
- TODO: Move this out from this function to main loop
- */
- table->use_all_columns();
-
- /*
- Save copy of the record in table->record[1]. It might be needed
- later if linear search is used to find exact match.
- */
- store_record(table,record[1]);
-
- if (m_key_info)
- {
- DBUG_PRINT("info",("locating record using key #%u [%s] (index_read)",
- m_key_nr, m_key_info->name.str));
- /* We use this to test that the correct key is used in test cases. */
- DBUG_EXECUTE_IF("slave_crash_if_wrong_index",
- if(0 != strcmp(m_key_info->name.str,"expected_key")) abort(););
-
- /* The key is active: search the table using the index */
- if (!table->file->inited &&
- (error= table->file->ha_index_init(m_key_nr, FALSE)))
- {
- DBUG_PRINT("info",("ha_index_init returns error %d",error));
- table->file->print_error(error, MYF(0));
- goto end;
- }
-
- /* Fill key data for the row */
-
- DBUG_ASSERT(m_key);
- key_copy(m_key, table->record[0], m_key_info, 0);
-
- /*
- Don't print debug messages when running valgrind since they can
- trigger false warnings.
- */
-#ifndef HAVE_valgrind
- DBUG_DUMP("key data", m_key, m_key_info->key_length);
-#endif
-
- /*
- We need to set the null bytes to ensure that the filler bit are
- all set when returning. There are storage engines that just set
- the necessary bits on the bytes and don't set the filler bits
- correctly.
- */
- if (table->s->null_bytes > 0)
- table->record[0][table->s->null_bytes - 1]|=
- 256U - (1U << table->s->last_null_bit_pos);
-
- if (unlikely((error= table->file->ha_index_read_map(table->record[0],
- m_key,
- HA_WHOLE_KEY,
- HA_READ_KEY_EXACT))))
- {
- DBUG_PRINT("info",("no record matching the key found in the table"));
- if (error == HA_ERR_KEY_NOT_FOUND)
- error= row_not_found_error(rgi);
- table->file->print_error(error, MYF(0));
- table->file->ha_index_end();
- goto end;
- }
-
- /*
- Don't print debug messages when running valgrind since they can
- trigger false warnings.
- */
-#ifndef HAVE_valgrind
- DBUG_PRINT("info",("found first matching record"));
- DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
-#endif
- /*
- Below is a minor "optimization". If the key (i.e., key number
- 0) has the HA_NOSAME flag set, we know that we have found the
- correct record (since there can be no duplicates); otherwise, we
- have to compare the record with the one found to see if it is
- the correct one.
-
- CAVEAT! This behaviour is essential for the replication of,
- e.g., the mysql.proc table since the correct record *shall* be
- found using the primary key *only*. There shall be no
- comparison of non-PK columns to decide if the correct record is
- found. I can see no scenario where it would be incorrect to
- chose the row to change only using a PK or an UNNI.
- */
- if (table->key_info->flags & HA_NOSAME)
- {
- /* Unique does not have non nullable part */
- if (!(table->key_info->flags & (HA_NULL_PART_KEY)))
- {
- error= 0;
- goto end;
- }
- else
- {
- KEY *keyinfo= table->key_info;
- /*
- Unique has nullable part. We need to check if there is any
- field in the BI image that is null and part of UNNI.
- */
- bool null_found= FALSE;
- 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;
- null_found= (*f)->is_null();
- }
-
- if (!null_found)
- {
- error= 0;
- goto end;
- }
-
- /* else fall through to index scan */
- }
- }
-
- is_index_scan=true;
-
- /*
- In case key is not unique, we still have to iterate over records found
- and find the one which is identical to the row given. A copy of the
- record we are looking for is stored in record[1].
- */
- DBUG_PRINT("info",("non-unique index, scanning it to find matching record"));
- /* We use this to test that the correct key is used in test cases. */
- DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort(););
-
- while (record_compare(table))
- {
- while ((error= table->file->ha_index_next(table->record[0])))
- {
- DBUG_PRINT("info",("no record matching the given row found"));
- table->file->print_error(error, MYF(0));
- table->file->ha_index_end();
- goto end;
- }
- }
- }
- else
- {
- DBUG_PRINT("info",("locating record using table scan (rnd_next)"));
- /* We use this to test that the correct key is used in test cases. */
- DBUG_EXECUTE_IF("slave_crash_if_table_scan", abort(););
-
- /* We don't have a key: search the table using rnd_next() */
- if (unlikely((error= table->file->ha_rnd_init_with_error(1))))
- {
- DBUG_PRINT("info",("error initializing table scan"
- " (ha_rnd_init returns %d)",error));
- goto end;
- }
-
- is_table_scan= true;
-
- /* Continue until we find the right record or have made a full loop */
- do
- {
- error= table->file->ha_rnd_next(table->record[0]);
-
- if (unlikely(error))
- DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
- switch (error) {
-
- case 0:
- DBUG_DUMP("record found", table->record[0], table->s->reclength);
- break;
-
- case HA_ERR_END_OF_FILE:
- DBUG_PRINT("info", ("Record not found"));
- table->file->ha_rnd_end();
- goto end;
-
- default:
- DBUG_PRINT("info", ("Failed to get next record"
- " (rnd_next returns %d)",error));
- table->file->print_error(error, MYF(0));
- table->file->ha_rnd_end();
- goto end;
- }
- }
- while (record_compare(table));
-
- /*
- Note: above record_compare will take into account all record fields
- which might be incorrect in case a partial row was given in the event
- */
-
- DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
- }
-
-end:
- if (is_table_scan || is_index_scan)
- issue_long_find_row_warning(get_general_type_code(), m_table->alias.c_ptr(),
- is_index_scan, rgi);
- DBUG_RETURN(error);
+ uncompress_buf();
}
-
#endif
-/*
- Constructor used to build an event for writing to the binary log.
- */
-
-#ifndef MYSQL_CLIENT
-Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid, bool is_transactional)
- : Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional,
- DELETE_ROWS_EVENT_V1)
-{
-}
-
-Delete_rows_compressed_log_event::Delete_rows_compressed_log_event(
- THD *thd_arg, TABLE *tbl_arg,
- ulong tid_arg,
- bool is_transactional)
- : Delete_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional)
-{
- m_type= DELETE_ROWS_COMPRESSED_EVENT_V1;
-}
-bool Delete_rows_compressed_log_event::write()
-{
- return Rows_log_event::write_compressed();
-}
-#endif /* #if !defined(MYSQL_CLIENT) */
+/**************************************************************************
+ Delete_rows_log_event member functions
+**************************************************************************/
/*
Constructor used by slave to read the event from the binary log.
@@ -14396,199 +3949,10 @@ Delete_rows_compressed_log_event::Delete_rows_compressed_log_event(
}
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-
-int
-Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
-{
- /*
- Increment the global status delete count variable
- */
- if (get_flags(STMT_END_F))
- status_var_increment(thd->status_var.com_stat[SQLCOM_DELETE]);
-
- if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
- m_table->s->primary_key < MAX_KEY)
- {
- /*
- We don't need to allocate any memory for m_key since it is not used.
- */
- return 0;
- }
- if (slave_run_triggers_for_rbr && !master_had_triggers)
- m_table->prepare_triggers_for_delete_stmt_or_event();
-
- return find_key();
-}
-
-int
-Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
- int error)
-{
- m_table->file->ha_index_or_rnd_end();
- my_free(m_key);
- m_key= NULL;
- m_key_info= NULL;
-
- return error;
-}
-
-int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
-{
- int error;
- const char *tmp= thd->get_proc_info();
- const char *message= "Delete_rows_log_event::find_row()";
- const bool invoke_triggers=
- slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
- DBUG_ASSERT(m_table != NULL);
-
-#ifdef WSREP_PROC_INFO
- my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Delete_rows_log_event::find_row(%lld)",
- (long long) wsrep_thd_trx_seqno(thd));
- message= thd->wsrep_info;
-#endif /* WSREP_PROC_INFO */
-
- thd_proc_info(thd, message);
- if (likely(!(error= find_row(rgi))))
- {
- /*
- Delete the record found, located in record[0]
- */
- message= "Delete_rows_log_event::ha_delete_row()";
-#ifdef WSREP_PROC_INFO
- snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Delete_rows_log_event::ha_delete_row(%lld)",
- (long long) wsrep_thd_trx_seqno(thd));
- message= thd->wsrep_info;
-#endif
- thd_proc_info(thd, message);
-
- if (invoke_triggers &&
- unlikely(process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)))
- error= HA_ERR_GENERIC; // in case if error is not set yet
- if (likely(!error))
- {
- m_table->mark_columns_per_binlog_row_image();
- if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
- {
- Field *end= m_table->vers_end_field();
- bitmap_set_bit(m_table->write_set, end->field_index);
- store_record(m_table, record[1]);
- end->set_time();
- error= m_table->file->ha_update_row(m_table->record[1],
- m_table->record[0]);
- }
- else
- {
- error= m_table->file->ha_delete_row(m_table->record[0]);
- }
- m_table->default_column_bitmaps();
- }
- if (invoke_triggers && likely(!error) &&
- unlikely(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();
- }
- thd_proc_info(thd, tmp);
- return error;
-}
-
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
-
-#ifdef MYSQL_CLIENT
-bool Delete_rows_log_event::print(FILE *file,
- PRINT_EVENT_INFO* print_event_info)
-{
- return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows");
-}
-
-bool Delete_rows_compressed_log_event::print(FILE *file,
- PRINT_EVENT_INFO* print_event_info)
-{
- char *new_buf;
- ulong len;
- bool is_malloc = false;
- if(!row_log_event_uncompress(glob_description_event,
- checksum_alg == BINLOG_CHECKSUM_ALG_CRC32,
- temp_buf, UINT_MAX32, NULL, 0, &is_malloc, &new_buf, &len))
- {
- free_temp_buf();
- register_temp_buf(new_buf, true);
- if (Rows_log_event::print_helper(file, print_event_info,
- "Delete_compressed_rows"))
- goto err;
- }
- else
- {
- if (my_b_printf(&print_event_info->head_cache,
- "ERROR: uncompress delete_compressed_rows failed\n"))
- goto err;
- }
-
- return 0;
-err:
- return 1;
-}
-#endif
-
-
-#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
-uint8 Delete_rows_log_event::get_trg_event_map()
-{
- return trg2bit(TRG_EVENT_DELETE);
-}
-#endif
-
/**************************************************************************
Update_rows_log_event member functions
**************************************************************************/
-/*
- Constructor used to build an event for writing to the binary log.
- */
-#if !defined(MYSQL_CLIENT)
-Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid,
- bool is_transactional)
-: Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional,
- UPDATE_ROWS_EVENT_V1)
-{
- init(tbl_arg->rpl_write_set);
-}
-
-Update_rows_compressed_log_event::Update_rows_compressed_log_event(THD *thd_arg, TABLE *tbl_arg,
- ulong tid,
- bool is_transactional)
-: Update_rows_log_event(thd_arg, tbl_arg, tid, is_transactional)
-{
- m_type = UPDATE_ROWS_COMPRESSED_EVENT_V1;
-}
-
-bool Update_rows_compressed_log_event::write()
-{
- return Rows_log_event::write_compressed();
-}
-
-void Update_rows_log_event::init(MY_BITMAP const *cols)
-{
- /* 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)))
- {
- /* Cols can be zero if this is a dummy binrows event */
- if (likely(cols != NULL))
- {
- memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols));
- create_last_word_mask(&m_cols_ai);
- }
- }
-}
-#endif /* !defined(MYSQL_CLIENT) */
-
-
Update_rows_log_event::~Update_rows_log_event()
{
if (m_cols_ai.bitmap)
@@ -14622,203 +3986,6 @@ Update_rows_compressed_log_event::Update_rows_compressed_log_event(
}
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-
-int
-Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
-{
- /*
- Increment the global status update count variable
- */
- if (get_flags(STMT_END_F))
- status_var_increment(thd->status_var.com_stat[SQLCOM_UPDATE]);
-
- int err;
- if ((err= find_key()))
- return err;
-
- if (slave_run_triggers_for_rbr && !master_had_triggers)
- m_table->prepare_triggers_for_update_stmt_or_event();
-
- return 0;
-}
-
-int
-Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
- int error)
-{
- /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
- m_table->file->ha_index_or_rnd_end();
- my_free(m_key); // Free for multi_malloc
- m_key= NULL;
- m_key_info= NULL;
-
- return error;
-}
-
-int
-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;
- const char *tmp= thd->get_proc_info();
- const char *message= "Update_rows_log_event::find_row()";
- DBUG_ASSERT(m_table != NULL);
-
-#ifdef WSREP_PROC_INFO
- my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Update_rows_log_event::find_row(%lld)",
- (long long) wsrep_thd_trx_seqno(thd));
- message= thd->wsrep_info;
-#endif /* WSREP_PROC_INFO */
-
- thd_proc_info(thd, message);
- // Temporary fix to find out why it fails [/Matz]
- memcpy(m_table->read_set->bitmap, m_cols.bitmap, (m_table->read_set->n_bits + 7) / 8);
- memcpy(m_table->write_set->bitmap, m_cols_ai.bitmap, (m_table->write_set->n_bits + 7) / 8);
-
- m_table->mark_columns_per_binlog_row_image();
-
- int error= find_row(rgi);
- if (unlikely(error))
- {
- /*
- We need to read the second image in the event of error to be
- able to skip to the next pair of updates
- */
- if ((m_curr_row= m_curr_row_end))
- unpack_current_row(rgi, &m_cols_ai);
- thd_proc_info(thd, tmp);
- return error;
- }
-
- /*
- This is the situation after locating BI:
-
- ===|=== before image ====|=== after image ===|===
- ^ ^
- m_curr_row m_curr_row_end
-
- BI found in the table is stored in record[0]. We copy it to record[1]
- and unpack AI to record[0].
- */
-
- store_record(m_table,record[1]);
-
- m_curr_row= m_curr_row_end;
- message= "Update_rows_log_event::unpack_current_row()";
-#ifdef WSREP_PROC_INFO
- my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Update_rows_log_event::unpack_current_row(%lld)",
- (long long) wsrep_thd_trx_seqno(thd));
- message= thd->wsrep_info;
-#endif /* WSREP_PROC_INFO */
-
- /* this also updates m_curr_row_end */
- thd_proc_info(thd, message);
- if (unlikely((error= unpack_current_row(rgi, &m_cols_ai))))
- goto err;
-
- /*
- Now we have the right row to update. The old row (the one we're
- looking for) is in record[1] and the new row is in record[0].
- */
-#ifndef HAVE_valgrind
- /*
- Don't print debug messages when running valgrind since they can
- trigger false warnings.
- */
- DBUG_PRINT("info",("Updating row in table"));
- DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
- DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
-#endif
-
- message= "Update_rows_log_event::ha_update_row()";
-#ifdef WSREP_PROC_INFO
- my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "Update_rows_log_event::ha_update_row(%lld)",
- (long long) wsrep_thd_trx_seqno(thd));
- message= thd->wsrep_info;
-#endif /* WSREP_PROC_INFO */
-
- thd_proc_info(thd, message);
- if (invoke_triggers &&
- unlikely(process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE)))
- {
- error= HA_ERR_GENERIC; // in case if error is not set yet
- goto err;
- }
-
- if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
- m_table->vers_update_fields();
- error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
- if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
- error= 0;
- if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
- {
- store_record(m_table, record[2]);
- error= vers_insert_history_row(m_table);
- restore_record(m_table, record[2]);
- }
- m_table->default_column_bitmaps();
-
- if (invoke_triggers && likely(!error) &&
- unlikely(process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE)))
- error= HA_ERR_GENERIC; // in case if error is not set yet
-
- thd_proc_info(thd, tmp);
-
-err:
- m_table->file->ha_index_or_rnd_end();
- return error;
-}
-
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
-
-#ifdef MYSQL_CLIENT
-bool Update_rows_log_event::print(FILE *file,
- PRINT_EVENT_INFO* print_event_info)
-{
- return Rows_log_event::print_helper(file, print_event_info, "Update_rows");
-}
-
-bool
-Update_rows_compressed_log_event::print(FILE *file,
- PRINT_EVENT_INFO *print_event_info)
-{
- char *new_buf;
- ulong len;
- bool is_malloc= false;
- if(!row_log_event_uncompress(glob_description_event,
- checksum_alg == BINLOG_CHECKSUM_ALG_CRC32,
- temp_buf, UINT_MAX32, NULL, 0, &is_malloc, &new_buf, &len))
- {
- free_temp_buf();
- register_temp_buf(new_buf, true);
- if (Rows_log_event::print_helper(file, print_event_info,
- "Update_compressed_rows"))
- goto err;
- }
- else
- {
- if (my_b_printf(&print_event_info->head_cache,
- "ERROR: uncompress update_compressed_rows failed\n"))
- goto err;
- }
-
- return 0;
-err:
- return 1;
-}
-#endif
-
-#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
-uint8 Update_rows_log_event::get_trg_event_map()
-{
- return trg2bit(TRG_EVENT_UPDATE);
-}
-#endif
-
Incident_log_event::Incident_log_event(const char *buf, uint event_len,
const Format_description_log_event *descr_event)
: Log_event(buf, descr_event)
@@ -14855,7 +4022,7 @@ Incident_log_event::Incident_log_event(const char *buf, uint event_len,
m_incident= INCIDENT_NONE;
DBUG_VOID_RETURN;
}
- if (!(m_message.str= (char*) my_malloc(len+1, MYF(MY_WME))))
+ if (!(m_message.str= (char*) my_malloc(key_memory_log_event, len+1, MYF(MY_WME))))
{
/* Mark this event invalid */
m_incident= INCIDENT_NONE;
@@ -14888,123 +4055,6 @@ Incident_log_event::description() const
}
-#ifndef MYSQL_CLIENT
-void Incident_log_event::pack_info(Protocol *protocol)
-{
- char buf[256];
- size_t bytes;
- if (m_message.length > 0)
- bytes= my_snprintf(buf, sizeof(buf), "#%d (%s)",
- m_incident, description());
- else
- bytes= my_snprintf(buf, sizeof(buf), "#%d (%s): %s",
- m_incident, description(), m_message.str);
- protocol->store(buf, bytes, &my_charset_bin);
-}
-#endif /* MYSQL_CLIENT */
-
-
-#if defined(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)
-{
- char *head= (*arg_buf);
- uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
- char *buf= (*arg_buf);
- const char *error= 0;
- Log_event *res= 0;
- DBUG_ENTER("wsrep_read_log_event");
-
- 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,(uchar)head[EVENT_TYPE_OFFSET]);
- }
- (*arg_buf)+= data_len;
- (*arg_buf_len)-= data_len;
- DBUG_RETURN(res);
-}
-#endif
-
-
-#ifdef MYSQL_CLIENT
-bool Incident_log_event::print(FILE *file,
- PRINT_EVENT_INFO *print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- Write_on_release_cache cache(&print_event_info->head_cache, file);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\n# Incident: %s\nRELOAD DATABASE; # Shall generate syntax error\n", description()))
- return 1;
- return cache.flush_data();
-}
-#endif
-
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int
-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");
-
- if (ignored_error_code(ER_SLAVE_INCIDENT))
- {
- DBUG_PRINT("info", ("Ignoring Incident"));
- DBUG_RETURN(0);
- }
-
- rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT, NULL,
- ER_THD(rgi->thd, ER_SLAVE_INCIDENT),
- description(),
- m_message.length > 0 ? m_message.str : "<none>");
- DBUG_RETURN(1);
-}
-#endif
-
-#ifdef MYSQL_SERVER
-bool
-Incident_log_event::write_data_header()
-{
- DBUG_ENTER("Incident_log_event::write_data_header");
- DBUG_PRINT("enter", ("m_incident: %d", m_incident));
- uchar buf[sizeof(int16)];
- int2store(buf, (int16) m_incident);
- DBUG_RETURN(write_data(buf, sizeof(buf)));
-}
-
-bool
-Incident_log_event::write_data_body()
-{
- uchar tmp[1];
- DBUG_ENTER("Incident_log_event::write_data_body");
- tmp[0]= (uchar) m_message.length;
- DBUG_RETURN(write_data(tmp, sizeof(tmp)) ||
- write_data(m_message.str, m_message.length));
-}
-#endif
-
Ignorable_log_event::Ignorable_log_event(const char *buf,
const Format_description_log_event
*descr_event,
@@ -15020,155 +4070,8 @@ Ignorable_log_event::~Ignorable_log_event()
{
}
-#ifndef MYSQL_CLIENT
-/* Pack info for its unrecognized ignorable event */
-void Ignorable_log_event::pack_info(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 */
-bool Ignorable_log_event::print(FILE *file,
- PRINT_EVENT_INFO *print_event_info)
-{
- if (print_event_info->short_form)
- return 0;
-
- if (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))
- return 1;
- return 0;
-}
-#endif
-
-
-#ifdef MYSQL_CLIENT
-/**
- The default values for these variables should be values that are
- *incorrect*, i.e., values that cannot occur in an event. This way,
- they will always be printed for the first event.
-*/
-st_print_event_info::st_print_event_info()
-{
- myf const flags = MYF(MY_WME | MY_NABP);
- /*
- Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
- program's startup, but these explicit bzero() is for the day someone
- creates dynamic instances.
- */
- bzero(db, sizeof(db));
- bzero(charset, sizeof(charset));
- bzero(time_zone_str, sizeof(time_zone_str));
- delimiter[0]= ';';
- delimiter[1]= 0;
- flags2_inited= 0;
- sql_mode_inited= 0;
- row_events= 0;
- sql_mode= 0;
- auto_increment_increment= 0;
- auto_increment_offset= 0;
- charset_inited= 0;
- lc_time_names_number= ~0;
- charset_database_number= ILLEGAL_CHARSET_INFO_NUMBER;
- thread_id= 0;
- server_id= 0;
- domain_id= 0;
- thread_id_printed= false;
- server_id_printed= false;
- domain_id_printed= false;
- allow_parallel= true;
- allow_parallel_printed= false;
- found_row_event= false;
- print_row_count= false;
- short_form= false;
- skip_replication= 0;
- printed_fd_event=FALSE;
- file= 0;
- base64_output_mode=BASE64_OUTPUT_UNSPEC;
- open_cached_file(&head_cache, NULL, NULL, 0, flags);
- open_cached_file(&body_cache, NULL, NULL, 0, flags);
- open_cached_file(&tail_cache, NULL, NULL, 0, flags);
-#ifdef WHEN_FLASHBACK_REVIEW_READY
- open_cached_file(&review_sql_cache, NULL, NULL, 0, flags);
-#endif
-}
-
-
-bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to)
-{
- reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE);
- if (cache->end_of_file > SIZE_T_MAX ||
- !(to->str= (char*) my_malloc((to->length= (size_t)cache->end_of_file), MYF(0))))
- {
- perror("Out of memory: can't allocate memory in copy_event_cache_to_string_and_reinit().");
- goto err;
- }
- if (my_b_read(cache, (uchar*) to->str, to->length))
- {
- my_free(to->str);
- perror("Can't read data from IO_CACHE");
- return true;
- }
- reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
- return false;
-
-err:
- to->str= 0;
- to->length= 0;
- return true;
-}
-#endif /* MYSQL_CLIENT */
-
bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file)
{
return (my_b_copy_all_to_file(cache, file) ||
reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE));
}
-
-#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)
- :Log_event(buf, description_event)
-{
- uint8 header_size= description_event->common_header_len;
- ident_len = event_len - header_size;
- set_if_smaller(ident_len,FN_REFLEN-1);
- log_ident= buf + header_size;
-}
-#endif
-
-#if defined(MYSQL_SERVER)
-/**
- 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 ||
- event_type == TRANSACTION_CONTEXT_EVENT ||
- event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
- (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
- return 1;
- return 0;
-}
-#endif /* MYSQL_SERVER */
diff --git a/sql/log_event.h b/sql/log_event.h
index 49e244f1151..4e193232f4b 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
- Copyright (c) 2009, 2017, MariaDB Corporation.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -35,6 +35,11 @@
#include <my_bitmap.h>
#include "rpl_constants.h"
+#include <vector>
+#include <string>
+#include <functional>
+#include <memory>
+#include <map>
#ifdef MYSQL_CLIENT
#include "sql_const.h"
@@ -217,6 +222,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -518,11 +524,11 @@ class String;
*/
#define OPTIONS_WRITTEN_TO_BIN_LOG \
(OPTION_AUTO_IS_NULL | OPTION_NO_FOREIGN_KEY_CHECKS | \
- OPTION_RELAXED_UNIQUE_CHECKS | OPTION_NOT_AUTOCOMMIT)
+ OPTION_RELAXED_UNIQUE_CHECKS | OPTION_NOT_AUTOCOMMIT | OPTION_IF_EXISTS)
/* Shouldn't be defined before */
#define EXPECTED_OPTIONS \
- ((1ULL << 14) | (1ULL << 26) | (1ULL << 27) | (1ULL << 19))
+ ((1ULL << 14) | (1ULL << 26) | (1ULL << 27) | (1ULL << 19) | (1ULL << 28))
#if OPTIONS_WRITTEN_TO_BIN_LOG != EXPECTED_OPTIONS
#error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values!
@@ -659,6 +665,7 @@ enum Log_event_type
/* MySQL 5.7 events, ignored by MariaDB */
TRANSACTION_CONTEXT_EVENT= 36,
VIEW_CHANGE_EVENT= 37,
+ /* not ignored */
XA_PREPARE_LOG_EVENT= 38,
/*
@@ -791,7 +798,6 @@ enum Int_event_type
INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2
};
-
#ifdef MYSQL_SERVER
class String;
class MYSQL_BIN_LOG;
@@ -881,6 +887,7 @@ typedef struct st_print_event_info
statement for it.
*/
bool skip_replication;
+ bool print_table_metadata;
/*
These two caches are used by the row-based replication events to
@@ -927,6 +934,8 @@ typedef struct st_print_event_info
class Log_event_writer
{
+ /* Log_event_writer is updated when ctx is set */
+ int (Log_event_writer::*encrypt_or_write)(const uchar *pos, size_t len);
public:
ulonglong bytes_written;
void *ctx; ///< Encryption context or 0 if no encryption is needed
@@ -937,10 +946,14 @@ public:
int write_footer();
my_off_t pos() { return my_b_safe_tell(file); }
void add_status(enum_logged_status status);
+ void set_incident();
+ void set_encrypted_writer()
+ { encrypt_or_write= &Log_event_writer::encrypt_and_write; }
Log_event_writer(IO_CACHE *file_arg, binlog_cache_data *cache_data_arg,
Binlog_crypt_data *cr= 0)
- : bytes_written(0), ctx(0),
+ :encrypt_or_write(&Log_event_writer::write_internal),
+ bytes_written(0), ctx(0),
file(file_arg), cache_data(cache_data_arg), crypto(cr) { }
private:
@@ -1221,7 +1234,7 @@ public:
*/
uint16 flags;
- uint16 cache_type;
+ enum_event_cache_type cache_type;
/**
A storage to cache the global system variable's value.
@@ -1354,7 +1367,8 @@ public:
static void *operator new(size_t size)
{
- return (void*) my_malloc((uint)size, MYF(MY_WME|MY_FAE));
+ extern PSI_memory_key key_memory_log_event;
+ return my_malloc(key_memory_log_event, size, MYF(MY_WME|MY_FAE));
}
static void operator delete(void *ptr, size_t)
@@ -2213,8 +2227,10 @@ public:
virtual bool is_commit() { return false; }
virtual bool is_rollback() { return false; }
#ifdef MYSQL_SERVER
- Query_compressed_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
- bool using_trans, bool direct, bool suppress_use, int error);
+ Query_compressed_log_event(THD* thd_arg, const char* query_arg,
+ ulong query_length,
+ bool using_trans, bool direct, bool suppress_use,
+ int error);
virtual bool write();
#endif
};
@@ -2225,24 +2241,16 @@ public:
****************************************************************************/
struct sql_ex_info
{
- sql_ex_info():
- cached_new_format(-1),
- field_term_len(0),
- enclosed_len(0),
- line_term_len(0),
- line_start_len(0),
- escaped_len(0),
- empty_flags(0)
- {} /* Remove gcc warning */
const char* field_term;
const char* enclosed;
const char* line_term;
const char* line_start;
const char* escaped;
- int cached_new_format;
- uint8 field_term_len,enclosed_len,line_term_len,line_start_len, escaped_len;
+ int cached_new_format= -1;
+ uint8 field_term_len= 0, enclosed_len= 0, line_term_len= 0,
+ line_start_len= 0, escaped_len= 0;
char opt_flags;
- char empty_flags;
+ char empty_flags= 0;
// store in new format even if old is possible
void force_new_format() { cached_new_format = 1;}
@@ -3027,6 +3035,32 @@ private:
#endif
};
+
+class Xid_apply_log_event: public Log_event
+{
+public:
+#ifdef MYSQL_SERVER
+ Xid_apply_log_event(THD* thd_arg):
+ Log_event(thd_arg, 0, TRUE) {}
+#endif
+ Xid_apply_log_event(const char* buf,
+ const Format_description_log_event *description_event):
+ Log_event(buf, description_event) {}
+
+ ~Xid_apply_log_event() {}
+ bool is_valid() const { return 1; }
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual int do_commit()= 0;
+ virtual int do_apply_event(rpl_group_info *rgi);
+ int do_record_gtid(THD *thd, rpl_group_info *rgi, bool in_trans,
+ void **out_hton);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+ virtual const char* get_query()= 0;
+#endif
+};
+
+
/**
@class Xid_log_event
@@ -3039,18 +3073,22 @@ private:
typedef ulonglong my_xid; // this line is the same as in handler.h
#endif
-class Xid_log_event: public Log_event
+class Xid_log_event: public Xid_apply_log_event
{
- public:
- my_xid xid;
+public:
+ my_xid xid;
#ifdef MYSQL_SERVER
Xid_log_event(THD* thd_arg, my_xid x, bool direct):
- Log_event(thd_arg, 0, TRUE), xid(x)
+ Xid_apply_log_event(thd_arg), xid(x)
{
if (direct)
cache_type= Log_event::EVENT_NO_CACHE;
}
+ const char* get_query()
+ {
+ return "COMMIT /* implicit, from Xid_log_event */";
+ }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
@@ -3066,15 +3104,172 @@ class Xid_log_event: public Log_event
#ifdef MYSQL_SERVER
bool write();
#endif
- bool is_valid() const { return 1; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(rpl_group_info *rgi);
- enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+ int do_commit();
+#endif
+};
+
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off. The latter option is only for
+ compatibility with the upstream.
+
+ From the groupping perspective the event finalizes the current
+ "prepare" group that is started with Gtid_log_event similarly to the
+ regular replicated transaction.
+*/
+
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ {MYSQL_,}XID definitions.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+ @param fmt formatID value
+ @param gln gtrid_length value
+ @param bln bqual_length value
+ @param dat data value
+
+ @return the value of the buffer pointer
+*/
+
+inline char *serialize_xid(char *buf, long fmt, long gln, long bln,
+ const char *dat)
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string consisting of the hex format representation of XID
+ as passed through fmt,gln,bln,dat argument:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gln; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) dat)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) dat)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gln + bln; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) dat)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) dat)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", fmt);
+
+ return buf;
+}
+
+/*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+*/
+static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+struct event_mysql_xid_t : MYSQL_XID
+{
+ char buf[ser_buf_size];
+ char *serialize()
+ {
+ return serialize_xid(buf, formatID, gtrid_length, bqual_length, data);
+ }
+};
+
+#ifndef MYSQL_CLIENT
+struct event_xid_t : XID
+{
+ char buf[ser_buf_size];
+
+ char *serialize(char *buf_arg)
+ {
+ return serialize_xid(buf_arg, formatID, gtrid_length, bqual_length, data);
+ }
+ char *serialize()
+ {
+ return serialize(buf);
+ }
+};
+#endif
+
+class XA_prepare_log_event: public Xid_apply_log_event
+{
+protected:
+
+ /* Constant contributor to subheader in write() by members of XID struct. */
+ static const int xid_subheader_no_data= 12;
+ event_mysql_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Xid_apply_log_event(thd_arg), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ bool is_valid() const { return m_xid.formatID != -1; }
+ int get_data_size()
+ {
+ return xid_subheader_no_data + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + ser_buf_size];
+ int do_commit();
+ const char* get_query()
+ {
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ m_xid.serialize());
+ return query;
+ }
#endif
};
+
/**
@class User_var_log_event
@@ -3387,8 +3582,12 @@ public:
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ event_xid_t xid;
+#else
+ event_mysql_xid_t xid;
+#endif
uchar flags2;
-
/* Flags2. */
/* FL_STANDALONE is set when there is no terminating COMMIT event. */
@@ -3415,6 +3614,10 @@ public:
static const uchar FL_WAITED= 16;
/* FL_DDL is set for event group containing DDL. */
static const uchar FL_DDL= 32;
+ /* FL_PREPARED_XA is set for XA transaction. */
+ static const uchar FL_PREPARED_XA= 64;
+ /* FL_"COMMITTED or ROLLED-BACK"_XA is set for XA transaction. */
+ static const uchar FL_COMPLETED_XA= 128;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
@@ -4073,6 +4276,18 @@ private:
ninth is in the least significant bit of the second byte, and so
on. </td>
</tr>
+ <tr>
+ <td>optional metadata fields</td>
+ <td>optional metadata fields are stored in Type, Length, Value(TLV) format.
+ Type takes 1 byte. Length is a packed integer value. Values takes
+ Length bytes.
+ </td>
+ <td>There are some optional metadata defined. They are listed in the table
+ @ref Table_table_map_event_optional_metadata. Optional metadata fields
+ follow null_bits. Whether binlogging an optional metadata is decided by the
+ server. The order is not defined, so they can be binlogged in any order.
+ </td>
+ </tr>
</table>
@@ -4278,6 +4493,123 @@ private:
</tr>
</table>
+ The table below lists all optional metadata types, along with the numerical
+ identifier for it and the size and interpretation of meta-data used
+ to describe the type.
+
+ @anchor Table_table_map_event_optional_metadata
+ <table>
+ <caption>Table_map_event optional metadata types: numerical identifier and
+ metadata. Optional metadata fields are stored in TLV fields.
+ Format of values are described in this table. </caption>
+ <tr>
+ <th>Type</th>
+ <th>Description</th>
+ <th>Format</th>
+ </tr>
+ <tr>
+ <td>SIGNEDNESS</td>
+ <td>signedness of numeric colums. This is included for all values of
+ binlog_row_metadata.</td>
+ <td>For each numeric column, a bit indicates whether the numeric
+ colunm has unsigned flag. 1 means it is unsigned. The number of
+ bytes needed for this is int((column_count + 7) / 8). The order is
+ the same as the order of column_type field.</td>
+ </tr>
+ <tr>
+ <td>DEFAULT_CHARSET</td>
+ <td>Charsets of character columns. It has a default charset for
+ the case that most of character columns have same charset and the
+ most used charset is binlogged as default charset.Collation
+ numbers are binlogged for identifying charsets. They are stored in
+ packed length format. Either DEFAULT_CHARSET or COLUMN_CHARSET is
+ included for all values of binlog_row_metadata.</td>
+ <td>Default charset's collation is logged first. The charsets which are not
+ same to default charset are logged following default charset. They are
+ logged as column index and charset collation number pair sequence. The
+ column index is counted only in all character columns. The order is same to
+ the order of column_type
+ field. </td>
+ </tr>
+ <tr>
+ <td>COLUMN_CHARSET</td>
+ <td>Charsets of character columns. For the case that most of columns have
+ different charsets, this field is logged. It is never logged with
+ DEFAULT_CHARSET together. Either DEFAULT_CHARSET or COLUMN_CHARSET is
+ included for all values of binlog_row_metadata.</td>
+ <td>It is a collation number sequence for all character columns.</td>
+ </tr>
+ <tr>
+ <td>COLUMN_NAME</td>
+ <td>Names of columns. This is only included if
+ binlog_row_metadata=FULL.</td>
+ <td>A sequence of column names. For each column name, 1 byte for
+ the string length in bytes is followed by a string without null
+ terminator.</td>
+ </tr>
+ <tr>
+ <td>SET_STR_VALUE</td>
+ <td>The string values of SET columns. This is only included if
+ binlog_row_metadata=FULL.</td>
+ <td>For each SET column, a pack_length representing the value
+ count is followed by a sequence of length and string pairs. length
+ is the byte count in pack_length format. The string has no null
+ terminator.</td>
+ </tr>
+ <tr>
+ <td>ENUM_STR_VALUE</td>
+ <td>The string values is ENUM columns. This is only included
+ if binlog_row_metadata=FULL.</td>
+ <td>The format is the same as SET_STR_VALUE.</td>
+ </tr>
+ <tr>
+ <td>GEOMETRY_TYPE</td>
+ <td>The real type of geometry columns. This is only included
+ if binlog_row_metadata=FULL.</td>
+ <td>A sequence of real type of geometry columns are stored in pack_length
+ format. </td>
+ </tr>
+ <tr>
+ <td>SIMPLE_PRIMARY_KEY</td>
+ <td>The primary key without any prefix. This is only included
+ if binlog_row_metadata=FULL and there is a primary key where every
+ key part covers an entire column.</td>
+ <td>A sequence of column indexes. The indexes are stored in pack_length
+ format.</td>
+ </tr>
+ <tr>
+ <td>PRIMARY_KEY_WITH_PREFIX</td>
+ <td>The primary key with some prefix. It doesn't appear together with
+ SIMPLE_PRIMARY_KEY. This is only included if
+ binlog_row_metadata=FULL and there is a primary key where some key
+ part covers a prefix of the column.</td>
+ <td>A sequence of column index and prefix length pairs. Both
+ column index and prefix length are in pack_length format. Prefix length
+ 0 means that the whole column value is used.</td>
+ </tr>
+ <tr>
+ <td>ENUM_AND_SET_DEFAULT_CHARSET</td>
+ <td>Charsets of ENUM and SET columns. It has the same layout as
+ DEFAULT_CHARSET. If there are SET or ENUM columns and
+ binlog_row_metadata=FULL, exactly one of
+ ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET
+ appears (the encoder chooses the representation that uses the
+ least amount of space). Otherwise, none of them appears.</td>
+ <td>The same format as for DEFAULT_CHARSET, except it counts ENUM
+ and SET columns rather than character columns.</td>
+ </tr>
+ <tr>
+ <td>ENUM_AND_SET_COLUMN_CHARSET</td>
+ <td>Charsets of ENUM and SET columns. It has the same layout as
+ COLUMN_CHARSET. If there are SET or ENUM columns and
+ binlog_row_metadata=FULL, exactly one of
+ ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET
+ appears (the encoder chooses the representation that uses the
+ least amount of space). Otherwise, none of them appears.</td>
+ <td>The same format as for COLUMN_CHARSET, except it counts ENUM
+ and SET columns rather than character columns.</td>
+ </tr>
+ </table>
*/
class Table_map_log_event : public Log_event
{
@@ -4313,6 +4645,124 @@ public:
};
typedef uint16 flag_set;
+ /**
+ DEFAULT_CHARSET and COLUMN_CHARSET don't appear together, and
+ ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET don't
+ appear together. They are just alternative ways to pack character
+ set information. When binlogging, it logs character sets in the
+ way that occupies least storage.
+
+ SIMPLE_PRIMARY_KEY and PRIMARY_KEY_WITH_PREFIX don't appear together.
+ SIMPLE_PRIMARY_KEY is for the primary keys which only use whole values of
+ pk columns. PRIMARY_KEY_WITH_PREFIX is
+ for the primary keys which just use part value of pk columns.
+ */
+ enum Optional_metadata_field_type
+ {
+ SIGNEDNESS = 1, // UNSIGNED flag of numeric columns
+ DEFAULT_CHARSET, /* Character set of string columns, optimized to
+ minimize space when many columns have the
+ same charset. */
+ COLUMN_CHARSET, /* Character set of string columns, optimized to
+ minimize space when columns have many
+ different charsets. */
+ COLUMN_NAME,
+ SET_STR_VALUE, // String value of SET columns
+ ENUM_STR_VALUE, // String value of ENUM columns
+ GEOMETRY_TYPE, // Real type of geometry columns
+ SIMPLE_PRIMARY_KEY, // Primary key without prefix
+ PRIMARY_KEY_WITH_PREFIX, // Primary key with prefix
+ ENUM_AND_SET_DEFAULT_CHARSET, /* Character set of enum and set
+ columns, optimized to minimize
+ space when many columns have the
+ same charset. */
+ ENUM_AND_SET_COLUMN_CHARSET, /* Character set of enum and set
+ columns, optimized to minimize
+ space when many columns have the
+ same charset. */
+ };
+ /**
+ Metadata_fields organizes m_optional_metadata into a structured format which
+ is easy to access.
+ */
+ // Values for binlog_row_metadata sysvar
+ enum enum_binlog_row_metadata
+ {
+ BINLOG_ROW_METADATA_NO_LOG= 0,
+ BINLOG_ROW_METADATA_MINIMAL= 1,
+ BINLOG_ROW_METADATA_FULL= 2
+ };
+ struct Optional_metadata_fields
+ {
+ typedef std::pair<unsigned int, unsigned int> uint_pair;
+ typedef std::vector<std::string> str_vector;
+
+ struct Default_charset
+ {
+ Default_charset() : default_charset(0) {}
+ bool empty() const { return default_charset == 0; }
+
+ // Default charset for the columns which are not in charset_pairs.
+ unsigned int default_charset;
+
+ /* The uint_pair means <column index, column charset number>. */
+ std::vector<uint_pair> charset_pairs;
+ };
+
+ // Contents of DEFAULT_CHARSET field is converted into Default_charset.
+ Default_charset m_default_charset;
+ // Contents of ENUM_AND_SET_DEFAULT_CHARSET are converted into
+ // Default_charset.
+ Default_charset m_enum_and_set_default_charset;
+ std::vector<bool> m_signedness;
+ // Character set number of every string column
+ std::vector<unsigned int> m_column_charset;
+ // Character set number of every ENUM or SET column.
+ std::vector<unsigned int> m_enum_and_set_column_charset;
+ std::vector<std::string> m_column_name;
+ // each str_vector stores values of one enum/set column
+ std::vector<str_vector> m_enum_str_value;
+ std::vector<str_vector> m_set_str_value;
+ std::vector<unsigned int> m_geometry_type;
+ /*
+ The uint_pair means <column index, prefix length>. Prefix length is 0 if
+ whole column value is used.
+ */
+ std::vector<uint_pair> m_primary_key;
+
+ /*
+ It parses m_optional_metadata and populates into above variables.
+
+ @param[in] optional_metadata points to the begin of optional metadata
+ fields in table_map_event.
+ @param[in] optional_metadata_len length of optional_metadata field.
+ */
+ Optional_metadata_fields(unsigned char* optional_metadata,
+ unsigned int optional_metadata_len);
+ };
+
+ /**
+ Print column metadata. Its format looks like:
+ # Columns(colume_name type, colume_name type, ...)
+ if colume_name field is not logged into table_map_log_event, then
+ only type is printed.
+
+ @@param[out] file the place where colume metadata is printed
+ @@param[in] The metadata extracted from optional metadata fields
+ */
+ void print_columns(IO_CACHE *file,
+ const Optional_metadata_fields &fields);
+ /**
+ Print primary information. Its format looks like:
+ # Primary Key(colume_name, column_name(prifix), ...)
+ if colume_name field is not logged into table_map_log_event, then
+ colume index is printed.
+
+ @@param[out] file the place where primary key is printed
+ @@param[in] The metadata extracted from optional metadata fields
+ */
+ void print_primary_key(IO_CACHE *file,
+ const Optional_metadata_fields &fields);
/* Special constants representing sets of flags */
enum
@@ -4379,6 +4829,51 @@ private:
#ifdef MYSQL_SERVER
TABLE *m_table;
+ Binlog_type_info *binlog_type_info_array;
+
+
+ // Metadata fields buffer
+ StringBuffer<1024> m_metadata_buf;
+
+ /**
+ Capture the optional metadata fields which should be logged into
+ table_map_log_event and serialize them into m_metadata_buf.
+ */
+ void init_metadata_fields();
+ bool init_signedness_field();
+ /**
+ Capture and serialize character sets. Character sets for
+ character columns (TEXT etc) and character sets for ENUM and SET
+ columns are stored in different metadata fields. The reason is
+ that TEXT character sets are included even when
+ binlog_row_metadata=MINIMAL, whereas ENUM and SET character sets
+ are included only when binlog_row_metadata=FULL.
+
+ @param include_type Predicate to determine if a given Field object
+ is to be included in the metadata field.
+
+ @param default_charset_type Type code when storing in "default
+ charset" format. (See comment above Table_maps_log_event in
+ libbinlogevents/include/rows_event.h)
+
+ @param column_charset_type Type code when storing in "column
+ charset" format. (See comment above Table_maps_log_event in
+ libbinlogevents/include/rows_event.h)
+ */
+ bool init_charset_field(bool(* include_type)(Binlog_type_info *, Field *),
+ Optional_metadata_field_type default_charset_type,
+ Optional_metadata_field_type column_charset_type);
+ bool init_column_name_field();
+ bool init_set_str_value_field();
+ bool init_enum_str_value_field();
+ bool init_geometry_type_field();
+ bool init_primary_key_field();
+#endif
+
+#ifdef MYSQL_CLIENT
+ class Charset_iterator;
+ class Default_charset_iterator;
+ class Column_charset_iterator;
#endif
char const *m_dbnam;
size_t m_dblen;
@@ -4400,6 +4895,8 @@ private:
ulong m_field_metadata_size;
uchar *m_null_bits;
uchar *m_meta_memory;
+ unsigned int m_optional_metadata_len;
+ unsigned char *m_optional_metadata;
};
@@ -4591,6 +5088,12 @@ public:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual uint8 get_trg_event_map()= 0;
+
+ inline bool do_invoke_trigger()
+ {
+ return (slave_run_triggers_for_rbr && !master_had_triggers) ||
+ slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_ENFORCE;
+ }
#endif
protected:
@@ -5073,11 +5576,12 @@ public:
Incident_log_event(THD *thd_arg, Incident incident, const LEX_CSTRING *msg)
: Log_event(thd_arg, 0, FALSE), m_incident(incident)
{
+ extern PSI_memory_key key_memory_Incident_log_event_message;
DBUG_ENTER("Incident_log_event::Incident_log_event");
DBUG_PRINT("enter", ("m_incident: %d", m_incident));
m_message.length= 0;
- if (unlikely(!(m_message.str= (char*) my_malloc(msg->length+1,
- MYF(MY_WME)))))
+ if (!(m_message.str= (char*) my_malloc(key_memory_Incident_log_event_message,
+ msg->length + 1, MYF(MY_WME))))
{
/* Mark this event invalid */
m_incident= INCIDENT_NONE;
@@ -5274,5 +5778,4 @@ int row_log_event_uncompress(const Format_description_log_event *description_eve
const char *src, ulong src_len, char* buf, ulong buf_size, bool* is_malloc,
char **dst, ulong *newlen);
-
#endif /* _log_event_h */
diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc
new file mode 100644
index 00000000000..0480a8fa59f
--- /dev/null
+++ b/sql/log_event_client.cc
@@ -0,0 +1,3932 @@
+/*
+ Copyright (c) 2000, 2018, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2020, 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
+ 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 MYSQL_CLIENT
+#error MYSQL_CLIENT must be defined here
+#endif
+
+#ifdef MYSQL_SERVER
+#error MYSQL_SERVER must not be defined here
+#endif
+
+
+static bool pretty_print_str(IO_CACHE* cache, const char* str,
+ size_t len, bool identifier)
+{
+ const char* end = str + len;
+ if (my_b_write_byte(cache, identifier ? '`' : '\''))
+ goto err;
+
+ while (str < end)
+ {
+ char c;
+ int error;
+
+ switch ((c=*str++)) {
+ case '\n': error= my_b_write(cache, (uchar*)"\\n", 2); break;
+ case '\r': error= my_b_write(cache, (uchar*)"\\r", 2); break;
+ case '\\': error= my_b_write(cache, (uchar*)"\\\\", 2); break;
+ case '\b': error= my_b_write(cache, (uchar*)"\\b", 2); break;
+ case '\t': error= my_b_write(cache, (uchar*)"\\t", 2); break;
+ case '\'': error= my_b_write(cache, (uchar*)"\\'", 2); break;
+ case 0 : error= my_b_write(cache, (uchar*)"\\0", 2); break;
+ default:
+ error= my_b_write_byte(cache, c);
+ break;
+ }
+ if (unlikely(error))
+ goto err;
+ }
+ return my_b_write_byte(cache, identifier ? '`' : '\'');
+
+err:
+ return 1;
+}
+
+/**
+ Print src as an string enclosed with "'"
+
+ @param[out] cache IO_CACHE where the string will be printed.
+ @param[in] str the string will be printed.
+ @param[in] len length of the string.
+*/
+static inline bool pretty_print_str(IO_CACHE* cache, const char* str,
+ size_t len)
+{
+ return pretty_print_str(cache, str, len, false);
+}
+
+/**
+ Print src as an identifier enclosed with "`"
+
+ @param[out] cache IO_CACHE where the identifier will be printed.
+ @param[in] str the string will be printed.
+ @param[in] len length of the string.
+ */
+static inline bool pretty_print_identifier(IO_CACHE* cache, const char* str,
+ size_t len)
+{
+ return pretty_print_str(cache, str, len, true);
+}
+
+
+
+/**
+ Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
+ commands just before it prints a query.
+*/
+
+static bool print_set_option(IO_CACHE* file, uint32 bits_changed,
+ uint32 option, uint32 flags, const char* name,
+ bool* need_comma)
+{
+ if (bits_changed & option)
+ {
+ if (*need_comma)
+ if (my_b_write(file, (uchar*)", ", 2))
+ goto err;
+ if (my_b_printf(file, "%s=%d", name, MY_TEST(flags & option)))
+ goto err;
+ *need_comma= 1;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+
+static bool 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
+ size_t emit_buf_written;
+
+ if (my_b_printf(file,
+ "# "
+ "|Timestamp "
+ "|Type "
+ "|Master ID "
+ "|Size "
+ "|Master Pos "
+ "|Flags\n"))
+ goto err;
+ 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));
+ if (my_b_write(file, reinterpret_cast<uchar*>(emit_buf), emit_buf_written) ||
+ my_b_write(file, (uchar*)"#\n", 2))
+ goto err;
+
+ return 0;
+err:
+ return 1;
+}
+
+
+/*
+ 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 bool 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 0; // ok, nothing to do
+
+ 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]= ' ';
+ if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
+ sizeof(emit_buffer) - 1))
+ goto err;
+ 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)));
+ if (my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
+ c - emit_buffer))
+ goto err;
+ }
+ if (my_b_write(file, (uchar*)"#\n", 2))
+ goto err;
+
+ return 0;
+err:
+ return 1;
+}
+
+static inline bool is_numeric_type(uint type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+static inline bool is_character_type(uint type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BLOB:
+ // Base class is blob for geom type
+ case MYSQL_TYPE_GEOMETRY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool is_enum_or_set_type(uint type) {
+ return type == MYSQL_TYPE_ENUM || type == MYSQL_TYPE_SET;
+}
+
+
+/*
+ Log_event::print_header()
+*/
+
+bool Log_event::print_header(IO_CACHE* file,
+ PRINT_EVENT_INFO* print_event_info,
+ bool is_more __attribute__((unused)))
+{
+ char llbuff[22];
+ my_off_t hexdump_from= print_event_info->hexdump_from;
+ DBUG_ENTER("Log_event::print_header");
+
+ if (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)))
+ goto err;
+
+ /* print the checksum */
+
+ if (checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ {
+ char checksum_buf[BINLOG_CHECKSUM_LEN * 2 + 4]; // to fit to "%p "
+ size_t const bytes_written=
+ my_snprintf(checksum_buf, sizeof(checksum_buf), "0x%08x ", crc);
+ if (my_b_printf(file, "%s ", get_type(&binlog_checksum_typelib,
+ checksum_alg)) ||
+ my_b_printf(file, checksum_buf, bytes_written))
+ goto err;
+ }
+
+ /* mysqlbinlog --hexdump */
+ if (print_event_info->hexdump_from)
+ {
+ my_b_write_byte(file, '\n');
+ uchar *ptr= (uchar*)temp_buf;
+ my_off_t size= uint4korr(ptr + EVENT_LEN_OFFSET);
+ my_off_t hdr_len= get_header_len(print_event_info->common_header_len);
+
+ size-= hdr_len;
+
+ if (my_b_printf(file, "# Position\n"))
+ goto err;
+
+ /* Write the header, nicely formatted by field. */
+ if (hexdump_minimal_header_to_io_cache(file, hexdump_from, ptr))
+ goto err;
+
+ ptr+= hdr_len;
+ hexdump_from+= hdr_len;
+
+ /* Print the rest of the data, mimicking "hexdump -C" output. */
+ if (hexdump_data_to_io_cache(file, hexdump_from, ptr, size))
+ goto err;
+
+ /*
+ Prefix the next line so that the output from print_helper()
+ will appear as a comment.
+ */
+ if (my_b_write(file, (uchar*)"# Event: ", 9))
+ goto err;
+ }
+
+ DBUG_RETURN(0);
+
+err:
+ DBUG_RETURN(1);
+}
+
+
+/**
+ Prints a quoted string to io cache.
+ Control characters are displayed as hex sequence, e.g. \x00
+ Single-quote and backslash characters are escaped with a \
+
+ @param[in] file IO cache
+ @param[in] prt Pointer to string
+ @param[in] length String length
+*/
+
+static void
+my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length)
+{
+ const uchar *s;
+ my_b_write_byte(file, '\'');
+ for (s= ptr; length > 0 ; s++, length--)
+ {
+ if (*s > 0x1F)
+ my_b_write_byte(file, *s);
+ else if (*s == '\'')
+ my_b_write(file, (uchar*)"\\'", 2);
+ else if (*s == '\\')
+ my_b_write(file, (uchar*)"\\\\", 2);
+ else
+ {
+ uchar hex[10];
+ size_t len= my_snprintf((char*) hex, sizeof(hex), "%s%02x", "\\x", *s);
+ my_b_write(file, hex, len);
+ }
+ }
+ my_b_write_byte(file, '\'');
+}
+
+
+/**
+ Prints a bit string to io cache in format b'1010'.
+
+ @param[in] file IO cache
+ @param[in] ptr Pointer to string
+ @param[in] nbits Number of bits
+*/
+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_write(file, (uchar*)"b'", 2);
+ for (bitnum= skip_bits ; bitnum < nbits8; bitnum++)
+ {
+ int is_set= (ptr[(bitnum) / 8] >> (7 - bitnum % 8)) & 0x01;
+ my_b_write_byte(file, (is_set ? '1' : '0'));
+ }
+ my_b_write_byte(file, '\'');
+}
+
+
+/**
+ Prints a packed string to io cache.
+ The string consists of length packed to 1 or 2 bytes,
+ followed by string data itself.
+
+ @param[in] file IO cache
+ @param[in] ptr Pointer to string
+ @param[in] length String size
+
+ @retval - number of bytes scanned.
+*/
+static size_t
+my_b_write_quoted_with_length(IO_CACHE *file, const uchar *ptr, uint length)
+{
+ if (length < 256)
+ {
+ length= *ptr;
+ my_b_write_quoted(file, ptr + 1, length);
+ return length + 1;
+ }
+ else
+ {
+ length= uint2korr(ptr);
+ my_b_write_quoted(file, ptr + 2, length);
+ return length + 2;
+ }
+}
+
+
+/**
+ Prints a 32-bit number in both signed and unsigned representation
+
+ @param[in] file IO cache
+ @param[in] sl Signed number
+ @param[in] ul Unsigned number
+*/
+static bool
+my_b_write_sint32_and_uint32(IO_CACHE *file, int32 si, uint32 ui)
+{
+ bool res= my_b_printf(file, "%d", si);
+ if (si < 0)
+ if (my_b_printf(file, " (%u)", ui))
+ res= 1;
+ return res;
+}
+
+
+/**
+ Print a packed value of the given SQL type into IO cache
+
+ @param[in] file IO cache
+ @param[in] ptr Pointer to string
+ @param[in] type Column type
+ @param[in] meta Column meta information
+ @param[out] typestr SQL type string buffer (for verbose output)
+ @param[out] typestr_length Size of typestr
+
+ @retval - number of bytes scanned from ptr.
+ Except in case of NULL, in which case we return 1 to indicate ok
+*/
+
+static size_t
+log_event_print_value(IO_CACHE *file, PRINT_EVENT_INFO *print_event_info,
+ const uchar *ptr, uint type, uint meta,
+ char *typestr, size_t typestr_length)
+{
+ uint32 length= 0;
+
+ if (type == MYSQL_TYPE_STRING)
+ {
+ if (meta >= 256)
+ {
+ uint byte0= meta >> 8;
+ uint byte1= meta & 0xFF;
+
+ if ((byte0 & 0x30) != 0x30)
+ {
+ /* a long CHAR() field: see #37426 */
+ length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
+ type= byte0 | 0x30;
+ }
+ else
+ length = meta & 0xFF;
+ }
+ else
+ length= meta;
+ }
+
+ switch (type) {
+ case MYSQL_TYPE_LONG:
+ {
+ strmake(typestr, "INT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ int32 si= sint4korr(ptr);
+ uint32 ui= uint4korr(ptr);
+ my_b_write_sint32_and_uint32(file, si, ui);
+ return 4;
+ }
+
+ case MYSQL_TYPE_TINY:
+ {
+ strmake(typestr, "TINYINT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ my_b_write_sint32_and_uint32(file, (int) (signed char) *ptr,
+ (uint) (unsigned char) *ptr);
+ return 1;
+ }
+
+ case MYSQL_TYPE_SHORT:
+ {
+ strmake(typestr, "SHORTINT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ int32 si= (int32) sint2korr(ptr);
+ uint32 ui= (uint32) uint2korr(ptr);
+ my_b_write_sint32_and_uint32(file, si, ui);
+ return 2;
+ }
+
+ case MYSQL_TYPE_INT24:
+ {
+ strmake(typestr, "MEDIUMINT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ int32 si= sint3korr(ptr);
+ uint32 ui= uint3korr(ptr);
+ my_b_write_sint32_and_uint32(file, si, ui);
+ return 3;
+ }
+
+ case MYSQL_TYPE_LONGLONG:
+ {
+ strmake(typestr, "LONGINT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ char tmp[64];
+ size_t length;
+ longlong si= sint8korr(ptr);
+ length= (longlong10_to_str(si, tmp, -10) - tmp);
+ my_b_write(file, (uchar*)tmp, length);
+ if (si < 0)
+ {
+ ulonglong ui= uint8korr(ptr);
+ longlong10_to_str((longlong) ui, tmp, 10);
+ my_b_printf(file, " (%s)", tmp);
+ }
+ return 8;
+ }
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ uint precision= meta >> 8;
+ uint decimals= meta & 0xFF;
+ my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)",
+ precision, decimals);
+ if (!ptr)
+ goto return_null;
+
+ uint bin_size= my_decimal_get_binary_size(precision, decimals);
+ my_decimal dec((const uchar *) ptr, precision, decimals);
+ int length= DECIMAL_MAX_STR_LENGTH;
+ char buff[DECIMAL_MAX_STR_LENGTH + 1];
+ decimal2string(&dec, buff, &length, 0, 0, 0);
+ my_b_write(file, (uchar*)buff, length);
+ return bin_size;
+ }
+
+ case MYSQL_TYPE_FLOAT:
+ {
+ strmake(typestr, "FLOAT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ float fl;
+ float4get(fl, ptr);
+ char tmp[320];
+ sprintf(tmp, "%-20g", (double) fl);
+ my_b_printf(file, "%s", tmp); /* my_snprintf doesn't support %-20g */
+ return 4;
+ }
+
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double dbl;
+ strmake(typestr, "DOUBLE", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ float8get(dbl, ptr);
+ char tmp[320];
+ sprintf(tmp, "%-.20g", dbl); /* strmake doesn't support %-20g */
+ my_b_printf(file, tmp, "%s");
+ return 8;
+ }
+
+ case MYSQL_TYPE_BIT:
+ {
+ /* Meta-data: bit_len, bytes_in_rec, 2 bytes */
+ uint nbits= ((meta >> 8) * 8) + (meta & 0xFF);
+ my_snprintf(typestr, typestr_length, "BIT(%d)", nbits);
+ if (!ptr)
+ goto return_null;
+
+ length= (nbits + 7) / 8;
+ my_b_write_bit(file, ptr, nbits);
+ return length;
+ }
+
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ strmake(typestr, "TIMESTAMP", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ uint32 i32= uint4korr(ptr);
+ my_b_printf(file, "%d", i32);
+ return 4;
+ }
+
+ case MYSQL_TYPE_TIMESTAMP2:
+ {
+ my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", meta);
+ if (!ptr)
+ goto return_null;
+
+ 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, (uchar*)buf, buflen);
+ return my_timestamp_binary_length(meta);
+ }
+
+ case MYSQL_TYPE_DATETIME:
+ {
+ strmake(typestr, "DATETIME", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ ulong d, t;
+ uint64 i64= uint8korr(ptr); /* YYYYMMDDhhmmss */
+ d= (ulong) (i64 / 1000000);
+ t= (ulong) (i64 % 1000000);
+
+ 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);
+ return 8;
+ }
+
+ case MYSQL_TYPE_DATETIME2:
+ {
+ my_snprintf(typestr, typestr_length, "DATETIME(%d)", meta);
+ if (!ptr)
+ goto return_null;
+
+ 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);
+ return my_datetime_binary_length(meta);
+ }
+
+ case MYSQL_TYPE_TIME:
+ {
+ strmake(typestr, "TIME", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ 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);
+ return 3;
+ }
+
+ case MYSQL_TYPE_TIME2:
+ {
+ my_snprintf(typestr, typestr_length, "TIME(%d)", meta);
+ if (!ptr)
+ goto return_null;
+
+ 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);
+ return my_time_binary_length(meta);
+ }
+
+ case MYSQL_TYPE_NEWDATE:
+ {
+ strmake(typestr, "DATE", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ uint32 tmp= uint3korr(ptr);
+ int part;
+ char buf[11];
+ char *pos= &buf[10]; // start from '\0' to the beginning
+
+ /* Copied from field.cc */
+ *pos--=0; // End NULL
+ part=(int) (tmp & 31);
+ *pos--= (char) ('0'+part%10);
+ *pos--= (char) ('0'+part/10);
+ *pos--= ':';
+ part=(int) (tmp >> 5 & 15);
+ *pos--= (char) ('0'+part%10);
+ *pos--= (char) ('0'+part/10);
+ *pos--= ':';
+ part=(int) (tmp >> 9);
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos--= (char) ('0'+part%10); part/=10;
+ *pos= (char) ('0'+part);
+ my_b_printf(file , "'%s'", buf);
+ return 3;
+ }
+
+ case MYSQL_TYPE_DATE:
+ {
+ strmake(typestr, "DATE", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ uint i32= uint3korr(ptr);
+ my_b_printf(file , "'%04d:%02d:%02d'",
+ (int)(i32 / (16L * 32L)), (int)(i32 / 32L % 16L),
+ (int)(i32 % 32L));
+ return 3;
+ }
+
+ case MYSQL_TYPE_YEAR:
+ {
+ strmake(typestr, "YEAR", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ uint32 i32= *ptr;
+ my_b_printf(file, "%04d", i32+ 1900);
+ return 1;
+ }
+
+ case MYSQL_TYPE_ENUM:
+ switch (meta & 0xFF) {
+ case 1:
+ strmake(typestr, "ENUM(1 byte)", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ my_b_printf(file, "%d", (int) *ptr);
+ return 1;
+ case 2:
+ {
+ strmake(typestr, "ENUM(2 bytes)", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ int32 i32= uint2korr(ptr);
+ my_b_printf(file, "%d", i32);
+ return 2;
+ }
+ default:
+ my_b_printf(file, "!! Unknown ENUM packlen=%d", meta & 0xFF);
+ return 0;
+ }
+ break;
+
+ case MYSQL_TYPE_SET:
+ my_snprintf(typestr, typestr_length, "SET(%d bytes)", meta & 0xFF);
+ if (!ptr)
+ goto return_null;
+
+ my_b_write_bit(file, ptr , (meta & 0xFF) * 8);
+ return meta & 0xFF;
+
+ case MYSQL_TYPE_BLOB:
+ switch (meta) {
+ case 1:
+ strmake(typestr, "TINYBLOB/TINYTEXT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ length= *ptr;
+ my_b_write_quoted(file, ptr + 1, length);
+ return length + 1;
+ case 2:
+ strmake(typestr, "BLOB/TEXT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ length= uint2korr(ptr);
+ my_b_write_quoted(file, ptr + 2, length);
+ return length + 2;
+ case 3:
+ strmake(typestr, "MEDIUMBLOB/MEDIUMTEXT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ length= uint3korr(ptr);
+ my_b_write_quoted(file, ptr + 3, length);
+ return length + 3;
+ case 4:
+ strmake(typestr, "LONGBLOB/LONGTEXT", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ length= uint4korr(ptr);
+ my_b_write_quoted(file, ptr + 4, length);
+ return length + 4;
+ default:
+ my_b_printf(file, "!! Unknown BLOB packlen=%d", length);
+ return 0;
+ }
+
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ length= meta;
+ my_snprintf(typestr, typestr_length, "VARSTRING(%d)", length);
+ if (!ptr)
+ goto return_null;
+
+ return my_b_write_quoted_with_length(file, ptr, length);
+
+ case MYSQL_TYPE_STRING:
+ my_snprintf(typestr, typestr_length, "STRING(%d)", length);
+ if (!ptr)
+ goto return_null;
+
+ return my_b_write_quoted_with_length(file, ptr, length);
+
+ case MYSQL_TYPE_DECIMAL:
+ print_event_info->flush_for_error();
+ fprintf(stderr, "\nError: Found Old DECIMAL (mysql-4.1 or earlier). "
+ "Not enough metadata to display the value.\n");
+ break;
+
+ case MYSQL_TYPE_GEOMETRY:
+ strmake(typestr, "GEOMETRY", typestr_length);
+ if (!ptr)
+ goto return_null;
+
+ length= uint4korr(ptr);
+ my_b_write_quoted(file, ptr + meta, length);
+ return length + meta;
+
+ default:
+ print_event_info->flush_for_error();
+ fprintf(stderr,
+ "\nError: Don't know how to handle column type: %d meta: %d (%04x)\n",
+ type, meta, meta);
+ break;
+ }
+ *typestr= 0;
+ return 0;
+
+return_null:
+ return my_b_write(file, (uchar*) "NULL", 4) ? 0 : 1;
+}
+
+
+/**
+ Print a packed row into IO cache
+
+ @param[in] file IO cache
+ @param[in] td Table definition
+ @param[in] print_event_into Print parameters
+ @param[in] cols_bitmap Column bitmaps.
+ @param[in] value Pointer to packed row
+ @param[in] prefix Row's SQL clause ("SET", "WHERE", etc)
+
+ @retval 0 error
+ # number of bytes scanned.
+*/
+
+
+size_t
+Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
+ PRINT_EVENT_INFO *print_event_info,
+ MY_BITMAP *cols_bitmap,
+ const uchar *value, const uchar *prefix,
+ const my_bool no_fill_output)
+{
+ const uchar *value0= value;
+ const uchar *null_bits= value;
+ uint null_bit_index= 0;
+ char typestr[64]= "";
+
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ /* Storing the review SQL */
+ IO_CACHE *review_sql= &print_event_info->review_sql_cache;
+ LEX_STRING review_str;
+#endif
+
+ /*
+ Skip metadata bytes which gives the information about nullabity of master
+ columns. Master writes one bit for each affected column.
+ */
+
+ value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
+
+ if (!no_fill_output)
+ if (my_b_printf(file, "%s", prefix))
+ goto err;
+
+ for (uint i= 0; i < (uint)td->size(); i ++)
+ {
+ size_t size;
+ int is_null= (null_bits[null_bit_index / 8]
+ >> (null_bit_index % 8)) & 0x01;
+
+ if (bitmap_is_set(cols_bitmap, i) == 0)
+ continue;
+
+ if (!no_fill_output)
+ if (my_b_printf(file, "### @%d=", static_cast<int>(i + 1)))
+ goto err;
+
+ if (!is_null)
+ {
+ size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
+ if (value + fsize > m_rows_end)
+ {
+ if (!no_fill_output)
+ if (my_b_printf(file, "***Corrupted replication event was detected."
+ " Not printing the value***\n"))
+ goto err;
+ value+= fsize;
+ return 0;
+ }
+ }
+
+ if (!no_fill_output)
+ {
+ size= log_event_print_value(file, print_event_info, is_null? NULL: value,
+ td->type(i), td->field_metadata(i),
+ typestr, sizeof(typestr));
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ if (need_flashback_review)
+ {
+ String tmp_str, hex_str;
+ IO_CACHE tmp_cache;
+
+ // Using a tmp IO_CACHE to get the value output
+ open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
+ size= log_event_print_value(&tmp_cache, print_event_info,
+ is_null ? NULL: value,
+ td->type(i), td->field_metadata(i),
+ typestr, sizeof(typestr));
+ error= copy_event_cache_to_string_and_reinit(&tmp_cache, &review_str);
+ close_cached_file(&tmp_cache);
+ if (unlikely(error))
+ return 0;
+
+ switch (td->type(i)) // Converting a string to HEX format
+ {
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_BLOB:
+ // Avoid write_pos changed to a new area
+ // tmp_str.free();
+ tmp_str.append(review_str.str + 1, review_str.length - 2); // Removing quotation marks
+ if (hex_str.alloc(tmp_str.length()*2+1)) // If out of memory
+ {
+ fprintf(stderr, "\nError: Out of memory. "
+ "Could not print correct binlog event.\n");
+ exit(1);
+ }
+ octet2hex((char*) hex_str.ptr(), tmp_str.ptr(), tmp_str.length());
+ if (my_b_printf(review_sql, ", UNHEX('%s')", hex_str.ptr()))
+ goto err;
+ break;
+ default:
+ tmp_str.free();
+ if (tmp_str.append(review_str.str, review_str.length) ||
+ my_b_printf(review_sql, ", %s", tmp_str.ptr()))
+ goto err;
+ break;
+ }
+ my_free(revieww_str.str);
+ }
+#endif
+ }
+ else
+ {
+ IO_CACHE tmp_cache;
+ open_cached_file(&tmp_cache, NULL, NULL, 0, MYF(MY_WME | MY_NABP));
+ size= log_event_print_value(&tmp_cache, print_event_info,
+ is_null ? NULL: value,
+ td->type(i), td->field_metadata(i),
+ typestr, sizeof(typestr));
+ close_cached_file(&tmp_cache);
+ }
+
+ if (!size)
+ goto err;
+
+ if (!is_null)
+ value+= size;
+
+ if (print_event_info->verbose > 1 && !no_fill_output)
+ {
+ if (my_b_write(file, (uchar*)" /* ", 4) ||
+ my_b_printf(file, "%s ", typestr) ||
+ my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
+ td->field_metadata(i),
+ td->maybe_null(i), is_null) ||
+ my_b_write(file, (uchar*)"*/", 2))
+ goto err;
+ }
+
+ if (!no_fill_output)
+ if (my_b_write_byte(file, '\n'))
+ goto err;
+
+ null_bit_index++;
+ }
+ return value - value0;
+
+err:
+ return 0;
+}
+
+
+/**
+ Exchange the SET part and WHERE part for the Update events.
+ Revert the operations order for the Write and Delete events.
+ And then revert the events order from the last one to the first one.
+
+ @param[in] print_event_info PRINT_EVENT_INFO
+ @param[in] rows_buff Packed event buff
+*/
+
+void Rows_log_event::change_to_flashback_event(PRINT_EVENT_INFO *print_event_info,
+ uchar *rows_buff, Log_event_type ev_type)
+{
+ Table_map_log_event *map;
+ table_def *td;
+ DYNAMIC_ARRAY rows_arr;
+ uchar *swap_buff1;
+ uchar *rows_pos= rows_buff + m_rows_before_size;
+
+ if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
+ !(td= map->create_table_def()))
+ return;
+
+ /* If the write rows event contained no values for the AI */
+ if (((get_general_type_code() == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
+ goto end;
+
+ (void) my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &rows_arr, sizeof(LEX_STRING), 8, 8, MYF(0));
+
+ for (uchar *value= m_rows_buf; value < m_rows_end; )
+ {
+ uchar *start_pos= value;
+ size_t length1= 0;
+ if (!(length1= print_verbose_one_row(NULL, td, print_event_info,
+ &m_cols, value,
+ (const uchar*) "", TRUE)))
+ {
+ fprintf(stderr, "\nError row length: %zu\n", length1);
+ exit(1);
+ }
+ value+= length1;
+
+ swap_buff1= (uchar *) my_malloc(PSI_NOT_INSTRUMENTED, length1, MYF(0));
+ if (!swap_buff1)
+ {
+ fprintf(stderr, "\nError: Out of memory. "
+ "Could not exchange to flashback event.\n");
+ exit(1);
+ }
+ memcpy(swap_buff1, start_pos, length1);
+
+ // For Update_event, we have the second part
+ size_t length2= 0;
+ if (ev_type == UPDATE_ROWS_EVENT ||
+ ev_type == UPDATE_ROWS_EVENT_V1)
+ {
+ if (!(length2= print_verbose_one_row(NULL, td, print_event_info,
+ &m_cols, value,
+ (const uchar*) "", TRUE)))
+ {
+ fprintf(stderr, "\nError row length: %zu\n", length2);
+ exit(1);
+ }
+ value+= length2;
+
+ void *swap_buff2= my_malloc(PSI_NOT_INSTRUMENTED, length2, MYF(0));
+ if (!swap_buff2)
+ {
+ fprintf(stderr, "\nError: Out of memory. "
+ "Could not exchange to flashback event.\n");
+ exit(1);
+ }
+ memcpy(swap_buff2, start_pos + length1, length2); // WHERE part
+
+ /* Swap SET and WHERE part */
+ memcpy(start_pos, swap_buff2, length2);
+ memcpy(start_pos + length2, swap_buff1, length1);
+ my_free(swap_buff2);
+ }
+
+ my_free(swap_buff1);
+
+ /* Copying one row into a buff, and pushing into the array */
+ LEX_STRING one_row;
+
+ one_row.length= length1 + length2;
+ one_row.str= (char *) my_malloc(PSI_NOT_INSTRUMENTED, one_row.length, MYF(0));
+ memcpy(one_row.str, start_pos, one_row.length);
+ if (one_row.str == NULL || push_dynamic(&rows_arr, (uchar *) &one_row))
+ {
+ fprintf(stderr, "\nError: Out of memory. "
+ "Could not push flashback event into array.\n");
+ exit(1);
+ }
+ }
+
+ /* Copying rows from the end to the begining into event */
+ for (uint i= rows_arr.elements; i > 0; --i)
+ {
+ LEX_STRING *one_row= dynamic_element(&rows_arr, i - 1, LEX_STRING*);
+
+ memcpy(rows_pos, (uchar *)one_row->str, one_row->length);
+ rows_pos+= one_row->length;
+ my_free(one_row->str);
+ }
+ delete_dynamic(&rows_arr);
+
+end:
+ delete td;
+}
+
+/**
+ Calc length of a packed value of the given SQL type
+
+ @param[in] ptr Pointer to string
+ @param[in] type Column type
+ @param[in] meta Column meta information
+
+ @retval - number of bytes scanned from ptr.
+ Except in case of NULL, in which case we return 1 to indicate ok
+*/
+
+static size_t calc_field_event_length(const uchar *ptr, uint type, uint meta)
+{
+ uint32 length= 0;
+
+ if (type == MYSQL_TYPE_STRING)
+ {
+ if (meta >= 256)
+ {
+ uint byte0= meta >> 8;
+ uint byte1= meta & 0xFF;
+
+ if ((byte0 & 0x30) != 0x30)
+ {
+ /* a long CHAR() field: see #37426 */
+ length= byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
+ type= byte0 | 0x30;
+ }
+ else
+ length = meta & 0xFF;
+ }
+ else
+ length= meta;
+ }
+
+ switch (type) {
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_TIMESTAMP:
+ return 4;
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_YEAR:
+ return 1;
+ case MYSQL_TYPE_SHORT:
+ return 2;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_DATE:
+ return 3;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_DATETIME:
+ return 8;
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ uint precision= meta >> 8;
+ uint decimals= meta & 0xFF;
+ uint bin_size= my_decimal_get_binary_size(precision, decimals);
+ return bin_size;
+ }
+ case MYSQL_TYPE_FLOAT:
+ return 4;
+ case MYSQL_TYPE_DOUBLE:
+ return 8;
+ case MYSQL_TYPE_BIT:
+ {
+ /* Meta-data: bit_len, bytes_in_rec, 2 bytes */
+ uint nbits= ((meta >> 8) * 8) + (meta & 0xFF);
+ length= (nbits + 7) / 8;
+ return length;
+ }
+ case MYSQL_TYPE_TIMESTAMP2:
+ return my_timestamp_binary_length(meta);
+ case MYSQL_TYPE_DATETIME2:
+ return my_datetime_binary_length(meta);
+ case MYSQL_TYPE_TIME2:
+ return my_time_binary_length(meta);
+ case MYSQL_TYPE_ENUM:
+ switch (meta & 0xFF) {
+ case 1:
+ case 2:
+ return (meta & 0xFF);
+ default:
+ /* Unknown ENUM packlen=%d", meta & 0xFF */
+ return 0;
+ }
+ break;
+ case MYSQL_TYPE_SET:
+ return meta & 0xFF;
+ case MYSQL_TYPE_BLOB:
+ switch (meta) {
+ default:
+ return 0;
+ case 1:
+ return *ptr + 1;
+ case 2:
+ return uint2korr(ptr) + 2;
+ case 3:
+ return uint3korr(ptr) + 3;
+ case 4:
+ return uint4korr(ptr) + 4;
+ }
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ length= meta;
+ /* fall through */
+ case MYSQL_TYPE_STRING:
+ if (length < 256)
+ return (uint) *ptr + 1;
+ return uint2korr(ptr) + 2;
+ case MYSQL_TYPE_DECIMAL:
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+size_t
+Rows_log_event::calc_row_event_length(table_def *td,
+ PRINT_EVENT_INFO *print_event_info,
+ MY_BITMAP *cols_bitmap,
+ const uchar *value)
+{
+ const uchar *value0= value;
+ const uchar *null_bits= value;
+ uint null_bit_index= 0;
+
+ /*
+ Skip metadata bytes which gives the information about nullabity of master
+ columns. Master writes one bit for each affected column.
+ */
+
+ value+= (bitmap_bits_set(cols_bitmap) + 7) / 8;
+
+ for (uint i= 0; i < (uint)td->size(); i ++)
+ {
+ int is_null;
+ is_null= (null_bits[null_bit_index / 8] >> (null_bit_index % 8)) & 0x01;
+
+ if (bitmap_is_set(cols_bitmap, i) == 0)
+ continue;
+
+ if (!is_null)
+ {
+ size_t size;
+ size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
+ if (value + fsize > m_rows_end)
+ {
+ /* Corrupted replication event was detected, skipping entry */
+ return 0;
+ }
+ if (!(size= calc_field_event_length(value, td->type(i),
+ td->field_metadata(i))))
+ return 0;
+ value+= size;
+ }
+ null_bit_index++;
+ }
+ return value - value0;
+}
+
+
+/**
+ Calculate how many rows there are in the event
+
+ @param[in] file IO cache
+ @param[in] print_event_into Print parameters
+*/
+
+void Rows_log_event::count_row_events(PRINT_EVENT_INFO *print_event_info)
+{
+ Table_map_log_event *map;
+ table_def *td;
+ uint row_events;
+ Log_event_type general_type_code= get_general_type_code();
+
+ switch (general_type_code) {
+ case WRITE_ROWS_EVENT:
+ case DELETE_ROWS_EVENT:
+ row_events= 1;
+ break;
+ case UPDATE_ROWS_EVENT:
+ row_events= 2;
+ break;
+ default:
+ DBUG_ASSERT(0); /* Not possible */
+ return;
+ }
+
+ if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
+ !(td= map->create_table_def()))
+ {
+ /* Row event for unknown table */
+ return;
+ }
+
+ for (const uchar *value= m_rows_buf; value < m_rows_end; )
+ {
+ size_t length;
+ print_event_info->row_events++;
+
+ /* Print the first image */
+ if (!(length= calc_row_event_length(td, print_event_info,
+ &m_cols, value)))
+ break;
+ value+= length;
+ DBUG_ASSERT(value <= m_rows_end);
+
+ /* Print the second image (for UPDATE only) */
+ if (row_events == 2)
+ {
+ if (!(length= calc_row_event_length(td, print_event_info,
+ &m_cols_ai, value)))
+ break;
+ value+= length;
+ DBUG_ASSERT(value <= m_rows_end);
+ }
+ }
+ delete td;
+}
+
+
+/**
+ Print a row event into IO cache in human readable form (in SQL format)
+
+ @param[in] file IO cache
+ @param[in] print_event_into Print parameters
+*/
+
+bool Rows_log_event::print_verbose(IO_CACHE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ Table_map_log_event *map;
+ table_def *td= 0;
+ const char *sql_command, *sql_clause1, *sql_clause2;
+ const char *sql_command_short __attribute__((unused));
+ Log_event_type general_type_code= get_general_type_code();
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ IO_CACHE *review_sql= &print_event_info->review_sql_cache;
+#endif
+
+ 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);
+
+ if (my_b_printf(file, "### Extra row data format: %u, len: %u :",
+ m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET],
+ extra_payload_len))
+ goto err;
+ 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);
+ if (my_b_printf(file, "%s", buff))
+ goto err;
+ }
+ if (my_b_printf(file, "\n"))
+ goto err;
+ }
+
+ switch (general_type_code) {
+ case WRITE_ROWS_EVENT:
+ sql_command= "INSERT INTO";
+ sql_clause1= "### SET\n";
+ sql_clause2= NULL;
+ sql_command_short= "I";
+ break;
+ case DELETE_ROWS_EVENT:
+ sql_command= "DELETE FROM";
+ sql_clause1= "### WHERE\n";
+ sql_clause2= NULL;
+ sql_command_short= "D";
+ break;
+ case UPDATE_ROWS_EVENT:
+ sql_command= "UPDATE";
+ sql_clause1= "### WHERE\n";
+ sql_clause2= "### SET\n";
+ sql_command_short= "U";
+ break;
+ default:
+ sql_command= sql_clause1= sql_clause2= NULL;
+ sql_command_short= "";
+ DBUG_ASSERT(0); /* Not possible */
+ }
+
+ if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
+ !(td= map->create_table_def()))
+ {
+ return (my_b_printf(file, "### Row event for unknown table #%lu",
+ (ulong) m_table_id));
+ }
+
+ /* If the write rows event contained no values for the AI */
+ if (((general_type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
+ {
+ if (my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n",
+ map->get_db_name(), map->get_table_name()))
+ goto err;
+ goto end;
+ }
+
+ for (const uchar *value= m_rows_buf; value < m_rows_end; )
+ {
+ size_t length;
+ print_event_info->row_events++;
+
+ if (my_b_printf(file, "### %s %`s.%`s\n",
+ sql_command,
+ map->get_db_name(), map->get_table_name()))
+ goto err;
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ if (need_flashback_review)
+ if (my_b_printf(review_sql, "\nINSERT INTO `%s`.`%s` VALUES ('%s'",
+ map->get_review_dbname(), map->get_review_tablename(),
+ sql_command_short))
+ goto err;
+#endif
+
+ /* Print the first image */
+ if (!(length= print_verbose_one_row(file, td, print_event_info,
+ &m_cols, value,
+ (const uchar*) sql_clause1)))
+ goto err;
+ value+= length;
+
+ /* Print the second image (for UPDATE only) */
+ if (sql_clause2)
+ {
+ if (!(length= print_verbose_one_row(file, td, print_event_info,
+ &m_cols_ai, value,
+ (const uchar*) sql_clause2)))
+ goto err;
+ value+= length;
+ }
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ else
+ {
+ if (need_flashback_review)
+ for (size_t i= 0; i < td->size(); i ++)
+ if (my_b_printf(review_sql, ", NULL"))
+ goto err;
+ }
+
+ if (need_flashback_review)
+ if (my_b_printf(review_sql, ")%s\n", print_event_info->delimiter))
+ goto err;
+#endif
+ }
+
+end:
+ delete td;
+ return 0;
+err:
+ delete td;
+ return 1;
+}
+
+void free_table_map_log_event(Table_map_log_event *event)
+{
+ delete event;
+}
+
+/**
+ Encode the event, optionally per 'do_print_encoded' arg store the
+ result into the argument cache; optionally per event_info's
+ 'verbose' print into the cache a verbose representation of the event.
+ Note, no extra wrapping is done to the being io-cached data, like
+ to producing a BINLOG query. It's left for a routine that extracts from
+ the cache.
+
+ @param file pointer to IO_CACHE
+ @param print_event_info pointer to print_event_info specializing
+ what out of and how to print the event
+ @param do_print_encoded whether to store base64-encoded event
+ into @file.
+*/
+bool Log_event::print_base64(IO_CACHE* file,
+ PRINT_EVENT_INFO* print_event_info,
+ bool do_print_encoded)
+{
+ uchar *ptr= (uchar *)temp_buf;
+ uint32 size= uint4korr(ptr + EVENT_LEN_OFFSET);
+ DBUG_ENTER("Log_event::print_base64");
+
+ if (is_flashback)
+ {
+ uint tmp_size= size;
+ Rows_log_event *ev= NULL;
+ Log_event_type ev_type = (enum Log_event_type) ptr[EVENT_TYPE_OFFSET];
+ if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
+ checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
+ tmp_size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
+ switch (ev_type) {
+ case WRITE_ROWS_EVENT:
+ ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT;
+ ev= new Delete_rows_log_event((const char*) ptr, tmp_size,
+ glob_description_event);
+ ev->change_to_flashback_event(print_event_info, ptr, ev_type);
+ break;
+ case WRITE_ROWS_EVENT_V1:
+ ptr[EVENT_TYPE_OFFSET]= DELETE_ROWS_EVENT_V1;
+ ev= new Delete_rows_log_event((const char*) ptr, tmp_size,
+ glob_description_event);
+ ev->change_to_flashback_event(print_event_info, ptr, ev_type);
+ break;
+ case DELETE_ROWS_EVENT:
+ ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT;
+ ev= new Write_rows_log_event((const char*) ptr, tmp_size,
+ glob_description_event);
+ ev->change_to_flashback_event(print_event_info, ptr, ev_type);
+ break;
+ case DELETE_ROWS_EVENT_V1:
+ ptr[EVENT_TYPE_OFFSET]= WRITE_ROWS_EVENT_V1;
+ ev= new Write_rows_log_event((const char*) ptr, tmp_size,
+ glob_description_event);
+ ev->change_to_flashback_event(print_event_info, ptr, ev_type);
+ break;
+ case UPDATE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT_V1:
+ ev= new Update_rows_log_event((const char*) ptr, tmp_size,
+ glob_description_event);
+ ev->change_to_flashback_event(print_event_info, ptr, ev_type);
+ break;
+ default:
+ break;
+ }
+ delete ev;
+ }
+
+ if (do_print_encoded)
+ {
+ size_t const tmp_str_sz= my_base64_needed_encoded_length((int) size);
+ char *tmp_str;
+ if (!(tmp_str= (char *) my_malloc(PSI_NOT_INSTRUMENTED, tmp_str_sz, MYF(MY_WME))))
+ goto err;
+
+ if (my_base64_encode(ptr, (size_t) size, tmp_str))
+ {
+ DBUG_ASSERT(0);
+ }
+
+ my_b_printf(file, "%s\n", tmp_str);
+ my_free(tmp_str);
+ }
+
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ if (print_event_info->verbose || print_event_info->print_row_count ||
+ need_flashback_review)
+#else
+ // Flashback need the table_map to parse the event
+ if (print_event_info->verbose || print_event_info->print_row_count ||
+ is_flashback)
+#endif
+ {
+ 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
+
+ switch (et)
+ {
+ case TABLE_MAP_EVENT:
+ {
+ Table_map_log_event *map;
+ map= new Table_map_log_event((const char*) ptr, size,
+ glob_description_event);
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ if (need_flashback_review)
+ {
+ map->set_review_dbname(m_review_dbname.ptr());
+ map->set_review_tablename(m_review_tablename.ptr());
+ }
+#endif
+ print_event_info->m_table_map.set_table(map->get_table_id(), map);
+ break;
+ }
+ case WRITE_ROWS_EVENT:
+ case WRITE_ROWS_EVENT_V1:
+ {
+ ev= new Write_rows_log_event((const char*) ptr, size,
+ glob_description_event);
+ break;
+ }
+ case DELETE_ROWS_EVENT:
+ case DELETE_ROWS_EVENT_V1:
+ {
+ ev= new Delete_rows_log_event((const char*) ptr, size,
+ glob_description_event);
+ break;
+ }
+ case UPDATE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT_V1:
+ {
+ ev= new Update_rows_log_event((const char*) ptr, size,
+ glob_description_event);
+ break;
+ }
+ case WRITE_ROWS_COMPRESSED_EVENT:
+ case WRITE_ROWS_COMPRESSED_EVENT_V1:
+ {
+ ev= new Write_rows_compressed_log_event((const char*) ptr, size,
+ glob_description_event);
+ break;
+ }
+ case UPDATE_ROWS_COMPRESSED_EVENT:
+ case UPDATE_ROWS_COMPRESSED_EVENT_V1:
+ {
+ ev= new Update_rows_compressed_log_event((const char*) ptr, size,
+ glob_description_event);
+ break;
+ }
+ case DELETE_ROWS_COMPRESSED_EVENT:
+ case DELETE_ROWS_COMPRESSED_EVENT_V1:
+ {
+ ev= new Delete_rows_compressed_log_event((const char*) ptr, size,
+ glob_description_event);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (ev)
+ {
+ bool error= 0;
+
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ ev->need_flashback_review= need_flashback_review;
+ if (print_event_info->verbose)
+ {
+ if (ev->print_verbose(&print_event_info->tail_cache, print_event_info))
+ goto err;
+ }
+ else
+ {
+ IO_CACHE tmp_cache;
+
+ if (open_cached_file(&tmp_cache, NULL, NULL, 0,
+ MYF(MY_WME | MY_NABP)))
+ {
+ delete ev;
+ goto err;
+ }
+
+ error= ev->print_verbose(&tmp_cache, print_event_info);
+ close_cached_file(&tmp_cache);
+ if (unlikely(error))
+ {
+ delete ev;
+ goto err;
+ }
+ }
+#else
+ if (print_event_info->verbose)
+ error= ev->print_verbose(&print_event_info->tail_cache, print_event_info);
+ else
+ ev->count_row_events(print_event_info);
+#endif
+ delete ev;
+ if (unlikely(error))
+ goto err;
+ }
+ }
+ DBUG_RETURN(0);
+
+err:
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Log_event::print_timestamp()
+*/
+
+bool Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
+{
+ struct tm *res;
+ time_t my_when= when;
+ DBUG_ENTER("Log_event::print_timestamp");
+ if (!ts)
+ ts = &my_when;
+ res=localtime(ts);
+
+ DBUG_RETURN(my_b_printf(file,"%02d%02d%02d %2d:%02d:%02d",
+ res->tm_year % 100,
+ res->tm_mon+1,
+ res->tm_mday,
+ res->tm_hour,
+ res->tm_min,
+ res->tm_sec));
+}
+
+
+/**
+ Query_log_event::print().
+
+ @todo
+ print the catalog ??
+*/
+bool Query_log_event::print_query_header(IO_CACHE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ // TODO: print the catalog ??
+ char buff[64], *end; // Enough for SET TIMESTAMP
+ bool different_db= 1;
+ uint32 tmp;
+
+ if (!print_event_info->short_form)
+ {
+ if (print_header(file, print_event_info, FALSE) ||
+ my_b_printf(file,
+ "\t%s\tthread_id=%lu\texec_time=%lu\terror_code=%d\n",
+ get_type_str(), (ulong) thread_id, (ulong) exec_time,
+ error_code))
+ goto err;
+ }
+
+ if ((flags & LOG_EVENT_SUPPRESS_USE_F))
+ {
+ if (!is_trans_keyword())
+ print_event_info->db[0]= '\0';
+ }
+ else if (db)
+ {
+ different_db= memcmp(print_event_info->db, db, db_len + 1);
+ if (different_db)
+ memcpy(print_event_info->db, db, db_len + 1);
+ if (db[0] && different_db)
+ if (my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter))
+ goto err;
+ }
+
+ end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
+ if (when_sec_part && when_sec_part <= TIME_MAX_SECOND_PART)
+ {
+ *end++= '.';
+ end=int10_to_str(when_sec_part, end, 10);
+ }
+ end= strmov(end, print_event_info->delimiter);
+ *end++='\n';
+ if (my_b_write(file, (uchar*) buff, (uint) (end-buff)))
+ goto err;
+ if ((!print_event_info->thread_id_printed ||
+ ((flags & LOG_EVENT_THREAD_SPECIFIC_F) &&
+ thread_id != print_event_info->thread_id)))
+ {
+ // If --short-form, print deterministic value instead of pseudo_thread_id.
+ if (my_b_printf(file,"SET @@session.pseudo_thread_id=%lu%s\n",
+ short_form ? 999999999 : (ulong)thread_id,
+ print_event_info->delimiter))
+ goto err;
+ print_event_info->thread_id= thread_id;
+ print_event_info->thread_id_printed= 1;
+ }
+
+ /*
+ If flags2_inited==0, this is an event from 3.23 or 4.0 or a dummy
+ event from the mtr test suite; nothing to print (remember we don't
+ produce mixed relay logs so there cannot be 5.0 events before that
+ one so there is nothing to reset).
+ */
+ if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */
+ {
+ /* tmp is a bitmask of bits which have changed. */
+ if (likely(print_event_info->flags2_inited))
+ /* All bits which have changed */
+ tmp= (print_event_info->flags2) ^ flags2;
+ else /* that's the first Query event we read */
+ {
+ print_event_info->flags2_inited= 1;
+ tmp= ~((uint32)0); /* all bits have changed */
+ }
+
+ if (unlikely(tmp)) /* some bits have changed */
+ {
+ bool need_comma= 0;
+ if (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,
+ "@@session.sql_auto_is_null", &need_comma) ||
+ print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2,
+ "@@session.unique_checks", &need_comma) ||
+ print_set_option(file, tmp, OPTION_NOT_AUTOCOMMIT, ~flags2,
+ "@@session.autocommit", &need_comma) ||
+ print_set_option(file, tmp, OPTION_NO_CHECK_CONSTRAINT_CHECKS,
+ ~flags2,
+ "@@session.check_constraint_checks", &need_comma) ||
+ print_set_option(file, tmp, OPTION_IF_EXISTS, flags2,
+ "@@session.sql_if_exists", &need_comma)||
+ my_b_printf(file,"%s\n", print_event_info->delimiter))
+ goto err;
+ print_event_info->flags2= flags2;
+ }
+ }
+
+ /*
+ Now the session variables;
+ it's more efficient to pass SQL_MODE as a number instead of a
+ comma-separated list.
+ FOREIGN_KEY_CHECKS, SQL_AUTO_IS_NULL, UNIQUE_CHECKS are session-only
+ variables (they have no global version; they're not listed in
+ sql_class.h), The tests below work for pure binlogs or pure relay
+ logs. Won't work for mixed relay logs but we don't create mixed
+ relay logs (that is, there is no relay log with a format change
+ except within the 3 first events, which mysqlbinlog handles
+ gracefully). So this code should always be good.
+ */
+
+ if (likely(sql_mode_inited) &&
+ (unlikely(print_event_info->sql_mode != sql_mode ||
+ !print_event_info->sql_mode_inited)))
+ {
+ char llbuff[22];
+ if (my_b_printf(file,"SET @@session.sql_mode=%s%s\n",
+ ullstr(sql_mode, llbuff), print_event_info->delimiter))
+ goto err;
+ print_event_info->sql_mode= sql_mode;
+ print_event_info->sql_mode_inited= 1;
+ }
+ if (print_event_info->auto_increment_increment != auto_increment_increment ||
+ print_event_info->auto_increment_offset != auto_increment_offset)
+ {
+ if (my_b_printf(file,"SET @@session.auto_increment_increment=%lu, @@session.auto_increment_offset=%lu%s\n",
+ auto_increment_increment,auto_increment_offset,
+ print_event_info->delimiter))
+ goto err;
+ print_event_info->auto_increment_increment= auto_increment_increment;
+ print_event_info->auto_increment_offset= auto_increment_offset;
+ }
+
+ /* TODO: print the catalog when we feature SET CATALOG */
+
+ if (likely(charset_inited) &&
+ (unlikely(!print_event_info->charset_inited ||
+ memcmp(print_event_info->charset, charset, 6))))
+ {
+ CHARSET_INFO *cs_info= get_charset(uint2korr(charset), MYF(MY_WME));
+ if (cs_info)
+ {
+ /* for mysql client */
+ if (my_b_printf(file, "/*!\\C %s */%s\n",
+ cs_info->csname, print_event_info->delimiter))
+ goto err;
+ }
+ if (my_b_printf(file,"SET "
+ "@@session.character_set_client=%d,"
+ "@@session.collation_connection=%d,"
+ "@@session.collation_server=%d"
+ "%s\n",
+ uint2korr(charset),
+ uint2korr(charset+2),
+ uint2korr(charset+4),
+ print_event_info->delimiter))
+ goto err;
+ memcpy(print_event_info->charset, charset, 6);
+ print_event_info->charset_inited= 1;
+ }
+ if (time_zone_len)
+ {
+ if (memcmp(print_event_info->time_zone_str,
+ time_zone_str, time_zone_len+1))
+ {
+ if (my_b_printf(file,"SET @@session.time_zone='%s'%s\n",
+ time_zone_str, print_event_info->delimiter))
+ goto err;
+ memcpy(print_event_info->time_zone_str, time_zone_str, time_zone_len+1);
+ }
+ }
+ if (lc_time_names_number != print_event_info->lc_time_names_number)
+ {
+ if (my_b_printf(file, "SET @@session.lc_time_names=%d%s\n",
+ lc_time_names_number, print_event_info->delimiter))
+ goto err;
+ print_event_info->lc_time_names_number= lc_time_names_number;
+ }
+ if (charset_database_number != print_event_info->charset_database_number)
+ {
+ if (charset_database_number)
+ {
+ if (my_b_printf(file, "SET @@session.collation_database=%d%s\n",
+ charset_database_number, print_event_info->delimiter))
+ goto err;
+ }
+ else if (my_b_printf(file, "SET @@session.collation_database=DEFAULT%s\n",
+ print_event_info->delimiter))
+ goto err;
+ print_event_info->charset_database_number= charset_database_number;
+ }
+ return 0;
+
+err:
+ return 1;
+}
+
+
+bool Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file, 0, this);
+
+ /**
+ reduce the size of io cache so that the write function is called
+ for every call to my_b_write().
+ */
+ DBUG_EXECUTE_IF ("simulate_file_write_error",
+ {(&cache)->write_pos= (&cache)->write_end- 500;});
+ if (print_query_header(&cache, print_event_info))
+ goto err;
+ if (!is_flashback)
+ {
+ if (my_b_write(&cache, (uchar*) query, q_len) ||
+ my_b_printf(&cache, "\n%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else // is_flashback == 1
+ {
+ if (strcmp("BEGIN", query) == 0)
+ {
+ if (my_b_write(&cache, (uchar*) "COMMIT", 6) ||
+ my_b_printf(&cache, "\n%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (strcmp("COMMIT", query) == 0)
+ {
+ if (my_b_printf(&cache, "START TRANSACTION\n%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ }
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ DBUG_ENTER("Start_log_event_v3::print");
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
+ if (!print_event_info->short_form)
+ {
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\tStart: binlog v %d, server v %s created ",
+ binlog_version, server_version) ||
+ print_timestamp(&cache))
+ goto err;
+ if (created)
+ if (my_b_printf(&cache," at startup"))
+ goto err;
+ if (my_b_printf(&cache, "\n"))
+ goto err;
+ if (flags & LOG_EVENT_BINLOG_IN_USE_F)
+ if (my_b_printf(&cache,
+ "# Warning: this binlog is either in use or was not "
+ "closed properly.\n"))
+ goto err;
+ }
+ if (!is_artificial_event() && created)
+ {
+#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
+ /*
+ This is for mysqlbinlog: like in replication, we want to delete the stale
+ tmp files left by an unclean shutdown of mysqld (temporary tables)
+ and rollback unfinished transaction.
+ Probably this can be done with RESET CONNECTION (syntax to be defined).
+ */
+ if (my_b_printf(&cache,"RESET CONNECTION%s\n",
+ print_event_info->delimiter))
+ goto err;
+#else
+ if (my_b_printf(&cache,"ROLLBACK%s\n", print_event_info->delimiter))
+ goto err;
+#endif
+ }
+ if (temp_buf &&
+ print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
+ !print_event_info->short_form)
+ {
+ /* BINLOG is matched with the delimiter below on the same level */
+ bool do_print_encoded=
+ print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS;
+ if (do_print_encoded)
+ my_b_printf(&cache, "BINLOG '\n");
+
+ if (print_base64(&cache, print_event_info, do_print_encoded))
+ goto err;
+
+ if (do_print_encoded)
+ my_b_printf(&cache, "'%s\n", print_event_info->delimiter);
+
+ print_event_info->printed_fd_event= TRUE;
+ }
+ DBUG_RETURN(cache.flush_data());
+err:
+ DBUG_RETURN(1);
+}
+
+
+bool Start_encryption_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+ StringBuffer<1024> buf;
+ buf.append(STRING_WITH_LEN("# Encryption scheme: "));
+ buf.append_ulonglong(crypto_scheme);
+ buf.append(STRING_WITH_LEN(", key_version: "));
+ buf.append_ulonglong(key_version);
+ buf.append(STRING_WITH_LEN(", nonce: "));
+ buf.append_hex(nonce, BINLOG_NONCE_LENGTH);
+ buf.append(STRING_WITH_LEN("\n# The rest of the binlog is encrypted!\n"));
+ if (my_b_write(&cache, (uchar*)buf.ptr(), buf.length()))
+ return 1;
+ return (cache.flush_data());
+}
+
+
+bool Load_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ return print(file, print_event_info, 0);
+}
+
+
+bool Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
+ bool commented)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file_arg);
+ bool different_db= 1;
+ DBUG_ENTER("Load_log_event::print");
+
+ if (!print_event_info->short_form)
+ {
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\tQuery\tthread_id=%ld\texec_time=%ld\n",
+ thread_id, exec_time))
+ goto err;
+ }
+
+ if (db)
+ {
+ /*
+ If the database is different from the one of the previous statement, we
+ need to print the "use" command, and we update the last_db.
+ But if commented, the "use" is going to be commented so we should not
+ update the last_db.
+ */
+ if ((different_db= memcmp(print_event_info->db, db, db_len + 1)) &&
+ !commented)
+ memcpy(print_event_info->db, db, db_len + 1);
+ }
+
+ if (db && db[0] && different_db)
+ if (my_b_printf(&cache, "%suse %`s%s\n",
+ commented ? "# " : "",
+ db, print_event_info->delimiter))
+ goto err;
+
+ if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
+ if (my_b_printf(&cache,"%sSET @@session.pseudo_thread_id=%lu%s\n",
+ commented ? "# " : "", (ulong)thread_id,
+ print_event_info->delimiter))
+ goto err;
+ if (my_b_printf(&cache, "%sLOAD DATA ",
+ commented ? "# " : ""))
+ goto err;
+ if (check_fname_outside_temp_buf())
+ if (my_b_write_string(&cache, "LOCAL "))
+ goto err;
+ if (my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname))
+ goto err;
+
+ if (sql_ex.opt_flags & REPLACE_FLAG)
+ {
+ if (my_b_write_string(&cache, "REPLACE "))
+ goto err;
+ }
+ else if (sql_ex.opt_flags & IGNORE_FLAG)
+ if (my_b_write_string(&cache, "IGNORE "))
+ goto err;
+
+ if (my_b_printf(&cache, "INTO TABLE `%s`", table_name) ||
+ my_b_write_string(&cache, " FIELDS TERMINATED BY ") ||
+ pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len))
+ goto err;
+
+ if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
+ if (my_b_write_string(&cache, " OPTIONALLY "))
+ goto err;
+ if (my_b_write_string(&cache, " ENCLOSED BY ") ||
+ pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len) ||
+ my_b_write_string(&cache, " ESCAPED BY ") ||
+ pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len) ||
+ my_b_write_string(&cache, " LINES TERMINATED BY ") ||
+ pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len))
+ goto err;
+
+ if (sql_ex.line_start)
+ {
+ if (my_b_write_string(&cache," STARTING BY ") ||
+ pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len))
+ goto err;
+ }
+ if ((long) skip_lines > 0)
+ if (my_b_printf(&cache, " IGNORE %ld LINES", (long) skip_lines))
+ goto err;
+
+ if (num_fields)
+ {
+ uint i;
+ const char* field = fields;
+ if (my_b_write_string(&cache, " ("))
+ goto err;
+ for (i = 0; i < num_fields; i++)
+ {
+ if (i)
+ if (my_b_write_byte(&cache, ','))
+ goto err;
+ if (my_b_printf(&cache, "%`s", field))
+ goto err;
+ field += field_lens[i] + 1;
+ }
+ if (my_b_write_byte(&cache, ')'))
+ goto err;
+ }
+
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ DBUG_RETURN(cache.flush_data());
+err:
+ DBUG_RETURN(1);
+}
+
+
+bool Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ char buf[22];
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tRotate to "))
+ goto err;
+ if (new_log_ident)
+ if (my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len))
+ goto err;
+ if (my_b_printf(&cache, " pos: %s\n", llstr(pos, buf)))
+ goto err;
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool Binlog_checkpoint_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
+ if (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'))
+ return 1;
+ return cache.flush_data();
+}
+
+
+bool
+Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+ char buf[21];
+ uint32 i;
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\tGtid list ["))
+ goto err;
+
+ for (i= 0; i < count; ++i)
+ {
+ longlong10_to_str(list[i].seq_no, buf, 10);
+ if (my_b_printf(&cache, "%u-%u-%s", list[i].domain_id,
+ list[i].server_id, buf))
+ goto err;
+ if (i < count-1)
+ if (my_b_printf(&cache, ",\n# "))
+ goto err;
+ }
+ if (my_b_printf(&cache, "]\n"))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ char llbuff[22];
+ const char *UNINIT_VAR(msg);
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
+ if (!print_event_info->short_form)
+ {
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tIntvar\n"))
+ goto err;
+ }
+
+ if (my_b_printf(&cache, "SET "))
+ goto err;
+ switch (type) {
+ case LAST_INSERT_ID_EVENT:
+ msg="LAST_INSERT_ID";
+ break;
+ case INSERT_ID_EVENT:
+ msg="INSERT_ID";
+ break;
+ case INVALID_INT_EVENT:
+ default: // cannot happen
+ msg="INVALID_INT";
+ break;
+ }
+ if (my_b_printf(&cache, "%s=%s%s\n",
+ msg, llstr(val,llbuff), print_event_info->delimiter))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool Rand_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 llbuff[22],llbuff2[22];
+ if (!print_event_info->short_form)
+ {
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tRand\n"))
+ goto err;
+ }
+ if (my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
+ llstr(seed1, llbuff),llstr(seed2, llbuff2),
+ print_event_info->delimiter))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool Xid_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, this);
+
+ if (!print_event_info->short_form)
+ {
+ char buf[64];
+ longlong10_to_str(xid, buf, 10);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\tXid = %s\n", buf))
+ goto err;
+ }
+ if (my_b_printf(&cache, is_flashback ? "START TRANSACTION%s\n" : "COMMIT%s\n",
+ print_event_info->delimiter))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool User_var_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)
+ {
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tUser_var\n"))
+ goto err;
+ }
+
+ if (my_b_write_string(&cache, "SET @") ||
+ my_b_write_backtick_quote(&cache, name, name_len))
+ goto err;
+
+ if (is_null)
+ {
+ if (my_b_printf(&cache, ":=NULL%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else
+ {
+ switch (type) {
+ case REAL_RESULT:
+ double real_val;
+ char real_buf[FMT_G_BUFSIZE(14)];
+ float8get(real_val, val);
+ sprintf(real_buf, "%.14g", real_val);
+ if (my_b_printf(&cache, ":=%s%s\n", real_buf,
+ print_event_info->delimiter))
+ goto err;
+ break;
+ case INT_RESULT:
+ char int_buf[22];
+ longlong10_to_str(uint8korr(val), int_buf,
+ ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10));
+ if (my_b_printf(&cache, ":=%s%s\n", int_buf,
+ print_event_info->delimiter))
+ goto err;
+ break;
+ case DECIMAL_RESULT:
+ {
+ char str_buf[200];
+ int str_len= sizeof(str_buf) - 1;
+ int precision= (int)val[0];
+ int scale= (int)val[1];
+ decimal_digit_t dec_buf[10];
+ decimal_t dec;
+ dec.len= 10;
+ dec.buf= dec_buf;
+
+ bin2decimal((uchar*) val+2, &dec, precision, scale);
+ decimal2string(&dec, str_buf, &str_len, 0, 0, 0);
+ str_buf[str_len]= 0;
+ if (my_b_printf(&cache, ":=%s%s\n", str_buf,
+ print_event_info->delimiter))
+ goto err;
+ break;
+ }
+ case STRING_RESULT:
+ {
+ /*
+ Let's express the string in hex. That's the most robust way. If we
+ print it in character form instead, we need to escape it with
+ character_set_client which we don't know (we will know it in 5.0, but
+ in 4.1 we don't know it easily when we are printing
+ User_var_log_event). Explanation why we would need to bother with
+ character_set_client (quoting Bar):
+ > Note, the parser doesn't switch to another unescaping mode after
+ > it has met a character set introducer.
+ > For example, if an SJIS client says something like:
+ > SET @a= _ucs2 \0a\0b'
+ > the string constant is still unescaped according to SJIS, not
+ > according to UCS2.
+ */
+ char *hex_str;
+ CHARSET_INFO *cs;
+ bool error;
+
+ // 2 hex digits / byte
+ hex_str= (char *) my_malloc(PSI_NOT_INSTRUMENTED, 2 * val_len + 1 + 3, MYF(MY_WME));
+ if (!hex_str)
+ goto err;
+ str_to_hex(hex_str, val, val_len);
+ /*
+ For proper behaviour when mysqlbinlog|mysql, we need to explicitly
+ specify the variable's collation. It will however cause problems when
+ people want to mysqlbinlog|mysql into another server not supporting the
+ character set. But there's not much to do about this and it's unlikely.
+ */
+ if (!(cs= get_charset(charset_number, MYF(0))))
+ { /*
+ Generate an unusable command (=> syntax error) is probably the best
+ thing we can do here.
+ */
+ error= my_b_printf(&cache, ":=???%s\n", print_event_info->delimiter);
+ }
+ else
+ error= my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
+ cs->csname, hex_str, cs->name,
+ print_event_info->delimiter);
+ my_free(hex_str);
+ if (unlikely(error))
+ goto err;
+ break;
+ }
+ case ROW_RESULT:
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ }
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+#ifdef HAVE_REPLICATION
+
+bool Unknown_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file_arg);
+
+ if (what != ENCRYPTED)
+ {
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\n# Unknown event\n"))
+ goto err;
+ }
+ else if (my_b_printf(&cache, "# Encrypted event\n"))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+bool Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_write_string(&cache, "\tStop\n"))
+ return 1;
+ return cache.flush_data();
+}
+
+#endif
+
+
+bool Create_file_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info,
+ bool enable_local)
+{
+ if (print_event_info->short_form)
+ {
+ if (enable_local && check_fname_outside_temp_buf())
+ return Load_log_event::print(file, print_event_info);
+ return 0;
+ }
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ if (enable_local)
+ {
+ if (Load_log_event::print(file, print_event_info,
+ !check_fname_outside_temp_buf()))
+ goto err;
+
+ /**
+ reduce the size of io cache so that the write function is called
+ for every call to my_b_printf().
+ */
+ DBUG_EXECUTE_IF ("simulate_create_event_write_error",
+ {(&cache)->write_pos= (&cache)->write_end;
+ DBUG_SET("+d,simulate_file_write_error");});
+ /*
+ That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
+ SHOW BINLOG EVENTS we don't.
+ */
+ if (my_b_write_byte(&cache, '#'))
+ goto err;
+ }
+
+ if (my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+
+}
+
+
+bool Create_file_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ return print(file, print_event_info, 0);
+}
+
+
+/*
+ Append_block_log_event::print()
+*/
+
+bool Append_block_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\n#%s: file_id: %d block_len: %d\n",
+ get_type_str(), file_id, block_len))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+/*
+ Delete_file_log_event::print()
+*/
+
+bool Delete_file_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\n#Delete_file: file_id=%u\n", file_id))
+ return 1;
+
+ return cache.flush_data();
+}
+
+/*
+ Execute_load_log_event::print()
+*/
+
+bool Execute_load_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\n#Exec_load: file_id=%d\n",
+ file_id))
+ return 1;
+
+ return cache.flush_data();
+}
+
+bool Execute_load_query_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ return print(file, print_event_info, 0);
+}
+
+/**
+ Prints the query as LOAD DATA LOCAL and with rewritten filename.
+*/
+bool Execute_load_query_log_event::print(FILE* file,
+ PRINT_EVENT_INFO* print_event_info,
+ const char *local_fname)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ if (print_query_header(&cache, print_event_info))
+ goto err;
+
+ /**
+ reduce the size of io cache so that the write function is called
+ for every call to my_b_printf().
+ */
+ DBUG_EXECUTE_IF ("simulate_execute_event_write_error",
+ {(&cache)->write_pos= (&cache)->write_end;
+ DBUG_SET("+d,simulate_file_write_error");});
+
+ if (local_fname)
+ {
+ if (my_b_write(&cache, (uchar*) query, fn_pos_start) ||
+ my_b_write_string(&cache, " LOCAL INFILE ") ||
+ pretty_print_str(&cache, local_fname, (int)strlen(local_fname)))
+ goto err;
+
+ if (dup_handling == LOAD_DUP_REPLACE)
+ if (my_b_write_string(&cache, " REPLACE"))
+ goto err;
+
+ if (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))
+ goto err;
+ }
+ else
+ {
+ if (my_b_write(&cache, (uchar*) query, q_len) ||
+ my_b_printf(&cache, "\n%s\n", print_event_info->delimiter))
+ goto err;
+ }
+
+ if (!print_event_info->short_form)
+ my_b_printf(&cache, "# file_id: %d \n", file_id);
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+
+
+const char str_binlog[]= "\nBINLOG '\n";
+const char fmt_delim[]= "'%s\n";
+const char fmt_n_delim[]= "\n'%s";
+const char fmt_frag[]= "\nSET @binlog_fragment_%d ='\n";
+const char fmt_binlog2[]= "BINLOG @binlog_fragment_0, @binlog_fragment_1%s\n";
+
+/**
+ Print an event "body" cache to @c file possibly in two fragments.
+ Each fragement is optionally per @c do_wrap to produce an SQL statement.
+
+ @param file a file to print to
+ @param body the "body" IO_CACHE of event
+ @param do_wrap whether to wrap base64-encoded strings with
+ SQL cover.
+ @param delimiter delimiter string
+
+ @param is_verbose MDEV-10362 workraround parameter to pass
+ info on presence of verbose printout in cache encoded data
+
+ The function signals on any error through setting @c body->error to -1.
+*/
+bool copy_cache_to_file_wrapped(IO_CACHE *body,
+ FILE *file,
+ bool do_wrap,
+ const char *delimiter,
+ bool is_verbose /*TODO: remove */)
+{
+ const my_off_t cache_size= my_b_tell(body);
+
+ if (reinit_io_cache(body, READ_CACHE, 0L, FALSE, FALSE))
+ goto err;
+
+ if (!do_wrap)
+ {
+ my_b_copy_to_file(body, file, SIZE_T_MAX);
+ }
+ else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) >
+ opt_binlog_rows_event_max_encoded_size)
+ {
+ /*
+ 2 fragments can always represent near 1GB row-based
+ base64-encoded event as two strings each of size less than
+ max(max_allowed_packet). Greater number of fragments does not
+ save from potential need to tweak (increase) @@max_allowed_packet
+ before to process the fragments. So 2 is safe and enough.
+
+ Split the big query when its packet size's estimation exceeds a
+ limit. The estimate includes the maximum packet header
+ contribution of non-compressed packet.
+ */
+ my_fprintf(file, fmt_frag, 0);
+ if (my_b_copy_to_file(body, file, (size_t) cache_size/2 + 1))
+ goto err;
+ my_fprintf(file, fmt_n_delim, delimiter);
+
+ my_fprintf(file, fmt_frag, 1);
+ if (my_b_copy_to_file(body, file, SIZE_T_MAX))
+ goto err;
+ my_fprintf(file, fmt_delim, delimiter);
+
+ my_fprintf(file, fmt_binlog2, delimiter);
+ }
+ else
+ {
+ my_fprintf(file, str_binlog);
+ if (my_b_copy_to_file(body, file, SIZE_T_MAX))
+ goto err;
+ my_fprintf(file, fmt_delim, delimiter);
+ }
+ reinit_io_cache(body, WRITE_CACHE, 0, FALSE, TRUE);
+
+ return false;
+
+err:
+ body->error = -1;
+ return true;
+}
+
+
+/**
+ Print an event "body" cache to @c file possibly in two fragments.
+ Each fragement is optionally per @c do_wrap to produce an SQL statement.
+
+ @param file a file to print to
+ @param body the "body" IO_CACHE of event
+ @param do_wrap whether to wrap base64-encoded strings with
+ SQL cover.
+ @param delimiter delimiter string
+
+ The function signals on any error through setting @c body->error to -1.
+*/
+bool copy_cache_to_string_wrapped(IO_CACHE *cache,
+ LEX_STRING *to,
+ bool do_wrap,
+ const char *delimiter,
+ bool is_verbose)
+{
+ const my_off_t cache_size= my_b_tell(cache);
+ // contribution to total size estimate of formating
+ const size_t fmt_size=
+ sizeof(str_binlog) + 2*(sizeof(fmt_frag) + 2 /* %d */) +
+ sizeof(fmt_delim) + sizeof(fmt_n_delim) +
+ sizeof(fmt_binlog2) +
+ 3*PRINT_EVENT_INFO::max_delimiter_size;
+
+ if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE))
+ goto err;
+
+ if (!(to->str= (char*) my_malloc(PSI_NOT_INSTRUMENTED, (size_t)cache->end_of_file + fmt_size,
+ MYF(0))))
+ {
+ perror("Out of memory: can't allocate memory in "
+ "copy_cache_to_string_wrapped().");
+ goto err;
+ }
+
+ if (!do_wrap)
+ {
+ if (my_b_read(cache, (uchar*) to->str,
+ (to->length= (size_t)cache->end_of_file)))
+ goto err;
+ }
+ else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) >
+ opt_binlog_rows_event_max_encoded_size)
+ {
+ /*
+ 2 fragments can always represent near 1GB row-based
+ base64-encoded event as two strings each of size less than
+ max(max_allowed_packet). Greater number of fragments does not
+ save from potential need to tweak (increase) @@max_allowed_packet
+ before to process the fragments. So 2 is safe and enough.
+
+ Split the big query when its packet size's estimation exceeds a
+ limit. The estimate includes the maximum packet header
+ contribution of non-compressed packet.
+ */
+ char *str= to->str;
+ size_t add_to_len;
+
+ str += (to->length= sprintf(str, fmt_frag, 0));
+ if (my_b_read(cache, (uchar*) str, (uint32) (cache_size/2 + 1)))
+ goto err;
+ str += (add_to_len = (uint32) (cache_size/2 + 1));
+ to->length += add_to_len;
+ str += (add_to_len= sprintf(str, fmt_n_delim, delimiter));
+ to->length += add_to_len;
+
+ str += (add_to_len= sprintf(str, fmt_frag, 1));
+ to->length += add_to_len;
+ if (my_b_read(cache, (uchar*) str, uint32(cache->end_of_file - (cache_size/2 + 1))))
+ goto err;
+ str += (add_to_len= uint32(cache->end_of_file - (cache_size/2 + 1)));
+ to->length += add_to_len;
+ {
+ str += (add_to_len= sprintf(str , fmt_delim, delimiter));
+ to->length += add_to_len;
+ }
+ to->length += sprintf(str, fmt_binlog2, delimiter);
+ }
+ else
+ {
+ char *str= to->str;
+
+ str += (to->length= sprintf(str, str_binlog));
+ if (my_b_read(cache, (uchar*) str, (size_t)cache->end_of_file))
+ goto err;
+ str += cache->end_of_file;
+ to->length += (size_t)cache->end_of_file;
+ to->length += sprintf(str , fmt_delim, delimiter);
+ }
+
+ reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
+
+ return false;
+
+err:
+ cache->error= -1;
+ return true;
+}
+
+/**
+ The function invokes base64 encoder to run on the current
+ event string and store the result into two caches.
+ When the event ends the current statement the caches are is copied into
+ the argument file.
+ Copying is also concerned how to wrap the event, specifically to produce
+ a valid SQL syntax.
+ When the encoded data size is within max(MAX_ALLOWED_PACKET)
+ a regular BINLOG query is composed. Otherwise it is build as fragmented
+
+ SET @binlog_fragment_0='...';
+ SET @binlog_fragment_1='...';
+ BINLOG @binlog_fragment_0, @binlog_fragment_1;
+
+ where fragments are represented by a pair of indexed user
+ "one shot" variables.
+
+ @note
+ If any changes made don't forget to duplicate them to
+ Old_rows_log_event as long as it's supported.
+
+ @param file pointer to IO_CACHE
+ @param print_event_info pointer to print_event_info specializing
+ what out of and how to print the event
+ @param name the name of a table that the event operates on
+
+ The function signals on any error of cache access through setting
+ that cache's @c error to -1.
+*/
+bool Rows_log_event::print_helper(FILE *file,
+ PRINT_EVENT_INFO *print_event_info,
+ char const *const name)
+{
+ IO_CACHE *const head= &print_event_info->head_cache;
+ IO_CACHE *const body= &print_event_info->body_cache;
+ IO_CACHE *const tail= &print_event_info->tail_cache;
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ IO_CACHE *const sql= &print_event_info->review_sql_cache;
+#endif
+ bool do_print_encoded=
+ print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
+ print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS &&
+ !print_event_info->short_form;
+ bool const last_stmt_event= get_flags(STMT_END_F);
+
+ if (!print_event_info->short_form)
+ {
+ char llbuff[22];
+
+ print_header(head, print_event_info, !last_stmt_event);
+ if (my_b_printf(head, "\t%s: table id %s%s\n",
+ name, ullstr(m_table_id, llbuff),
+ last_stmt_event ? " flags: STMT_END_F" : ""))
+ goto err;
+ }
+ if (!print_event_info->short_form || print_event_info->print_row_count)
+ if (print_base64(body, print_event_info, do_print_encoded))
+ goto err;
+
+ if (last_stmt_event)
+ {
+ if (!is_flashback)
+ {
+ if (copy_event_cache_to_file_and_reinit(head, file) ||
+ copy_cache_to_file_wrapped(body, file, do_print_encoded,
+ print_event_info->delimiter,
+ print_event_info->verbose) ||
+ copy_event_cache_to_file_and_reinit(tail, file))
+ goto err;
+ }
+ else
+ {
+ LEX_STRING tmp_str;
+
+ if (copy_event_cache_to_string_and_reinit(head, &tmp_str))
+ return 1;
+ output_buf.append(tmp_str.str, tmp_str.length); // Not \0 terminated);
+ my_free(tmp_str.str);
+
+ if (copy_cache_to_string_wrapped(body, &tmp_str, do_print_encoded,
+ print_event_info->delimiter,
+ print_event_info->verbose))
+ return 1;
+ output_buf.append(tmp_str.str, tmp_str.length);
+ my_free(tmp_str.str);
+ if (copy_event_cache_to_string_and_reinit(tail, &tmp_str))
+ return 1;
+ output_buf.append(tmp_str.str, tmp_str.length);
+ my_free(tmp_str.str);
+
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ if (copy_event_cache_to_string_and_reinit(sql, &tmp_str))
+ return 1;
+ output_buf.append(tmp_str.str, tmp_str.length);
+ my_free(tmp_str.str);
+#endif
+ }
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+
+bool Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
+{
+ char *pbeg; // beginning of the next line
+ char *pend; // end of the next line
+ uint cnt= 0; // characters counter
+
+ if (!pinfo->short_form)
+ {
+ if (print_header(&pinfo->head_cache, pinfo, TRUE) ||
+ my_b_printf(&pinfo->head_cache, "\tAnnotate_rows:\n"))
+ goto err;
+ }
+ else if (my_b_printf(&pinfo->head_cache, "# Annotate_rows:\n"))
+ goto err;
+
+ for (pbeg= m_query_txt; ; pbeg= pend)
+ {
+ // skip all \r's and \n's at the beginning of the next line
+ for (;; pbeg++)
+ {
+ if (++cnt > m_query_len)
+ return 0;
+
+ if (*pbeg != '\r' && *pbeg != '\n')
+ break;
+ }
+
+ // find end of the next line
+ for (pend= pbeg + 1;
+ ++cnt <= m_query_len && *pend != '\r' && *pend != '\n';
+ pend++)
+ ;
+
+ // print next line
+ if (my_b_write(&pinfo->head_cache, (const uchar*) "#Q> ", 4) ||
+ my_b_write(&pinfo->head_cache, (const uchar*) pbeg, pend - pbeg) ||
+ my_b_write(&pinfo->head_cache, (const uchar*) "\n", 1))
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+
+/*
+ Rewrite database name for the event to name specified by new_db
+ SYNOPSIS
+ new_db Database name to change to
+ new_len Length
+ desc Event describing binlog that we're writing to.
+
+ DESCRIPTION
+ Reset db name. This function assumes that temp_buf member contains event
+ representation taken from a binary log. It resets m_dbnam and m_dblen and
+ rewrites temp_buf with new db name.
+
+ RETURN
+ 0 - Success
+ other - Error
+*/
+
+int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
+ const Format_description_log_event* desc)
+{
+ DBUG_ENTER("Table_map_log_event::rewrite_db");
+ DBUG_ASSERT(temp_buf);
+
+ uint header_len= MY_MIN(desc->common_header_len,
+ LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN;
+ int len_diff;
+
+ if (!(len_diff= (int)(new_len - m_dblen)))
+ {
+ memcpy((void*) (temp_buf + header_len + 1), new_db, m_dblen + 1);
+ memcpy((void*) m_dbnam, new_db, m_dblen + 1);
+ DBUG_RETURN(0);
+ }
+
+ // Create new temp_buf
+ ulong event_cur_len= uint4korr(temp_buf + EVENT_LEN_OFFSET);
+ ulong event_new_len= event_cur_len + len_diff;
+ char* new_temp_buf= (char*) my_malloc(PSI_NOT_INSTRUMENTED, event_new_len, MYF(MY_WME));
+
+ if (!new_temp_buf)
+ {
+ sql_print_error("Table_map_log_event::rewrite_db: "
+ "failed to allocate new temp_buf (%d bytes required)",
+ event_new_len);
+ DBUG_RETURN(-1);
+ }
+
+ // Rewrite temp_buf
+ char* ptr= new_temp_buf;
+ size_t cnt= 0;
+
+ // Copy header and change event length
+ memcpy(ptr, temp_buf, header_len);
+ int4store(ptr + EVENT_LEN_OFFSET, event_new_len);
+ ptr += header_len;
+ cnt += header_len;
+
+ // Write new db name length and new name
+ DBUG_ASSERT(new_len < 0xff);
+ *ptr++ = (char)new_len;
+ memcpy(ptr, new_db, new_len + 1);
+ ptr += new_len + 1;
+ cnt += m_dblen + 2;
+
+ // Copy rest part
+ memcpy(ptr, temp_buf + cnt, event_cur_len - cnt);
+
+ // Reregister temp buf
+ free_temp_buf();
+ register_temp_buf(new_temp_buf, TRUE);
+
+ // Reset m_dbnam and m_dblen members
+ m_dblen= new_len;
+
+ // m_dbnam resides in m_memory together with m_tblnam and m_coltype
+ uchar* memory= m_memory;
+ char const* tblnam= m_tblnam;
+ uchar* coltype= m_coltype;
+
+ m_memory= (uchar*) my_multi_malloc(PSI_NOT_INSTRUMENTED, MYF(MY_WME),
+ &m_dbnam, (uint) m_dblen + 1,
+ &m_tblnam, (uint) m_tbllen + 1,
+ &m_coltype, (uint) m_colcnt,
+ NullS);
+
+ if (!m_memory)
+ {
+ sql_print_error("Table_map_log_event::rewrite_db: "
+ "failed to allocate new m_memory (%d + %d + %d bytes required)",
+ m_dblen + 1, m_tbllen + 1, m_colcnt);
+ DBUG_RETURN(-1);
+ }
+
+ memcpy((void*)m_dbnam, new_db, m_dblen + 1);
+ memcpy((void*)m_tblnam, tblnam, m_tbllen + 1);
+ memcpy(m_coltype, coltype, m_colcnt);
+
+ my_free(memory);
+ DBUG_RETURN(0);
+}
+
+
+bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+ if (!print_event_info->short_form)
+ {
+ char llbuff[22];
+
+ print_header(&print_event_info->head_cache, print_event_info, TRUE);
+ if (my_b_printf(&print_event_info->head_cache,
+ "\tTable_map: %`s.%`s mapped to number %s%s\n",
+ m_dbnam, m_tblnam, ullstr(m_table_id, llbuff),
+ ((m_flags & TM_BIT_HAS_TRIGGERS_F) ?
+ " (has triggers)" : "")))
+ goto err;
+ }
+ if (!print_event_info->short_form || print_event_info->print_row_count)
+ {
+
+ if (print_event_info->print_table_metadata)
+ {
+ Optional_metadata_fields fields(m_optional_metadata,
+ m_optional_metadata_len);
+
+ print_columns(&print_event_info->head_cache, fields);
+ print_primary_key(&print_event_info->head_cache, fields);
+ }
+ bool do_print_encoded=
+ print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
+ print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS &&
+ !print_event_info->short_form;
+
+ if (print_base64(&print_event_info->body_cache, print_event_info,
+ do_print_encoded) ||
+ copy_event_cache_to_file_and_reinit(&print_event_info->head_cache,
+ file))
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/**
+ Interface for iterator over charset columns.
+*/
+class Table_map_log_event::Charset_iterator
+{
+ public:
+ typedef Table_map_log_event::Optional_metadata_fields::Default_charset
+ Default_charset;
+ virtual const CHARSET_INFO *next()= 0;
+ virtual ~Charset_iterator(){};
+ /**
+ Factory method to create an instance of the appropriate subclass.
+ */
+ static std::unique_ptr<Charset_iterator> create_charset_iterator(
+ const Default_charset &default_charset,
+ const std::vector<uint> &column_charset);
+};
+
+/**
+ Implementation of charset iterator for the DEFAULT_CHARSET type.
+*/
+class Table_map_log_event::Default_charset_iterator : public Charset_iterator
+{
+ public:
+ Default_charset_iterator(const Default_charset &default_charset)
+ : m_iterator(default_charset.charset_pairs.begin()),
+ m_end(default_charset.charset_pairs.end()),
+ m_column_index(0),
+ m_default_charset_info(
+ get_charset(default_charset.default_charset, 0)) {}
+
+ const CHARSET_INFO *next() override {
+ const CHARSET_INFO *ret;
+ if (m_iterator != m_end && m_iterator->first == m_column_index) {
+ ret = get_charset(m_iterator->second, 0);
+ m_iterator++;
+ } else
+ ret = m_default_charset_info;
+ m_column_index++;
+ return ret;
+ }
+ ~Default_charset_iterator(){};
+
+ private:
+ std::vector<Optional_metadata_fields::uint_pair>::const_iterator m_iterator,
+ m_end;
+ uint m_column_index;
+ const CHARSET_INFO *m_default_charset_info;
+};
+//Table_map_log_event::Default_charset_iterator::~Default_charset_iterator(){int a=8;a++; a--;};
+/**
+ Implementation of charset iterator for the COLUMNT_CHARSET type.
+*/
+class Table_map_log_event::Column_charset_iterator : public Charset_iterator
+{
+ public:
+ Column_charset_iterator(const std::vector<uint> &column_charset)
+ : m_iterator(column_charset.begin()), m_end(column_charset.end()) {}
+
+ const CHARSET_INFO *next() override {
+ const CHARSET_INFO *ret = nullptr;
+ if (m_iterator != m_end) {
+ ret = get_charset(*m_iterator, 0);
+ m_iterator++;
+ }
+ return ret;
+ }
+
+ ~Column_charset_iterator(){};
+ private:
+ std::vector<uint>::const_iterator m_iterator;
+ std::vector<uint>::const_iterator m_end;
+};
+//Table_map_log_event::Column_charset_iterator::~Column_charset_iterator(){int a=8;a++; a--;};
+
+std::unique_ptr<Table_map_log_event::Charset_iterator>
+Table_map_log_event::Charset_iterator::create_charset_iterator(
+ const Default_charset &default_charset,
+ const std::vector<uint> &column_charset)
+{
+ if (!default_charset.empty())
+ return std::unique_ptr<Charset_iterator>(
+ new Default_charset_iterator(default_charset));
+ else
+ return std::unique_ptr<Charset_iterator>(
+ new Column_charset_iterator(column_charset));
+}
+/**
+ return the string name of a type.
+
+ @param[in] type type of a column
+ @param[in|out] meta_ptr the meta_ptr of the column. If the type doesn't have
+ metadata, it will not change meta_ptr, otherwise
+ meta_ptr will be moved to the end of the column's
+ metadat.
+ @param[in] cs charset of the column if it is a character column.
+ @param[out] typestr buffer to storing the string name of the type
+ @param[in] typestr_length length of typestr
+ @param[in] geometry_type internal geometry_type
+ */
+static void get_type_name(uint type, unsigned char** meta_ptr,
+ const CHARSET_INFO *cs, char *typestr,
+ uint typestr_length, unsigned int geometry_type)
+{
+ switch (type) {
+ case MYSQL_TYPE_LONG:
+ my_snprintf(typestr, typestr_length, "%s", "INT");
+ break;
+ case MYSQL_TYPE_TINY:
+ my_snprintf(typestr, typestr_length, "TINYINT");
+ break;
+ case MYSQL_TYPE_SHORT:
+ my_snprintf(typestr, typestr_length, "SMALLINT");
+ break;
+ case MYSQL_TYPE_INT24:
+ my_snprintf(typestr, typestr_length, "MEDIUMINT");
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ my_snprintf(typestr, typestr_length, "BIGINT");
+ break;
+ case MYSQL_TYPE_NEWDECIMAL:
+ my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)",
+ (*meta_ptr)[0], (*meta_ptr)[1]);
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ my_snprintf(typestr, typestr_length, "FLOAT");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ my_snprintf(typestr, typestr_length, "DOUBLE");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_BIT:
+ my_snprintf(typestr, typestr_length, "BIT(%d)",
+ (((*meta_ptr)[0])) + (*meta_ptr)[1]*8);
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_TIMESTAMP2:
+ if (**meta_ptr != 0)
+ my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", **meta_ptr);
+ else
+ my_snprintf(typestr, typestr_length, "TIMESTAMP");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_DATETIME2:
+ if (**meta_ptr != 0)
+ my_snprintf(typestr, typestr_length, "DATETIME(%d)", **meta_ptr);
+ else
+ my_snprintf(typestr, typestr_length, "DATETIME");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_TIME2:
+ if (**meta_ptr != 0)
+ my_snprintf(typestr, typestr_length, "TIME(%d)", **meta_ptr);
+ else
+ my_snprintf(typestr, typestr_length, "TIME");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_DATE:
+ my_snprintf(typestr, typestr_length, "DATE");
+ break;
+ case MYSQL_TYPE_YEAR:
+ my_snprintf(typestr, typestr_length, "YEAR");
+ break;
+ case MYSQL_TYPE_ENUM:
+ my_snprintf(typestr, typestr_length, "ENUM");
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_SET:
+ my_snprintf(typestr, typestr_length, "SET");
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_BLOB:
+ {
+ bool is_text= (cs && cs->number != my_charset_bin.number);
+ const char *names[5][2] = {
+ {"INVALID_BLOB(%d)", "INVALID_TEXT(%d)"},
+ {"TINYBLOB", "TINYTEXT"},
+ {"BLOB", "TEXT"},
+ {"MEDIUMBLOB", "MEDIUMTEXT"},
+ {"LONGBLOB", "LONGTEXT"}
+ };
+ unsigned char size= **meta_ptr;
+
+ if (size == 0 || size > 4)
+ my_snprintf(typestr, typestr_length, names[0][is_text], size);
+ else
+ my_snprintf(typestr, typestr_length, names[**meta_ptr][is_text]);
+
+ (*meta_ptr)++;
+ }
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ if (cs && cs->number != my_charset_bin.number)
+ my_snprintf(typestr, typestr_length, "VARCHAR(%d)",
+ uint2korr(*meta_ptr)/cs->mbmaxlen);
+ else
+ my_snprintf(typestr, typestr_length, "VARBINARY(%d)",
+ uint2korr(*meta_ptr));
+
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_STRING:
+ {
+ uint byte0= (*meta_ptr)[0];
+ uint byte1= (*meta_ptr)[1];
+ uint len= (((byte0 & 0x30) ^ 0x30) << 4) | byte1;
+
+ if (cs && cs->number != my_charset_bin.number)
+ my_snprintf(typestr, typestr_length, "CHAR(%d)", len/cs->mbmaxlen);
+ else
+ my_snprintf(typestr, typestr_length, "BINARY(%d)", len);
+
+ (*meta_ptr)+= 2;
+ }
+ break;
+ case MYSQL_TYPE_GEOMETRY:
+ {
+ const char* names[8] = {
+ "GEOMETRY", "POINT", "LINESTRING", "POLYGON", "MULTIPOINT",
+ "MULTILINESTRING", "MULTIPOLYGON", "GEOMETRYCOLLECTION"
+ };
+ if (geometry_type < 8)
+ my_snprintf(typestr, typestr_length, names[geometry_type]);
+ else
+ my_snprintf(typestr, typestr_length, "INVALID_GEOMETRY_TYPE(%u)",
+ geometry_type);
+ (*meta_ptr)++;
+ }
+ break;
+ default:
+ *typestr= 0;
+ break;
+ }
+}
+
+void Table_map_log_event::print_columns(IO_CACHE *file,
+ const Optional_metadata_fields &fields)
+{
+ unsigned char* field_metadata_ptr= m_field_metadata;
+ std::vector<bool>::const_iterator signedness_it= fields.m_signedness.begin();
+
+ std::unique_ptr<Charset_iterator> charset_it =
+ Charset_iterator::create_charset_iterator(fields.m_default_charset,
+ fields.m_column_charset);
+ std::unique_ptr<Charset_iterator> enum_and_set_charset_it =
+ Charset_iterator::create_charset_iterator(
+ fields.m_enum_and_set_default_charset,
+ fields.m_enum_and_set_column_charset);
+ std::vector<std::string>::const_iterator col_names_it=
+ fields.m_column_name.begin();
+ std::vector<Optional_metadata_fields::str_vector>::const_iterator
+ set_str_values_it= fields.m_set_str_value.begin();
+ std::vector<Optional_metadata_fields::str_vector>::const_iterator
+ enum_str_values_it= fields.m_enum_str_value.begin();
+ std::vector<unsigned int>::const_iterator geometry_type_it=
+ fields.m_geometry_type.begin();
+
+ uint geometry_type= 0;
+
+ my_b_printf(file, "# Columns(");
+
+ for (unsigned long i= 0; i < m_colcnt; i++)
+ {
+ uint real_type = m_coltype[i];
+ if (real_type == MYSQL_TYPE_STRING &&
+ (*field_metadata_ptr == MYSQL_TYPE_ENUM ||
+ *field_metadata_ptr == MYSQL_TYPE_SET))
+ real_type= *field_metadata_ptr;
+
+ // Get current column's collation id if it is a character, enum,
+ // or set column
+ const CHARSET_INFO *cs = NULL;
+ if (is_character_type(real_type))
+ cs = charset_it->next();
+ else if (is_enum_or_set_type(real_type))
+ cs = enum_and_set_charset_it->next();
+
+ // Print column name
+ if (col_names_it != fields.m_column_name.end())
+ {
+ pretty_print_identifier(file, col_names_it->c_str(), col_names_it->size());
+ my_b_printf(file, " ");
+ col_names_it++;
+ }
+
+
+ // update geometry_type for geometry columns
+ if (real_type == MYSQL_TYPE_GEOMETRY)
+ {
+ geometry_type= (geometry_type_it != fields.m_geometry_type.end()) ?
+ *geometry_type_it++ : 0;
+ }
+
+ // print column type
+ const uint TYPE_NAME_LEN = 100;
+ char type_name[TYPE_NAME_LEN];
+ get_type_name(real_type, &field_metadata_ptr, cs, type_name,
+ TYPE_NAME_LEN, geometry_type);
+
+ if (type_name[0] == '\0')
+ {
+ my_b_printf(file, "INVALID_TYPE(%d)", real_type);
+ continue;
+ }
+ my_b_printf(file, "%s", type_name);
+
+ // Print UNSIGNED for numeric column
+ if (is_numeric_type(real_type) &&
+ signedness_it != fields.m_signedness.end())
+ {
+ if (*signedness_it == true)
+ my_b_printf(file, " UNSIGNED");
+ signedness_it++;
+ }
+
+ // if the column is not marked as 'null', print 'not null'
+ if (!(m_null_bits[(i / 8)] & (1 << (i % 8))))
+ my_b_printf(file, " NOT NULL");
+
+ // Print string values of SET and ENUM column
+ const Optional_metadata_fields::str_vector *str_values= NULL;
+ if (real_type == MYSQL_TYPE_ENUM &&
+ enum_str_values_it != fields.m_enum_str_value.end())
+ {
+ str_values= &(*enum_str_values_it);
+ enum_str_values_it++;
+ }
+ else if (real_type == MYSQL_TYPE_SET &&
+ set_str_values_it != fields.m_set_str_value.end())
+ {
+ str_values= &(*set_str_values_it);
+ set_str_values_it++;
+ }
+
+ if (str_values != NULL)
+ {
+ const char *separator= "(";
+ for (Optional_metadata_fields::str_vector::const_iterator it=
+ str_values->begin(); it != str_values->end(); it++)
+ {
+ my_b_printf(file, "%s", separator);
+ pretty_print_str(file, it->c_str(), it->size());
+ separator= ",";
+ }
+ my_b_printf(file, ")");
+ }
+ // Print column character set, except in text columns with binary collation
+ if (cs != NULL &&
+ (is_enum_or_set_type(real_type) || cs->number != my_charset_bin.number))
+ my_b_printf(file, " CHARSET %s COLLATE %s", cs->csname, cs->name);
+ if (i != m_colcnt - 1) my_b_printf(file, ",\n# ");
+ }
+ my_b_printf(file, ")");
+ my_b_printf(file, "\n");
+}
+
+void Table_map_log_event::print_primary_key
+ (IO_CACHE *file,const Optional_metadata_fields &fields)
+{
+ if (!fields.m_primary_key.empty())
+ {
+ my_b_printf(file, "# Primary Key(");
+
+ std::vector<Optional_metadata_fields::uint_pair>::const_iterator it=
+ fields.m_primary_key.begin();
+
+ for (; it != fields.m_primary_key.end(); it++)
+ {
+ if (it != fields.m_primary_key.begin())
+ my_b_printf(file, ", ");
+
+ // Print column name or column index
+ if (it->first >= fields.m_column_name.size())
+ my_b_printf(file, "%u", it->first);
+ else
+ my_b_printf(file, "%s", fields.m_column_name[it->first].c_str());
+
+ // Print prefix length
+ if (it->second != 0)
+ my_b_printf(file, "(%u)", it->second);
+ }
+
+ my_b_printf(file, ")\n");
+ }
+}
+
+
+bool Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
+{
+ DBUG_EXECUTE_IF("simulate_cache_read_error",
+ {DBUG_SET("+d,simulate_my_b_fill_error");});
+ return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Delete_rows" : "Write_rows");
+}
+
+bool Write_rows_compressed_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ char *new_buf;
+ ulong len;
+ bool is_malloc = false;
+ if(!row_log_event_uncompress(glob_description_event,
+ checksum_alg == BINLOG_CHECKSUM_ALG_CRC32,
+ temp_buf, UINT_MAX32, NULL, 0, &is_malloc, &new_buf, &len))
+ {
+ free_temp_buf();
+ register_temp_buf(new_buf, true);
+ if (Rows_log_event::print_helper(file, print_event_info,
+ "Write_compressed_rows"))
+ goto err;
+ }
+ else
+ {
+ if (my_b_printf(&print_event_info->head_cache,
+ "ERROR: uncompress write_compressed_rows failed\n"))
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+
+bool Delete_rows_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ return Rows_log_event::print_helper(file, print_event_info, is_flashback ? "Write_rows" : "Delete_rows");
+}
+
+
+bool Delete_rows_compressed_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ char *new_buf;
+ ulong len;
+ bool is_malloc = false;
+ if(!row_log_event_uncompress(glob_description_event,
+ checksum_alg == BINLOG_CHECKSUM_ALG_CRC32,
+ temp_buf, UINT_MAX32, NULL, 0, &is_malloc, &new_buf, &len))
+ {
+ free_temp_buf();
+ register_temp_buf(new_buf, true);
+ if (Rows_log_event::print_helper(file, print_event_info,
+ "Delete_compressed_rows"))
+ goto err;
+ }
+ else
+ {
+ if (my_b_printf(&print_event_info->head_cache,
+ "ERROR: uncompress delete_compressed_rows failed\n"))
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+
+bool Update_rows_log_event::print(FILE *file,
+ PRINT_EVENT_INFO* print_event_info)
+{
+ return Rows_log_event::print_helper(file, print_event_info, "Update_rows");
+}
+
+bool
+Update_rows_compressed_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ char *new_buf;
+ ulong len;
+ bool is_malloc= false;
+ if(!row_log_event_uncompress(glob_description_event,
+ checksum_alg == BINLOG_CHECKSUM_ALG_CRC32,
+ temp_buf, UINT_MAX32, NULL, 0, &is_malloc, &new_buf, &len))
+ {
+ free_temp_buf();
+ register_temp_buf(new_buf, true);
+ if (Rows_log_event::print_helper(file, print_event_info,
+ "Update_compressed_rows"))
+ goto err;
+ }
+ else
+ {
+ if (my_b_printf(&print_event_info->head_cache,
+ "ERROR: uncompress update_compressed_rows failed\n"))
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+
+bool Incident_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ Write_on_release_cache cache(&print_event_info->head_cache, file);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\n# Incident: %s\nRELOAD DATABASE; # Shall generate syntax error\n", description()))
+ return 1;
+ return cache.flush_data();
+}
+
+
+/* Print for its unrecognized ignorable event */
+bool Ignorable_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ if (print_event_info->short_form)
+ return 0;
+
+ if (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))
+ return 1;
+ return 0;
+}
+
+
+/**
+ The default values for these variables should be values that are
+ *incorrect*, i.e., values that cannot occur in an event. This way,
+ they will always be printed for the first event.
+*/
+st_print_event_info::st_print_event_info()
+{
+ myf const flags = MYF(MY_WME | MY_NABP);
+ /*
+ Currently we only use static PRINT_EVENT_INFO objects, so zeroed at
+ program's startup, but these explicit bzero() is for the day someone
+ creates dynamic instances.
+ */
+ bzero(db, sizeof(db));
+ bzero(charset, sizeof(charset));
+ bzero(time_zone_str, sizeof(time_zone_str));
+ delimiter[0]= ';';
+ delimiter[1]= 0;
+ flags2_inited= 0;
+ flags2= 0;
+ sql_mode_inited= 0;
+ row_events= 0;
+ sql_mode= 0;
+ auto_increment_increment= 0;
+ auto_increment_offset= 0;
+ charset_inited= 0;
+ lc_time_names_number= ~0;
+ charset_database_number= ILLEGAL_CHARSET_INFO_NUMBER;
+ thread_id= 0;
+ server_id= 0;
+ domain_id= 0;
+ thread_id_printed= false;
+ server_id_printed= false;
+ domain_id_printed= false;
+ allow_parallel= true;
+ allow_parallel_printed= false;
+ found_row_event= false;
+ print_row_count= false;
+ short_form= false;
+ skip_replication= 0;
+ printed_fd_event=FALSE;
+ file= 0;
+ base64_output_mode=BASE64_OUTPUT_UNSPEC;
+ open_cached_file(&head_cache, NULL, NULL, 0, flags);
+ open_cached_file(&body_cache, NULL, NULL, 0, flags);
+ open_cached_file(&tail_cache, NULL, NULL, 0, flags);
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+ open_cached_file(&review_sql_cache, NULL, NULL, 0, flags);
+#endif
+}
+
+
+bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to)
+{
+ reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE);
+ if (cache->end_of_file > SIZE_T_MAX ||
+ !(to->str= (char*) my_malloc(PSI_NOT_INSTRUMENTED, (to->length= (size_t)cache->end_of_file), MYF(0))))
+ {
+ perror("Out of memory: can't allocate memory in copy_event_cache_to_string_and_reinit().");
+ goto err;
+ }
+ if (my_b_read(cache, (uchar*) to->str, to->length))
+ {
+ my_free(to->str);
+ perror("Can't read data from IO_CACHE");
+ return true;
+ }
+ reinit_io_cache(cache, WRITE_CACHE, 0, FALSE, TRUE);
+ return false;
+
+err:
+ to->str= 0;
+ to->length= 0;
+ return true;
+}
+
+
+bool
+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, this);
+ char buf[21];
+ char buf2[21];
+
+ if (!print_event_info->short_form && !is_flashback)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ longlong10_to_str(seq_no, buf, 10);
+ if (my_b_printf(&cache, "\tGTID %u-%u-%s", domain_id, server_id, buf))
+ goto err;
+ if (flags2 & FL_GROUP_COMMIT_ID)
+ {
+ longlong10_to_str(commit_id, buf2, 10);
+ if (my_b_printf(&cache, " cid=%s", buf2))
+ goto err;
+ }
+ if (flags2 & FL_DDL)
+ if (my_b_write_string(&cache, " ddl"))
+ goto err;
+ if (flags2 & FL_TRANSACTIONAL)
+ if (my_b_write_string(&cache, " trans"))
+ goto err;
+ if (flags2 & FL_WAITED)
+ if (my_b_write_string(&cache, " waited"))
+ goto err;
+ if (my_b_printf(&cache, "\n"))
+ goto err;
+
+ if (!print_event_info->allow_parallel_printed ||
+ print_event_info->allow_parallel != !!(flags2 & FL_ALLOW_PARALLEL))
+ {
+ if (my_b_printf(&cache,
+ "/*!100101 SET @@session.skip_parallel_replication=%u*/%s\n",
+ !(flags2 & FL_ALLOW_PARALLEL),
+ print_event_info->delimiter))
+ goto err;
+ print_event_info->allow_parallel= !!(flags2 & FL_ALLOW_PARALLEL);
+ print_event_info->allow_parallel_printed= true;
+ }
+
+ if (!print_event_info->domain_id_printed ||
+ print_event_info->domain_id != domain_id)
+ {
+ if (my_b_printf(&cache,
+ "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n",
+ domain_id, print_event_info->delimiter))
+ goto err;
+ 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)
+ {
+ if (my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n",
+ server_id, print_event_info->delimiter))
+ goto err;
+ print_event_info->server_id= server_id;
+ print_event_info->server_id_printed= true;
+ }
+
+ if (!is_flashback)
+ if (my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
+ buf, print_event_info->delimiter))
+ goto err;
+ }
+ if ((flags2 & FL_PREPARED_XA) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START ");
+ xid.serialize();
+ my_b_write(&cache, (uchar*) xid.buf, strlen(xid.buf));
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" :
+ "START TRANSACTION\n%s\n", print_event_info->delimiter))
+ goto err;
+ }
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+
+bool XA_prepare_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, this);
+ m_xid.serialize();
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", m_xid.buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ m_xid.buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index c71a1f39e28..10d1df4f3e7 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -32,6 +32,8 @@
#include "rpl_record_old.h"
#include "transaction.h"
+PSI_memory_key key_memory_log_event_old;
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
// Old implementation of do_apply_event()
@@ -899,7 +901,7 @@ int Delete_rows_log_event_old::do_before_row_operations(TABLE *table)
if (table->s->keys > 0)
{
- m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ m_memory= (uchar*) my_multi_malloc(key_memory_log_event_old, MYF(MY_WME),
&m_after_image,
(uint) table->s->reclength,
&m_key,
@@ -908,7 +910,7 @@ int Delete_rows_log_event_old::do_before_row_operations(TABLE *table)
}
else
{
- m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
+ m_after_image= (uchar*) my_malloc(key_memory_log_event_old, table->s->reclength, MYF(MY_WME));
m_memory= (uchar*)m_after_image;
m_key= NULL;
}
@@ -997,7 +999,7 @@ int Update_rows_log_event_old::do_before_row_operations(TABLE *table)
if (table->s->keys > 0)
{
- m_memory= (uchar*) my_multi_malloc(MYF(MY_WME),
+ m_memory= (uchar*) my_multi_malloc(key_memory_log_event_old, MYF(MY_WME),
&m_after_image,
(uint) table->s->reclength,
&m_key,
@@ -1006,7 +1008,7 @@ int Update_rows_log_event_old::do_before_row_operations(TABLE *table)
}
else
{
- m_after_image= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME));
+ m_after_image= (uchar*) my_malloc(key_memory_log_event_old, table->s->reclength, MYF(MY_WME));
m_memory= m_after_image;
m_key= NULL;
}
@@ -1252,7 +1254,7 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
m_table_id, m_flags, m_width, data_size));
DBUG_DUMP("rows_data", (uchar*) ptr_rows_data, data_size);
- m_rows_buf= (uchar*) my_malloc(data_size, MYF(MY_WME));
+ m_rows_buf= (uchar*) my_malloc(key_memory_log_event_old, data_size, MYF(MY_WME));
if (likely((bool)m_rows_buf))
{
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
@@ -1326,7 +1328,7 @@ int Old_rows_log_event::do_add_row_data(uchar *row_data, size_t length)
my_ptrdiff_t const new_alloc=
block_size * ((cur_size + length + block_size - 1) / block_size);
- uchar* const new_buf= (uchar*)my_realloc((uchar*)m_rows_buf, (uint) new_alloc,
+ uchar* const new_buf= (uchar*)my_realloc(key_memory_log_event_old, (uchar*)m_rows_buf, (uint) new_alloc,
MYF(MY_ALLOW_ZERO_PTR|MY_WME));
if (unlikely(!new_buf))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
@@ -2557,7 +2559,7 @@ Delete_rows_log_event_old::do_before_row_operations(const Slave_reporting_capabi
if (m_table->s->keys > 0)
{
// Allocate buffer for key searches
- m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ m_key= (uchar*)my_malloc(key_memory_log_event_old, m_table->key_info->key_length, MYF(MY_WME));
if (!m_key)
return HA_ERR_OUT_OF_MEM;
}
@@ -2655,7 +2657,7 @@ Update_rows_log_event_old::do_before_row_operations(const Slave_reporting_capabi
if (m_table->s->keys > 0)
{
// Allocate buffer for key searches
- m_key= (uchar*)my_malloc(m_table->key_info->key_length, MYF(MY_WME));
+ m_key= (uchar*)my_malloc(key_memory_log_event_old, m_table->key_info->key_length, MYF(MY_WME));
if (!m_key)
return HA_ERR_OUT_OF_MEM;
}
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
new file mode 100644
index 00000000000..dd7e166059a
--- /dev/null
+++ b/sql/log_event_server.cc
@@ -0,0 +1,8524 @@
+/*
+ Copyright (c) 2000, 2019, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2020, 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
+ 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-1335 USA */
+
+
+#include "mariadb.h"
+#include "sql_priv.h"
+
+#ifdef MYSQL_CLIENT
+#error MYSQL_CLIENT must not be defined here
+#endif
+
+#ifndef MYSQL_SERVER
+#error MYSQL_SERVER must be defined here
+#endif
+
+#include "unireg.h"
+#include "log_event.h"
+#include "sql_base.h" // close_thread_tables
+#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE
+#include "sql_locale.h" // MY_LOCALE, my_locale_by_number, my_locale_en_US
+#include "key.h" // key_copy
+#include "lock.h" // mysql_unlock_tables
+#include "sql_parse.h" // mysql_test_parse_for_slave
+#include "tztime.h" // struct Time_zone
+#include "sql_load.h" // mysql_load
+#include "sql_db.h" // load_db_opt_by_name
+#include "slave.h"
+#include "rpl_rli.h"
+#include "rpl_mi.h"
+#include "rpl_filter.h"
+#include "rpl_record.h"
+#include "transaction.h"
+#include <my_dir.h>
+#include "sql_show.h" // append_identifier
+#include "debug_sync.h" // debug_sync
+#include <mysql/psi/mysql_statement.h>
+#include <strfunc.h>
+#include "compat56.h"
+#include "wsrep_mysqld.h"
+#include "sql_insert.h"
+
+#include <my_bitmap.h>
+#include "rpl_utility.h"
+#include "rpl_constants.h"
+#include "sql_digest.h"
+#include "zlib.h"
+
+
+#define log_cs &my_charset_latin1
+
+
+#if defined(HAVE_REPLICATION)
+static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd);
+
+static const char *HA_ERR(int i)
+{
+ /*
+ This function should only be called in case of an error
+ was detected
+ */
+ DBUG_ASSERT(i != 0);
+ switch (i) {
+ case HA_ERR_KEY_NOT_FOUND: return "HA_ERR_KEY_NOT_FOUND";
+ case HA_ERR_FOUND_DUPP_KEY: return "HA_ERR_FOUND_DUPP_KEY";
+ case HA_ERR_RECORD_CHANGED: return "HA_ERR_RECORD_CHANGED";
+ case HA_ERR_WRONG_INDEX: return "HA_ERR_WRONG_INDEX";
+ case HA_ERR_CRASHED: return "HA_ERR_CRASHED";
+ case HA_ERR_WRONG_IN_RECORD: return "HA_ERR_WRONG_IN_RECORD";
+ case HA_ERR_OUT_OF_MEM: return "HA_ERR_OUT_OF_MEM";
+ case HA_ERR_NOT_A_TABLE: return "HA_ERR_NOT_A_TABLE";
+ case HA_ERR_WRONG_COMMAND: return "HA_ERR_WRONG_COMMAND";
+ case HA_ERR_OLD_FILE: return "HA_ERR_OLD_FILE";
+ case HA_ERR_NO_ACTIVE_RECORD: return "HA_ERR_NO_ACTIVE_RECORD";
+ case HA_ERR_RECORD_DELETED: return "HA_ERR_RECORD_DELETED";
+ case HA_ERR_RECORD_FILE_FULL: return "HA_ERR_RECORD_FILE_FULL";
+ case HA_ERR_INDEX_FILE_FULL: return "HA_ERR_INDEX_FILE_FULL";
+ case HA_ERR_END_OF_FILE: return "HA_ERR_END_OF_FILE";
+ case HA_ERR_UNSUPPORTED: return "HA_ERR_UNSUPPORTED";
+ case HA_ERR_TO_BIG_ROW: return "HA_ERR_TO_BIG_ROW";
+ case HA_WRONG_CREATE_OPTION: return "HA_WRONG_CREATE_OPTION";
+ case HA_ERR_FOUND_DUPP_UNIQUE: return "HA_ERR_FOUND_DUPP_UNIQUE";
+ case HA_ERR_UNKNOWN_CHARSET: return "HA_ERR_UNKNOWN_CHARSET";
+ case HA_ERR_WRONG_MRG_TABLE_DEF: return "HA_ERR_WRONG_MRG_TABLE_DEF";
+ case HA_ERR_CRASHED_ON_REPAIR: return "HA_ERR_CRASHED_ON_REPAIR";
+ case HA_ERR_CRASHED_ON_USAGE: return "HA_ERR_CRASHED_ON_USAGE";
+ case HA_ERR_LOCK_WAIT_TIMEOUT: return "HA_ERR_LOCK_WAIT_TIMEOUT";
+ case HA_ERR_LOCK_TABLE_FULL: return "HA_ERR_LOCK_TABLE_FULL";
+ case HA_ERR_READ_ONLY_TRANSACTION: return "HA_ERR_READ_ONLY_TRANSACTION";
+ case HA_ERR_LOCK_DEADLOCK: return "HA_ERR_LOCK_DEADLOCK";
+ case HA_ERR_CANNOT_ADD_FOREIGN: return "HA_ERR_CANNOT_ADD_FOREIGN";
+ case HA_ERR_NO_REFERENCED_ROW: return "HA_ERR_NO_REFERENCED_ROW";
+ case HA_ERR_ROW_IS_REFERENCED: return "HA_ERR_ROW_IS_REFERENCED";
+ case HA_ERR_NO_SAVEPOINT: return "HA_ERR_NO_SAVEPOINT";
+ case HA_ERR_NON_UNIQUE_BLOCK_SIZE: return "HA_ERR_NON_UNIQUE_BLOCK_SIZE";
+ case HA_ERR_NO_SUCH_TABLE: return "HA_ERR_NO_SUCH_TABLE";
+ case HA_ERR_TABLE_EXIST: return "HA_ERR_TABLE_EXIST";
+ case HA_ERR_NO_CONNECTION: return "HA_ERR_NO_CONNECTION";
+ case HA_ERR_NULL_IN_SPATIAL: return "HA_ERR_NULL_IN_SPATIAL";
+ case HA_ERR_TABLE_DEF_CHANGED: return "HA_ERR_TABLE_DEF_CHANGED";
+ case HA_ERR_NO_PARTITION_FOUND: return "HA_ERR_NO_PARTITION_FOUND";
+ case HA_ERR_RBR_LOGGING_FAILED: return "HA_ERR_RBR_LOGGING_FAILED";
+ case HA_ERR_DROP_INDEX_FK: return "HA_ERR_DROP_INDEX_FK";
+ case HA_ERR_FOREIGN_DUPLICATE_KEY: return "HA_ERR_FOREIGN_DUPLICATE_KEY";
+ case HA_ERR_TABLE_NEEDS_UPGRADE: return "HA_ERR_TABLE_NEEDS_UPGRADE";
+ case HA_ERR_TABLE_READONLY: return "HA_ERR_TABLE_READONLY";
+ case HA_ERR_AUTOINC_READ_FAILED: return "HA_ERR_AUTOINC_READ_FAILED";
+ case HA_ERR_AUTOINC_ERANGE: return "HA_ERR_AUTOINC_ERANGE";
+ case HA_ERR_GENERIC: return "HA_ERR_GENERIC";
+ case HA_ERR_RECORD_IS_THE_SAME: return "HA_ERR_RECORD_IS_THE_SAME";
+ case HA_ERR_LOGGING_IMPOSSIBLE: return "HA_ERR_LOGGING_IMPOSSIBLE";
+ case HA_ERR_CORRUPT_EVENT: return "HA_ERR_CORRUPT_EVENT";
+ case HA_ERR_ROWS_EVENT_APPLY : return "HA_ERR_ROWS_EVENT_APPLY";
+ }
+ 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->speculation == rpl_group_info::SPECULATE_OPTIMISTIC)
+ return true;
+ 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
+
+ @param level error, warning or info
+ @param ha_error HA_ERR_ code
+ @param rli pointer to the active Relay_log_info instance
+ @param thd pointer to the slave thread's thd
+ @param table pointer to the event's table object
+ @param type the type of the event
+ @param log_name the master binlog file name
+ @param pos the master binlog file pos (the next after the event)
+
+*/
+static void inline slave_rows_error_report(enum loglevel level, int ha_error,
+ rpl_group_info *rgi, THD *thd,
+ TABLE *table, const char * type,
+ const char *log_name, my_off_t pos)
+{
+ const char *handler_error= (ha_error ? HA_ERR(ha_error) : NULL);
+ char buff[MAX_SLAVE_ERRMSG], *slider;
+ const char *buff_end= buff + sizeof(buff);
+ size_t len;
+ 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++)
+ {
+ len= my_snprintf(slider, buff_end - slider,
+ " %s, Error_code: %d;", err->get_message_text(),
+ err->get_sql_errno());
+ }
+
+ if (ha_error != 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 %llu",
+ type, table->s->db.str, table->s->table_name.str,
+ buff, handler_error == NULL ? "<unknown>" : handler_error,
+ log_name, pos);
+ else
+ 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 %llu",
+ type, table->s->db.str, table->s->table_name.str,
+ buff, log_name, pos);
+}
+#endif
+
+#if defined(HAVE_REPLICATION)
+static void set_thd_db(THD *thd, Rpl_filter *rpl_filter,
+ const char *db, uint32 db_len)
+{
+ char lcase_db_buf[NAME_LEN +1];
+ LEX_CSTRING new_db;
+ new_db.length= db_len;
+ if (lower_case_table_names == 1)
+ {
+ strmov(lcase_db_buf, db);
+ my_casedn_str(system_charset_info, lcase_db_buf);
+ new_db.str= lcase_db_buf;
+ }
+ else
+ new_db.str= db;
+ /* TODO WARNING this makes rewrite_db respect lower_case_table_names values
+ * for more info look MDEV-17446 */
+ new_db.str= rpl_filter->get_rewrite_db(new_db.str, &new_db.length);
+ thd->set_db(&new_db);
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+
+inline int idempotent_error_code(int err_code)
+{
+ int ret= 0;
+
+ switch (err_code)
+ {
+ case 0:
+ ret= 1;
+ break;
+ /*
+ The following list of "idempotent" errors
+ means that an error from the list might happen
+ because of idempotent (more than once)
+ applying of a binlog file.
+ Notice, that binlog has a ddl operation its
+ second applying may cause
+
+ case HA_ERR_TABLE_DEF_CHANGED:
+ case HA_ERR_CANNOT_ADD_FOREIGN:
+
+ which are not included into to the list.
+
+ Note that HA_ERR_RECORD_DELETED is not in the list since
+ do_exec_row() should not return that error code.
+ */
+ case HA_ERR_RECORD_CHANGED:
+ case HA_ERR_KEY_NOT_FOUND:
+ case HA_ERR_END_OF_FILE:
+ case HA_ERR_FOUND_DUPP_KEY:
+ case HA_ERR_FOUND_DUPP_UNIQUE:
+ case HA_ERR_FOREIGN_DUPLICATE_KEY:
+ case HA_ERR_NO_REFERENCED_ROW:
+ case HA_ERR_ROW_IS_REFERENCED:
+ ret= 1;
+ break;
+ default:
+ ret= 0;
+ break;
+ }
+ return (ret);
+}
+
+/**
+ Ignore error code specified on command line.
+*/
+
+inline int ignored_error_code(int err_code)
+{
+ if (use_slave_mask && bitmap_is_set(&slave_error_mask, err_code))
+ {
+ statistic_increment(slave_skipped_errors, LOCK_status);
+ return 1;
+ }
+ return err_code == ER_SLAVE_IGNORED_TABLE;
+}
+
+/*
+ This function converts an engine's error to a server error.
+
+ If the thread does not have an error already reported, it tries to
+ define it by calling the engine's method print_error. However, if a
+ mapping is not found, it uses the ER_UNKNOWN_ERROR and prints out a
+ warning message.
+*/
+int convert_handler_error(int error, THD* thd, TABLE *table)
+{
+ 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->get_stmt_da()->sql_errno() :
+ ER_UNKNOWN_ERROR);
+ if (actual_error == ER_UNKNOWN_ERROR)
+ if (global_system_variables.log_warnings)
+ sql_print_warning("Unknown error detected %d in handler", error);
+ }
+
+ return (actual_error);
+}
+
+inline bool concurrency_error_code(int error)
+{
+ switch (error)
+ {
+ case ER_LOCK_WAIT_TIMEOUT:
+ case ER_LOCK_DEADLOCK:
+ case ER_XA_RBDEADLOCK:
+ return TRUE;
+ default:
+ return (FALSE);
+ }
+}
+
+inline bool unexpected_error_code(int unexpected_error)
+{
+ switch (unexpected_error)
+ {
+ case ER_NET_READ_ERROR:
+ case ER_NET_ERROR_ON_WRITE:
+ case ER_QUERY_INTERRUPTED:
+ case ER_STATEMENT_TIMEOUT:
+ case ER_CONNECTION_KILLED:
+ case ER_SERVER_SHUTDOWN:
+ case ER_NEW_ABORTING_CONNECTION:
+ return(TRUE);
+ default:
+ return(FALSE);
+ }
+}
+
+/*
+ pretty_print_str()
+*/
+
+static void
+pretty_print_str(String *packet, const char *str, int len)
+{
+ const char *end= str + len;
+ packet->append(STRING_WITH_LEN("'"));
+ while (str < end)
+ {
+ char c;
+ switch ((c=*str++)) {
+ case '\n': packet->append(STRING_WITH_LEN("\\n")); break;
+ case '\r': packet->append(STRING_WITH_LEN("\\r")); break;
+ case '\\': packet->append(STRING_WITH_LEN("\\\\")); break;
+ case '\b': packet->append(STRING_WITH_LEN("\\b")); break;
+ case '\t': packet->append(STRING_WITH_LEN("\\t")); break;
+ case '\'': packet->append(STRING_WITH_LEN("\\'")); break;
+ case 0 : packet->append(STRING_WITH_LEN("\\0")); break;
+ default:
+ packet->append(&c, 1);
+ break;
+ }
+ }
+ packet->append(STRING_WITH_LEN("'"));
+}
+#endif /* HAVE_REPLICATION */
+
+
+#if defined(HAVE_REPLICATION)
+
+/**
+ 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_CSTRING *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 ext Extension for file name
+
+ @return
+ Pointer to start of extension
+*/
+
+static char *slave_load_file_stem(char *buf, uint file_id,
+ int event_server_id, const char *ext,
+ LEX_CSTRING *connection_name)
+{
+ char *res;
+ res= buf+ unpack_dirname(buf, slave_load_tmpdir);
+ to_unix_path(buf);
+ 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
+ return res; // Pointer to extension
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+
+/**
+ Delete all temporary files used for SQL_LOAD.
+*/
+
+static void cleanup_load_tmpdir(LEX_CSTRING *connection_name)
+{
+ MY_DIR *dirp;
+ FILEINFO *file;
+ uint i;
+ char dir[FN_REFLEN], fname[FN_REFLEN];
+ char prefbuf[31 + MAX_CONNECTION_NAME* MAX_FILENAME_MBWIDTH + 1];
+ DBUG_ENTER("cleanup_load_tmpdir");
+
+ unpack_dirname(dir, slave_load_tmpdir);
+ if (!(dirp=my_dir(dir, MYF(MY_WME))))
+ return;
+
+ /*
+ When we are deleting temporary files, we should only remove
+ the files associated with the server id of our server.
+ We don't use event_server_id here because since we've disabled
+ direct binlogging of Create_file/Append_file/Exec_load events
+ we cannot meet Start_log event in the middle of events from one
+ LOAD DATA.
+ */
+
+ 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))
+ {
+ fn_format(fname,file->name,slave_load_tmpdir,"",MY_UNPACK_FILENAME);
+ mysql_file_delete(key_file_misc, fname, MYF(0));
+ }
+ }
+
+ my_dirend(dirp);
+ DBUG_VOID_RETURN;
+}
+#endif
+
+
+/**
+ 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(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 + 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, str, len);
+ else
+ {
+ *ptr++= '\'';
+ if (!no_backslash)
+ {
+ ptr+= escape_string_for_mysql(csinfo, ptr, 0, str, len);
+ }
+ else
+ {
+ const char *frm_str= str;
+
+ for (; frm_str < (str + len); frm_str++)
+ {
+ /* Using '' way to represent "'" */
+ if (*frm_str == '\'')
+ *ptr++= *frm_str;
+
+ *ptr++= *frm_str;
+ }
+ }
+
+ *ptr++= '\'';
+ }
+ to->length((uint32)(orig_len + ptr - beg));
+ return 0;
+}
+
+
+/**************************************************************************
+ Log_event methods (= the parent class of all events)
+**************************************************************************/
+
+Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
+ :log_pos(0), temp_buf(0), exec_time(0), thd(thd_arg),
+ checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
+{
+ server_id= thd->variables.server_id;
+ when= thd->start_time;
+ when_sec_part=thd->start_time_sec_part;
+
+ if (using_trans)
+ cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
+ else
+ cache_type= Log_event::EVENT_STMT_CACHE;
+ flags= flags_arg |
+ (thd->variables.option_bits & OPTION_SKIP_REPLICATION ?
+ LOG_EVENT_SKIP_REPLICATION_F : 0);
+}
+
+/**
+ This minimal constructor is for when you are not even sure that there
+ is a valid THD. For example in the server when we are shutting down or
+ flushing logs after receiving a SIGHUP (then we must write a Rotate to
+ the binlog but we have no THD, so we need this minimal constructor).
+*/
+
+Log_event::Log_event()
+ :temp_buf(0), exec_time(0), flags(0), cache_type(EVENT_INVALID_CACHE),
+ thd(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
+{
+ 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
+ */
+ when= 0;
+ when_sec_part=0;
+ log_pos= 0;
+}
+
+
+
+#ifdef HAVE_REPLICATION
+
+int Log_event::do_update_pos(rpl_group_info *rgi)
+{
+ Relay_log_info *rli= rgi->rli;
+ DBUG_ENTER("Log_event::do_update_pos");
+
+ DBUG_ASSERT(!rli->belongs_to_client());
+ /*
+ 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
+ Execute_load_log_event::do_apply_event. In this case, we don't
+ do anything here ; Execute_load_log_event::do_apply_event will
+ call Log_event::do_apply_event again later with the proper rli.
+ Strictly speaking, if we were sure that rli is null only in the
+ case discussed above, 'if (rli)' is useless here. But as we are
+ not 100% sure, keep it for now.
+
+ Matz: I don't think we will need this check with this refactoring.
+ */
+ if (rli)
+ {
+ /*
+ 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.
+ */
+ if (!rgi->is_parallel_exec || is_group_event(get_type_code()))
+ rli->stmt_done(log_pos, thd, rgi);
+ }
+ DBUG_RETURN(0); // Cannot fail currently
+}
+
+
+Log_event::enum_skip_reason
+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 == 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))
+ return EVENT_SKIP_IGNORE;
+ if (rli->slave_skip_counter > 0)
+ return EVENT_SKIP_COUNT;
+ return EVENT_SKIP_NOT;
+}
+
+
+/*
+ Log_event::pack_info()
+*/
+
+void Log_event::pack_info(Protocol *protocol)
+{
+ protocol->store("", &my_charset_bin);
+}
+
+
+/**
+ Only called by SHOW BINLOG EVENTS
+*/
+int Log_event::net_send(Protocol *protocol, const char* log_name, my_off_t pos)
+{
+ const char *p= strrchr(log_name, FN_LIBCHAR);
+ const char *event_type;
+ if (p)
+ log_name = p + 1;
+
+ protocol->prepare_for_resend();
+ protocol->store(log_name, &my_charset_bin);
+ protocol->store((ulonglong) pos);
+ event_type = get_type_str();
+ protocol->store(event_type, strlen(event_type), &my_charset_bin);
+ protocol->store((uint32) server_id);
+ protocol->store((ulonglong) log_pos);
+ pack_info(protocol);
+ return protocol->write();
+}
+#endif /* HAVE_REPLICATION */
+
+
+/**
+ init_show_field_list() prepares the column names and types for the
+ output of SHOW BINLOG EVENTS; it is used only by SHOW BINLOG
+ EVENTS.
+*/
+
+void Log_event::init_show_field_list(THD *thd, List<Item>* field_list)
+{
+ MEM_ROOT *mem_root= thd->mem_root;
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Log_name", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Pos",
+ MY_INT64_NUM_DECIMAL_DIGITS,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_empty_string(thd, "Event_type", 20),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "Server_id", 10,
+ MYSQL_TYPE_LONG),
+ mem_root);
+ field_list->push_back(new (mem_root)
+ Item_return_int(thd, "End_log_pos",
+ MY_INT64_NUM_DECIMAL_DIGITS,
+ MYSQL_TYPE_LONGLONG),
+ mem_root);
+ field_list->push_back(new (mem_root) Item_empty_string(thd, "Info", 20),
+ mem_root);
+}
+
+/**
+ A decider of whether to trigger checksum computation or not.
+ To be invoked in Log_event::write() stack.
+ The decision is positive
+
+ S,M) if it's been marked for checksumming with @c checksum_alg
+
+ M) otherwise, if @@global.binlog_checksum is not NONE and the event is
+ directly written to the binlog file.
+ The to-be-cached event decides at @c write_cache() time.
+
+ Otherwise the decision is negative.
+
+ @note A side effect of the method is altering Log_event::checksum_alg
+ it the latter was undefined at calling.
+
+ @return true Checksum should be used. Log_event::checksum_alg is set.
+ @return false No checksum
+*/
+
+my_bool Log_event::need_checksum()
+{
+ my_bool ret;
+ DBUG_ENTER("Log_event::need_checksum");
+
+ /*
+ few callers of Log_event::write
+ (incl FD::write, FD constructing code on the slave side, Rotate relay log
+ and Stop event)
+ provides their checksum alg preference through Log_event::checksum_alg.
+ */
+ if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ ret= checksum_alg != BINLOG_CHECKSUM_ALG_OFF;
+ else
+ {
+ ret= binlog_checksum_options && cache_type == Log_event::EVENT_NO_CACHE;
+ checksum_alg= ret ? (enum_binlog_checksum_alg)binlog_checksum_options
+ : 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
+ call (and therefore data_written is not zero) then `ret' must be
+ TRUE. It may not be null because FD is always checksummed.
+ */
+
+ DBUG_ASSERT(get_type_code() != FORMAT_DESCRIPTION_EVENT || ret ||
+ data_written == 0);
+
+ DBUG_ASSERT(!ret ||
+ ((checksum_alg == binlog_checksum_options ||
+ /*
+ Stop event closes the relay-log and its checksum alg
+ preference is set by the caller can be different
+ from the server's binlog_checksum_options.
+ */
+ get_type_code() == STOP_EVENT ||
+ /*
+ Rotate:s can be checksummed regardless of the server's
+ binlog_checksum_options. That applies to both
+ the local RL's Rotate and the master's Rotate
+ which IO thread instantiates via queue_binlog_ver_3_event.
+ */
+ get_type_code() == ROTATE_EVENT ||
+ get_type_code() == START_ENCRYPTION_EVENT ||
+ /* FD is always checksummed */
+ get_type_code() == FORMAT_DESCRIPTION_EVENT) &&
+ checksum_alg != BINLOG_CHECKSUM_ALG_OFF));
+
+ DBUG_ASSERT(checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
+
+ DBUG_ASSERT(((get_type_code() != ROTATE_EVENT &&
+ get_type_code() != STOP_EVENT) ||
+ get_type_code() != FORMAT_DESCRIPTION_EVENT) ||
+ cache_type == Log_event::EVENT_NO_CACHE);
+
+ DBUG_RETURN(ret);
+}
+
+int Log_event_writer::write_internal(const uchar *pos, size_t len)
+{
+ DBUG_ASSERT(!ctx || encrypt_or_write == &Log_event_writer::encrypt_and_write);
+ if (my_b_safe_write(file, pos, len))
+ {
+ DBUG_PRINT("error", ("write to log failed: %d", my_errno));
+ return 1;
+ }
+ bytes_written+= len;
+ return 0;
+}
+
+/*
+ as soon as encryption produces the first output block, write event_len
+ where it should be in a valid event header
+*/
+int Log_event_writer::maybe_write_event_len(uchar *pos, size_t len)
+{
+ if (len && event_len)
+ {
+ DBUG_ASSERT(len >= EVENT_LEN_OFFSET);
+ if (write_internal(pos + EVENT_LEN_OFFSET - 4, 4))
+ return 1;
+ int4store(pos + EVENT_LEN_OFFSET - 4, event_len);
+ event_len= 0;
+ }
+ return 0;
+}
+
+int Log_event_writer::encrypt_and_write(const uchar *pos, size_t len)
+{
+ uchar *dst;
+ size_t dstsize;
+ uint dstlen;
+ int res; // Safe as res is always set
+ DBUG_ASSERT(ctx);
+
+ if (!len)
+ return 0;
+
+ dstsize= encryption_encrypted_length((uint)len, ENCRYPTION_KEY_SYSTEM_DATA,
+ crypto->key_version);
+ if (!(dst= (uchar*)my_safe_alloca(dstsize)))
+ return 1;
+
+ if (encryption_ctx_update(ctx, pos, (uint)len, dst, &dstlen))
+ {
+ res= 1;
+ goto err;
+ }
+
+ if (maybe_write_event_len(dst, dstlen))
+ {
+ res= 1;
+ goto err;
+ }
+
+ res= write_internal(dst, dstlen);
+
+err:
+ my_safe_afree(dst, dstsize);
+ return res;
+}
+
+int Log_event_writer::write_header(uchar *pos, size_t len)
+{
+ DBUG_ENTER("Log_event_writer::write_header");
+ /*
+ recording checksum of FD event computed with dropped
+ possibly active LOG_EVENT_BINLOG_IN_USE_F flag.
+ Similar step at verication: the active flag is dropped before
+ checksum computing.
+ */
+ if (checksum_len)
+ {
+ uchar save=pos[FLAGS_OFFSET];
+ pos[FLAGS_OFFSET]&= ~LOG_EVENT_BINLOG_IN_USE_F;
+ crc= my_checksum(0, pos, len);
+ pos[FLAGS_OFFSET]= save;
+ }
+
+ if (ctx)
+ {
+ uchar iv[BINLOG_IV_LENGTH];
+ crypto->set_iv(iv, (uint32)my_b_safe_tell(file));
+ if (encryption_ctx_init(ctx, crypto->key, crypto->key_length,
+ iv, sizeof(iv), ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD,
+ ENCRYPTION_KEY_SYSTEM_DATA, crypto->key_version))
+ DBUG_RETURN(1);
+
+ DBUG_ASSERT(len >= LOG_EVENT_HEADER_LEN);
+ event_len= uint4korr(pos + EVENT_LEN_OFFSET);
+ DBUG_ASSERT(event_len >= len);
+ memcpy(pos + EVENT_LEN_OFFSET, pos, 4);
+ pos+= 4;
+ len-= 4;
+ }
+ DBUG_RETURN((this->*encrypt_or_write)(pos, len));
+}
+
+int Log_event_writer::write_data(const uchar *pos, size_t len)
+{
+ DBUG_ENTER("Log_event_writer::write_data");
+ if (checksum_len)
+ crc= my_checksum(crc, pos, len);
+
+ DBUG_RETURN((this->*encrypt_or_write)(pos, len));
+}
+
+int Log_event_writer::write_footer()
+{
+ DBUG_ENTER("Log_event_writer::write_footer");
+ if (checksum_len)
+ {
+ uchar checksum_buf[BINLOG_CHECKSUM_LEN];
+ int4store(checksum_buf, crc);
+ if ((this->*encrypt_or_write)(checksum_buf, BINLOG_CHECKSUM_LEN))
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ if (ctx)
+ {
+ uint dstlen;
+ uchar dst[MY_AES_BLOCK_SIZE*2];
+ if (encryption_ctx_finish(ctx, dst, &dstlen))
+ DBUG_RETURN(1);
+ if (maybe_write_event_len(dst, dstlen) || write_internal(dst, dstlen))
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ DBUG_RETURN(0);
+}
+
+/*
+ Log_event::write_header()
+*/
+
+bool Log_event::write_header(size_t event_data_length)
+{
+ uchar header[LOG_EVENT_HEADER_LEN];
+ ulong now;
+ DBUG_ENTER("Log_event::write_header");
+ DBUG_PRINT("enter", ("filepos: %lld length: %zu type: %d",
+ (longlong) writer->pos(), event_data_length,
+ (int) get_type_code()));
+
+ writer->checksum_len= need_checksum() ? BINLOG_CHECKSUM_LEN : 0;
+
+ /* Store number of bytes that will be written by this event */
+ data_written= event_data_length + sizeof(header) + writer->checksum_len;
+
+ /*
+ log_pos != 0 if this is relay-log event. In this case we should not
+ change the position
+ */
+
+ if (is_artificial_event())
+ {
+ /*
+ Artificial events are automatically generated and do not exist
+ in master's binary log, so log_pos should be set to 0.
+ */
+ log_pos= 0;
+ }
+ else if (!log_pos)
+ {
+ /*
+ Calculate the position of where the next event will start
+ (end of this event, that is).
+ */
+
+ log_pos= writer->pos() + data_written;
+
+ DBUG_EXECUTE_IF("dbug_master_binlog_over_2GB", log_pos += (1ULL <<31););
+ }
+
+ now= get_time(); // Query start time
+
+ /*
+ Header will be of size LOG_EVENT_HEADER_LEN for all events, except for
+ FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be
+ LOG_EVENT_MINIMAL_HEADER_LEN (remember these 2 have a frozen header,
+ because we read them before knowing the format).
+ */
+
+ int4store(header, now); // timestamp
+ header[EVENT_TYPE_OFFSET]= get_type_code();
+ int4store(header+ SERVER_ID_OFFSET, server_id);
+ int4store(header+ EVENT_LEN_OFFSET, data_written);
+ int4store(header+ LOG_POS_OFFSET, log_pos);
+ int2store(header + FLAGS_OFFSET, flags);
+
+ bool ret= writer->write_header(header, sizeof(header));
+ DBUG_RETURN(ret);
+}
+
+
+
+#if defined(HAVE_REPLICATION)
+inline Log_event::enum_skip_reason
+Log_event::continue_group(rpl_group_info *rgi)
+{
+ if (rgi->rli->slave_skip_counter == 1)
+ return Log_event::EVENT_SKIP_IGNORE;
+ return Log_event::do_shall_skip(rgi);
+}
+#endif
+
+/**************************************************************************
+ Query_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+
+/**
+ This (which is used only for SHOW BINLOG EVENTS) could be updated to
+ print SET @@session_var=. But this is not urgent, as SHOW BINLOG EVENTS is
+ only an information, it does not produce suitable queries to replay (for
+ example it does not print LOAD DATA INFILE).
+ @todo
+ show the catalog ??
+*/
+
+void Query_log_event::pack_info(Protocol *protocol)
+{
+ // TODO: show the catalog ??
+ char buf_mem[1024];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.real_alloc(9 + db_len + q_len);
+ if (!(flags & LOG_EVENT_SUPPRESS_USE_F)
+ && db && db_len)
+ {
+ buf.append(STRING_WITH_LEN("use "));
+ append_identifier(protocol->thd, &buf, db, db_len);
+ buf.append(STRING_WITH_LEN("; "));
+ }
+
+ DBUG_ASSERT(!flags2 || flags2_inited);
+
+ if (flags2 & (OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_AUTO_IS_NULL |
+ OPTION_RELAXED_UNIQUE_CHECKS |
+ OPTION_NO_CHECK_CONSTRAINT_CHECKS |
+ OPTION_IF_EXISTS))
+ {
+ buf.append(STRING_WITH_LEN("set "));
+ if (flags2 & OPTION_NO_FOREIGN_KEY_CHECKS)
+ buf.append(STRING_WITH_LEN("foreign_key_checks=1, "));
+ if (flags2 & OPTION_AUTO_IS_NULL)
+ buf.append(STRING_WITH_LEN("sql_auto_is_null, "));
+ if (flags2 & OPTION_RELAXED_UNIQUE_CHECKS)
+ buf.append(STRING_WITH_LEN("unique_checks=1, "));
+ if (flags2 & OPTION_NO_CHECK_CONSTRAINT_CHECKS)
+ buf.append(STRING_WITH_LEN("check_constraint_checks=1, "));
+ if (flags2 & OPTION_IF_EXISTS)
+ buf.append(STRING_WITH_LEN("@@sql_if_exists=1, "));
+ buf[buf.length()-2]=';';
+ }
+ if (query && q_len)
+ buf.append(query, q_len);
+ protocol->store(&buf);
+}
+#endif
+
+
+/**
+ Utility function for the next method (Query_log_event::write()) .
+*/
+static void store_str_with_code_and_len(uchar **dst, const char *src,
+ uint len, uint code)
+{
+ /*
+ only 1 byte to store the length of catalog, so it should not
+ surpass 255
+ */
+ DBUG_ASSERT(len <= 255);
+ DBUG_ASSERT(src);
+ *((*dst)++)= (uchar) code;
+ *((*dst)++)= (uchar) len;
+ bmove(*dst, src, len);
+ (*dst)+= len;
+}
+
+
+/**
+ Query_log_event::write().
+
+ @note
+ In this event we have to modify the header to have the correct
+ EVENT_LEN_OFFSET as we don't yet know how many status variables we
+ will print!
+*/
+
+bool Query_log_event::write()
+{
+ uchar buf[QUERY_HEADER_LEN + MAX_SIZE_LOG_EVENT_STATUS];
+ uchar *start, *start_of_status;
+ ulong event_length;
+
+ if (!query)
+ return 1; // Something wrong with event
+
+ /*
+ We want to store the thread id:
+ (- as an information for the user when he reads the binlog)
+ - if the query uses temporary table: for the slave SQL thread to know to
+ which master connection the temp table belongs.
+ Now imagine we (write()) are called by the slave SQL thread (we are
+ logging a query executed by this thread; the slave runs with
+ --log-slave-updates). Then this query will be logged with
+ thread_id=the_thread_id_of_the_SQL_thread. Imagine that 2 temp tables of
+ the same name were created simultaneously on the master (in the master
+ binlog you have
+ CREATE TEMPORARY TABLE t; (thread 1)
+ CREATE TEMPORARY TABLE t; (thread 2)
+ ...)
+ then in the slave's binlog there will be
+ CREATE TEMPORARY TABLE t; (thread_id_of_the_slave_SQL_thread)
+ CREATE TEMPORARY TABLE t; (thread_id_of_the_slave_SQL_thread)
+ which is bad (same thread id!).
+
+ To avoid this, we log the thread's thread id EXCEPT for the SQL
+ slave thread for which we log the original (master's) thread id.
+ Now this moves the bug: what happens if the thread id on the
+ master was 10 and when the slave replicates the query, a
+ connection number 10 is opened by a normal client on the slave,
+ and updates a temp table of the same name? We get a problem
+ again. To avoid this, in the handling of temp tables (sql_base.cc)
+ we use thread_id AND server_id. TODO when this is merged into
+ 4.1: in 4.1, slave_proxy_id has been renamed to pseudo_thread_id
+ and is a session variable: that's to make mysqlbinlog work with
+ temp tables. We probably need to introduce
+
+ SET PSEUDO_SERVER_ID
+ for mysqlbinlog in 4.1. mysqlbinlog would print:
+ SET PSEUDO_SERVER_ID=
+ SET PSEUDO_THREAD_ID=
+ for each query using temp tables.
+ */
+ int4store(buf + Q_THREAD_ID_OFFSET, slave_proxy_id);
+ int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
+ buf[Q_DB_LEN_OFFSET] = (char) db_len;
+ int2store(buf + Q_ERR_CODE_OFFSET, error_code);
+
+ /*
+ You MUST always write status vars in increasing order of code. This
+ guarantees that a slightly older slave will be able to parse those he
+ knows.
+ */
+ start_of_status= start= buf+QUERY_HEADER_LEN;
+ if (flags2_inited)
+ {
+ *start++= Q_FLAGS2_CODE;
+ int4store(start, flags2);
+ start+= 4;
+ }
+ if (sql_mode_inited)
+ {
+ *start++= Q_SQL_MODE_CODE;
+ int8store(start, (ulonglong)sql_mode);
+ start+= 8;
+ }
+ if (catalog_len) // i.e. this var is inited (false for 4.0 events)
+ {
+ store_str_with_code_and_len(&start,
+ catalog, catalog_len, Q_CATALOG_NZ_CODE);
+ /*
+ In 5.0.x where x<4 masters we used to store the end zero here. This was
+ a waste of one byte so we don't do it in x>=4 masters. We change code to
+ Q_CATALOG_NZ_CODE, because re-using the old code would make x<4 slaves
+ of this x>=4 master segfault (expecting a zero when there is
+ none). Remaining compatibility problems are: the older slave will not
+ find the catalog; but it is will not crash, and it's not an issue
+ that it does not find the catalog as catalogs were not used in these
+ older MySQL versions (we store it in binlog and read it from relay log
+ but do nothing useful with it). What is an issue is that the older slave
+ will stop processing the Q_* blocks (and jumps to the db/query) as soon
+ as it sees unknown Q_CATALOG_NZ_CODE; so it will not be able to read
+ Q_AUTO_INCREMENT*, Q_CHARSET and so replication will fail silently in
+ various ways. Documented that you should not mix alpha/beta versions if
+ they are not exactly the same version, with example of 5.0.3->5.0.2 and
+ 5.0.4->5.0.3. If replication is from older to new, the new will
+ recognize Q_CATALOG_CODE and have no problem.
+ */
+ }
+ if (auto_increment_increment != 1 || auto_increment_offset != 1)
+ {
+ *start++= Q_AUTO_INCREMENT;
+ int2store(start, auto_increment_increment);
+ int2store(start+2, auto_increment_offset);
+ start+= 4;
+ }
+ if (charset_inited)
+ {
+ *start++= Q_CHARSET_CODE;
+ memcpy(start, charset, 6);
+ start+= 6;
+ }
+ if (time_zone_len)
+ {
+ /* In the TZ sys table, column Name is of length 64 so this should be ok */
+ DBUG_ASSERT(time_zone_len <= MAX_TIME_ZONE_NAME_LENGTH);
+ store_str_with_code_and_len(&start,
+ time_zone_str, time_zone_len, Q_TIME_ZONE_CODE);
+ }
+ if (lc_time_names_number)
+ {
+ DBUG_ASSERT(lc_time_names_number <= 0xFFFF);
+ *start++= Q_LC_TIME_NAMES_CODE;
+ int2store(start, lc_time_names_number);
+ start+= 2;
+ }
+ if (charset_database_number)
+ {
+ DBUG_ASSERT(charset_database_number <= 0xFFFF);
+ *start++= Q_CHARSET_DATABASE_CODE;
+ int2store(start, charset_database_number);
+ start+= 2;
+ }
+ if (table_map_for_update)
+ {
+ *start++= Q_TABLE_MAP_FOR_UPDATE_CODE;
+ int8store(start, table_map_for_update);
+ start+= 8;
+ }
+ if (master_data_written != 0)
+ {
+ /*
+ Q_MASTER_DATA_WRITTEN_CODE only exists in relay logs where the master
+ has binlog_version<4 and the slave has binlog_version=4. See comment
+ for master_data_written in log_event.h for details.
+ */
+ *start++= Q_MASTER_DATA_WRITTEN_CODE;
+ int4store(start, master_data_written);
+ start+= 4;
+ }
+
+ if (thd && thd->need_binlog_invoker())
+ {
+ LEX_CSTRING user;
+ LEX_CSTRING host;
+ memset(&user, 0, sizeof(user));
+ memset(&host, 0, sizeof(host));
+
+ if (thd->slave_thread && thd->has_invoker())
+ {
+ /* user will be null, if master is older than this patch */
+ user= thd->get_invoker_user();
+ host= thd->get_invoker_host();
+ }
+ else
+ {
+ Security_context *ctx= thd->security_ctx;
+
+ if (thd->need_binlog_invoker() == THD::INVOKER_USER)
+ {
+ user.str= ctx->priv_user;
+ host.str= ctx->priv_host;
+ host.length= strlen(host.str);
+ }
+ else
+ {
+ user.str= ctx->priv_role;
+ host= empty_clex_str;
+ }
+ user.length= strlen(user.str);
+ }
+
+ if (user.length > 0)
+ {
+ *start++= Q_INVOKER;
+
+ /*
+ Store user length and user. The max length of use is 16, so 1 byte is
+ enough to store the user's length.
+ */
+ *start++= (uchar)user.length;
+ memcpy(start, user.str, user.length);
+ start+= user.length;
+
+ /*
+ Store host length and host. The max length of host is 60, so 1 byte is
+ enough to store the host's length.
+ */
+ *start++= (uchar)host.length;
+ memcpy(start, host.str, host.length);
+ start+= host.length;
+ }
+ }
+
+ if (thd && thd->query_start_sec_part_used)
+ {
+ *start++= Q_HRNOW;
+ get_time();
+ int3store(start, when_sec_part);
+ start+= 3;
+ }
+ /*
+ NOTE: When adding new status vars, please don't forget to update
+ the MAX_SIZE_LOG_EVENT_STATUS in log_event.h and update the function
+ code_name() in this file.
+
+ Here there could be code like
+ if (command-line-option-which-says-"log_this_variable" && inited)
+ {
+ *start++= Q_THIS_VARIABLE_CODE;
+ int4store(start, this_variable);
+ start+= 4;
+ }
+ */
+
+ /* Store length of status variables */
+ status_vars_len= (uint) (start-start_of_status);
+ DBUG_ASSERT(status_vars_len <= MAX_SIZE_LOG_EVENT_STATUS);
+ int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len);
+
+ /*
+ Calculate length of whole event
+ The "1" below is the \0 in the db's length
+ */
+ event_length= ((uint) (start-buf) + get_post_header_size_for_derived() +
+ db_len + 1 + q_len);
+
+ return write_header(event_length) ||
+ write_data(buf, QUERY_HEADER_LEN) ||
+ write_post_header_for_derived() ||
+ write_data(start_of_status, (uint) status_vars_len) ||
+ write_data(db, db_len + 1) ||
+ write_data(query, q_len) ||
+ write_footer();
+}
+
+bool Query_compressed_log_event::write()
+{
+ char *buffer;
+ uint32 alloc_size, compressed_size;
+ bool ret= true;
+
+ compressed_size= alloc_size= binlog_get_compress_len(q_len);
+ buffer= (char*) my_safe_alloca(alloc_size);
+ if (buffer &&
+ !binlog_buf_compress(query, buffer, q_len, &compressed_size))
+ {
+ /*
+ Write the compressed event. We have to temporarily store the event
+ in query and q_len as Query_log_event::write() uses these.
+ */
+ const char *query_tmp= query;
+ uint32 q_len_tmp= q_len;
+ query= buffer;
+ q_len= compressed_size;
+ ret= Query_log_event::write();
+ query= query_tmp;
+ q_len= q_len_tmp;
+ }
+ my_safe_afree(buffer, alloc_size);
+ return ret;
+}
+
+
+/**
+ The simplest constructor that could possibly work. This is used for
+ creating static objects that have a special meaning and are invisible
+ to the log.
+*/
+Query_log_event::Query_log_event()
+ :Log_event(), data_buf(0)
+{
+ memset(&user, 0, sizeof(user));
+ memset(&host, 0, sizeof(host));
+}
+
+
+/*
+ SYNOPSIS
+ Query_log_event::Query_log_event()
+ thd_arg - thread handle
+ 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
+
+ DESCRIPTION
+ Creates an event for binlogging
+ The value for `errcode' should be supplied by caller.
+*/
+Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
+ size_t query_length, bool using_trans,
+ bool direct, bool suppress_use, int errcode)
+
+ :Log_event(thd_arg,
+ (thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F :
+ 0) |
+ (suppress_use ? LOG_EVENT_SUPPRESS_USE_F : 0),
+ using_trans),
+ data_buf(0), query(query_arg), catalog(thd_arg->catalog),
+ q_len((uint32) query_length),
+ thread_id(thd_arg->thread_id),
+ /* save the original thread id; we already know the server id */
+ slave_proxy_id((ulong)thd_arg->variables.pseudo_thread_id),
+ flags2_inited(1), sql_mode_inited(1), charset_inited(1), flags2(0),
+ sql_mode(thd_arg->variables.sql_mode),
+ auto_increment_increment(thd_arg->variables.auto_increment_increment),
+ auto_increment_offset(thd_arg->variables.auto_increment_offset),
+ lc_time_names_number(thd_arg->variables.lc_time_names->number),
+ charset_database_number(0),
+ table_map_for_update((ulonglong)thd_arg->table_map_for_update),
+ master_data_written(0)
+{
+ /* status_vars_len is set just before writing the event */
+
+ time_t end_time;
+
+#ifdef WITH_WSREP
+ /*
+ If Query_log_event will contain non trans keyword (not BEGIN, COMMIT,
+ SAVEPOINT or ROLLBACK) we disable PA for this transaction.
+ Note that here WSREP(thd) might not be true e.g. when wsrep_shcema
+ is created we create tables with thd->variables.wsrep_on=false
+ to avoid replicating wsrep_schema tables to other nodes.
+ */
+ if (WSREP_ON && !is_trans_keyword())
+ {
+ thd->wsrep_PA_safe= false;
+ }
+#endif /* WITH_WSREP */
+
+ memset(&user, 0, sizeof(user));
+ memset(&host, 0, sizeof(host));
+ error_code= errcode;
+
+ end_time= my_time(0);
+ exec_time = (ulong) (end_time - thd_arg->start_time);
+ /**
+ @todo this means that if we have no catalog, then it is replicated
+ as an existing catalog of length zero. is that safe? /sven
+ */
+ catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
+
+ if (!(db= thd->db.str))
+ db= "";
+ db_len= (uint32) strlen(db);
+ if (thd_arg->variables.collation_database != thd_arg->db_charset)
+ charset_database_number= thd_arg->variables.collation_database->number;
+
+ /*
+ We only replicate over the bits of flags2 that we need: the rest
+ are masked out by "& OPTIONS_WRITTEN_TO_BINLOG".
+
+ We also force AUTOCOMMIT=1. Rationale (cf. BUG#29288): After
+ fixing BUG#26395, we always write BEGIN and COMMIT around all
+ transactions (even single statements in autocommit mode). This is
+ so that replication from non-transactional to transactional table
+ and error recovery from XA to non-XA table should work as
+ expected. The BEGIN/COMMIT are added in log.cc. However, there is
+ one exception: MyISAM bypasses log.cc and writes directly to the
+ binlog. So if autocommit is off, master has MyISAM, and slave has
+ a transactional engine, then the slave will just see one long
+ never-ending transaction. The only way to bypass explicit
+ BEGIN/COMMIT in the binlog is by using a non-transactional table.
+ So setting AUTOCOMMIT=1 will make this work as expected.
+
+ Note: explicitly replicate AUTOCOMMIT=1 from master. We do not
+ assume AUTOCOMMIT=1 on slave; the slave still reads the state of
+ the autocommit flag as written by the master to the binlog. This
+ behavior may change after WL#4162 has been implemented.
+ */
+ flags2= (uint32) (thd_arg->variables.option_bits &
+ (OPTIONS_WRITTEN_TO_BIN_LOG & ~OPTION_NOT_AUTOCOMMIT));
+ DBUG_ASSERT(thd_arg->variables.character_set_client->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.collation_connection->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.collation_server->number < 256*256);
+ DBUG_ASSERT(thd_arg->variables.character_set_client->mbminlen == 1);
+ int2store(charset, thd_arg->variables.character_set_client->number);
+ int2store(charset+2, thd_arg->variables.collation_connection->number);
+ int2store(charset+4, thd_arg->variables.collation_server->number);
+ if (thd_arg->time_zone_used)
+ {
+ /*
+ Note that our event becomes dependent on the Time_zone object
+ representing the time zone. Fortunately such objects are never deleted
+ or changed during mysqld's lifetime.
+ */
+ time_zone_len= thd_arg->variables.time_zone->get_name()->length();
+ time_zone_str= thd_arg->variables.time_zone->get_name()->ptr();
+ }
+ else
+ time_zone_len= 0;
+
+ LEX *lex= thd->lex;
+ /*
+ Defines that the statement will be written directly to the binary log
+ without being wrapped by a BEGIN...COMMIT. Otherwise, the statement
+ will be written to either the trx-cache or stmt-cache.
+
+ Note that a cache will not be used if the parameter direct is TRUE.
+ */
+ bool use_cache= FALSE;
+ /*
+ TRUE defines that the trx-cache must be used and by consequence the
+ use_cache is TRUE.
+
+ Note that a cache will not be used if the parameter direct is TRUE.
+ */
+ bool trx_cache= FALSE;
+ cache_type= Log_event::EVENT_INVALID_CACHE;
+
+ if (!direct)
+ {
+ switch (lex->sql_command)
+ {
+ case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
+ use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode());
+ break;
+
+ case SQLCOM_CREATE_TABLE:
+ case SQLCOM_CREATE_SEQUENCE:
+ /*
+ 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->first_select_lex()->item_list.elements &&
+ thd->is_current_stmt_binlog_format_row()) ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN);
+ use_cache= (lex->tmp_table() &&
+ thd->in_multi_stmt_transaction_mode()) || trx_cache;
+ break;
+ case SQLCOM_SET_OPTION:
+ if (lex->autocommit)
+ use_cache= trx_cache= FALSE;
+ else
+ use_cache= TRUE;
+ break;
+ case SQLCOM_RELEASE_SAVEPOINT:
+ case SQLCOM_ROLLBACK_TO_SAVEPOINT:
+ case SQLCOM_SAVEPOINT:
+ case SQLCOM_XA_END:
+ use_cache= trx_cache= TRUE;
+ break;
+ default:
+ use_cache= sqlcom_can_generate_row_events(thd);
+ break;
+ }
+ }
+
+ if (!use_cache || direct)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+ else if (using_trans || trx_cache || stmt_has_updated_trans_table(thd) ||
+ thd->lex->is_mixed_stmt_unsafe(thd->in_multi_stmt_transaction_mode(),
+ thd->variables.binlog_direct_non_trans_update,
+ trans_has_updated_trans_table(thd),
+ thd->tx_isolation))
+ cache_type= Log_event::EVENT_TRANSACTIONAL_CACHE;
+ 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 cache_tye: %d",
+ (ulong) flags2, sql_mode, cache_type));
+}
+
+Query_compressed_log_event::Query_compressed_log_event(THD* thd_arg, const char* query_arg,
+ ulong query_length, bool using_trans,
+ bool direct, bool suppress_use, int errcode)
+ :Query_log_event(thd_arg, query_arg, query_length, using_trans, direct,
+ suppress_use, errcode),
+ query_buf(0)
+{
+
+}
+
+
+#if defined(HAVE_REPLICATION)
+
+int Query_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ return do_apply_event(rgi, query, q_len);
+}
+
+/**
+ Compare if two errors should be regarded as equal.
+ This is to handle the case when you can get slightly different errors
+ on master and slave for the same thing.
+ @param
+ expected_error Error we got on master
+ actual_error Error we got on slave
+
+ @return
+ 1 Errors are equal
+ 0 Errors are different
+*/
+
+bool test_if_equal_repl_errors(int expected_error, int actual_error)
+{
+ if (expected_error == actual_error)
+ return 1;
+ switch (expected_error) {
+ case ER_DUP_ENTRY:
+ case ER_DUP_ENTRY_WITH_KEY_NAME:
+ case ER_DUP_KEY:
+ case ER_AUTOINC_READ_FAILED:
+ return (actual_error == ER_DUP_ENTRY ||
+ actual_error == ER_DUP_ENTRY_WITH_KEY_NAME ||
+ actual_error == ER_DUP_KEY ||
+ 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;
+ }
+ return 0;
+}
+
+
+/**
+ @todo
+ Compare the values of "affected rows" around here. Something
+ like:
+ @code
+ if ((uint32) affected_in_event != (uint32) affected_on_slave)
+ {
+ sql_print_error("Slave: did not get the expected number of affected \
+ rows running query from master - expected %d, got %d (this numbers \
+ should have matched modulo 4294967296).", 0, ...);
+ thd->query_error = 1;
+ }
+ @endcode
+ We may also want an option to tell the slave to ignore "affected"
+ 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(rpl_group_info *rgi,
+ const char *query_arg, uint32 q_len_arg)
+{
+ int expected_error,actual_error= 0;
+ Schema_specification_st db_options;
+ uint64 sub_id= 0;
+ void *hton= NULL;
+ rpl_gtid gtid;
+ Relay_log_info const *rli= rgi->rli;
+ Rpl_filter *rpl_filter= rli->mi->rpl_filter;
+ bool current_stmt_is_commit;
+ DBUG_ENTER("Query_log_event::do_apply_event");
+
+ /*
+ Colleagues: please never free(thd->catalog) in MySQL. This would
+ lead to bugs as here thd->catalog is a part of an alloced block,
+ not an entire alloced block (see
+ Query_log_event::do_apply_event()). Same for thd->db. Thank
+ you.
+ */
+ thd->catalog= catalog_len ? (char *) catalog : (char *)"";
+
+ size_t valid_len= Well_formed_prefix(system_charset_info,
+ db, db_len, NAME_LEN).length();
+
+ if (valid_len != db_len)
+ {
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "Invalid database name in Query event.");
+ thd->is_slave_error= true;
+ goto end;
+ }
+
+ set_thd_db(thd, rpl_filter, db, db_len);
+
+ /*
+ Setting the character set and collation of the current database thd->db.
+ */
+ load_db_opt_by_name(thd, thd->db.str, &db_options);
+ if (db_options.default_table_charset)
+ thd->db_charset= db_options.default_table_charset;
+ thd->variables.auto_increment_increment= auto_increment_increment;
+ thd->variables.auto_increment_offset= auto_increment_offset;
+
+ DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
+
+ thd->clear_error(1);
+ current_stmt_is_commit= is_commit();
+
+ DBUG_ASSERT(!current_stmt_is_commit || !rgi->tables_to_lock);
+ rgi->slave_close_thread_tables(thd);
+
+ /*
+ Note: We do not need to execute reset_one_shot_variables() if this
+ db_ok() test fails.
+ Reason: The db stored in binlog events is the same for SET and for
+ its companion query. If the SET is ignored because of
+ db_ok(), the companion query will also be ignored, and if
+ the companion query is ignored in the db_ok() test of
+ ::do_apply_event(), then the companion SET also have so
+ we don't need to reset_one_shot_variables().
+ */
+ if (is_trans_keyword() || rpl_filter->db_ok(thd->db.str))
+ {
+ thd->set_time(when, when_sec_part);
+ thd->set_query_and_id((char*)query_arg, q_len_arg,
+ thd->charset(), next_query_id());
+ thd->variables.pseudo_thread_id= thread_id; // for temp tables
+ DBUG_PRINT("query",("%s", thd->query()));
+
+ if (unlikely(!(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 must take their value from
+ flags2.
+ */
+ thd->variables.option_bits= flags2|(thd->variables.option_bits & ~OPTIONS_WRITTEN_TO_BIN_LOG);
+ }
+ /*
+ else, we are in a 3.23/4.0 binlog; we previously received a
+ Rotate_log_event which reset thd->variables.option_bits and
+ sql_mode etc, so nothing to do.
+ */
+ /*
+ 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
+ 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)
+ thd->variables.sql_mode=
+ (sql_mode_t) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) |
+ (sql_mode & ~(sql_mode_t) MODE_NO_DIR_IN_CREATE));
+ if (charset_inited)
+ {
+ 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=
+ get_charset(uint2korr(charset), MYF(MY_WME))) ||
+ !(thd->variables.collation_connection=
+ get_charset(uint2korr(charset+2), MYF(MY_WME))) ||
+ !(thd->variables.collation_server=
+ get_charset(uint2korr(charset+4), MYF(MY_WME))))
+ {
+ /*
+ We updated the thd->variables with nonsensical values (0). Let's
+ set them to something safe (i.e. which avoids crash), and we'll
+ stop with EE_UNKNOWN_CHARSET in compare_errors (unless set to
+ ignore this error).
+ */
+ set_slave_thread_default_charset(thd, rgi);
+ goto compare_errors;
+ }
+ thd->update_charset(); // for the charset change to take effect
+ /*
+ Reset thd->query_string.cs to the newly set value.
+ Note, there is a small flaw here. For a very short time frame
+ if the new charset is different from the old charset and
+ if another thread executes "SHOW PROCESSLIST" after
+ the above thd->set_query_and_id() and before this thd->set_query(),
+ and if the current query has some non-ASCII characters,
+ the another thread may see some '?' marks in the PROCESSLIST
+ result. This should be acceptable now. This is a reminder
+ to fix this if any refactoring happens here sometime.
+ */
+ thd->set_query((char*) query_arg, q_len_arg, thd->charset());
+ }
+ }
+ if (time_zone_len)
+ {
+ String tmp(time_zone_str, time_zone_len, &my_charset_bin);
+ if (!(thd->variables.time_zone= my_tz_find(thd, &tmp)))
+ {
+ my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), tmp.c_ptr());
+ thd->variables.time_zone= global_system_variables.time_zone;
+ goto compare_errors;
+ }
+ }
+ if (lc_time_names_number)
+ {
+ if (!(thd->variables.lc_time_names=
+ my_locale_by_number(lc_time_names_number)))
+ {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Unknown locale: '%d'", MYF(0), lc_time_names_number);
+ thd->variables.lc_time_names= &my_locale_en_US;
+ goto compare_errors;
+ }
+ }
+ else
+ thd->variables.lc_time_names= &my_locale_en_US;
+ if (charset_database_number)
+ {
+ CHARSET_INFO *cs;
+ if (!(cs= get_charset(charset_database_number, MYF(0))))
+ {
+ char buf[20];
+ int10_to_str((int) charset_database_number, buf, -10);
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), buf);
+ goto compare_errors;
+ }
+ thd->variables.collation_database= cs;
+ }
+ else
+ thd->variables.collation_database= thd->db_charset;
+
+ {
+ const CHARSET_INFO *cs= thd->charset();
+ /*
+ We cannot ask for parsing a statement using a character set
+ without state_maps (parser internal data).
+ */
+ if (!cs->state_map)
+ {
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "character_set cannot be parsed");
+ thd->is_slave_error= true;
+ goto end;
+ }
+ }
+
+ /*
+ 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 (unlikely(rpl_global_gtid_slave_state->record_gtid(thd, &gtid,
+ sub_id,
+ true, false,
+ &hton)))
+ {
+ 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);
+ /*
+ Flag if we need to rollback the statement transaction on
+ slave if it by chance succeeds.
+ If we expected a non-zero error code and get nothing and,
+ it is a concurrency issue or ignorable issue, effects
+ of the statement should be rolled back.
+ */
+ if (unlikely(expected_error) &&
+ (ignored_error_code(expected_error) ||
+ 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.str, thd->db.length,
+ thd->charset(), NULL);
+ THD_STAGE_INFO(thd, stage_starting);
+ 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);
+
+ if (thd->slave_thread)
+ {
+ /*
+ To be compatible with previous releases, the slave thread uses the global
+ log_slow_disabled_statements value, wich can be changed dynamically, so we
+ have to set the sql_log_slow respectively.
+ */
+ thd->variables.sql_log_slow= !MY_TEST(global_system_variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SLAVE);
+ }
+
+ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
+ FALSE, FALSE);
+ /* Finalize server status flags after executing a statement. */
+ thd->update_server_status();
+ log_slow_statement(thd);
+ thd->lex->restore_set_statement_var();
+ }
+
+ thd->variables.option_bits&= ~OPTION_MASTER_SQL_ERROR;
+ }
+ else
+ {
+ /*
+ The query got a really bad error on the master (thread killed etc),
+ which could be inconsistent. Parse it to test the table names: if the
+ replicate-*-do|ignore-table rules say "this query must be ignored" then
+ we exit gracefully; otherwise we warn about the bad error and tell DBA
+ to check/fix it.
+ */
+ if (mysql_test_parse_for_slave(thd, thd->query(), thd->query_length()))
+ thd->clear_error(1);
+ else
+ {
+ 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 \
+point. If you are sure that your master is ok, run this query manually on the \
+slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; \
+START SLAVE; . Query: '%s'", expected_error, thd->query());
+ thd->is_slave_error= 1;
+ }
+ goto end;
+ }
+
+ /* If the query was not ignored, it is printed to the general log */
+ if (likely(!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
+ {
+ /*
+ Bug#54201: If we skip an INSERT query that uses auto_increment, then we
+ should reset any @@INSERT_ID set by an Intvar_log_event associated with
+ the query; otherwise the @@INSERT_ID will linger until the next INSERT
+ that uses auto_increment and may affect extra triggers on the slave etc.
+
+ We reset INSERT_ID unconditionally; it is probably cheaper than
+ checking if it is necessary.
+ */
+ thd->auto_inc_intervals_forced.empty();
+ }
+
+compare_errors:
+ /*
+ In the slave thread, we may sometimes execute some DROP / * 40005
+ TEMPORARY * / TABLE that come from parts of binlogs (likely if we
+ use RESET SLAVE or CHANGE MASTER TO), while the temporary table
+ has already been dropped. To ignore such irrelevant "table does
+ not exist errors", we silently clear the error if TEMPORARY was used.
+ */
+ if ((thd->lex->sql_command == SQLCOM_DROP_TABLE ||
+ thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) &&
+ thd->lex->tmp_table() &&
+ thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR &&
+ !expected_error)
+ 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->get_stmt_da()->sql_errno() : 0;
+ DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
+ expected_error, actual_error));
+
+ if ((unlikely(expected_error) &&
+ !test_if_equal_repl_errors(expected_error, actual_error) &&
+ !concurrency_error_code(expected_error)) &&
+ !ignored_error_code(actual_error) &&
+ !ignored_error_code(expected_error))
+ {
+ 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_THD(thd, expected_error),
+ expected_error,
+ actual_error ? thd->get_stmt_da()->message() : "no error",
+ actual_error,
+ print_slave_db_safe(db), query_arg);
+ thd->is_slave_error= 1;
+ }
+ /*
+ If we get the same error code as expected and it is not a concurrency
+ issue, or should be ignored.
+ */
+ else if ((test_if_equal_repl_errors(expected_error, actual_error) &&
+ !concurrency_error_code(expected_error)) ||
+ ignored_error_code(actual_error))
+ {
+ DBUG_PRINT("info",("error ignored"));
+ thd->clear_error(1);
+ 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 (unlikely(thd->is_slave_error || thd->is_fatal_error))
+ {
+ 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"),
+ thd->get_db(), query_arg);
+ thd->is_slave_error= 1;
+#ifdef WITH_WSREP
+ if (wsrep_thd_is_toi(thd) && wsrep_must_ignore_error(thd))
+ {
+ thd->clear_error(1);
+ thd->killed= NOT_KILLED;
+ thd->wsrep_has_ignored_error= true;
+ }
+#endif /* WITH_WSREP */
+ }
+
+ /*
+ TODO: compare the values of "affected rows" around here. Something
+ like:
+ if ((uint32) affected_in_event != (uint32) affected_on_slave)
+ {
+ sql_print_error("Slave: did not get the expected number of affected \
+ rows running query from master - expected %d, got %d (this numbers \
+ should have matched modulo 4294967296).", 0, ...);
+ thd->is_slave_error = 1;
+ }
+ We may also want an option to tell the slave to ignore "affected"
+ mismatch. This mismatch could be implemented with a new ER_ code, and
+ to ignore it you would use --slave-skip-errors...
+
+ To do the comparison we need to know the value of "affected" which the
+ above mysql_parse() computed. And we need to know the value of
+ "affected" in the master's binlog. Both will be implemented later. The
+ important thing is that we now have the format ready to log the values
+ of "affected" in the binlog. So we can release 5.0.0 before effectively
+ logging "affected" and effectively comparing it.
+ */
+ } /* End of if (db_ok(... */
+
+ {
+ /**
+ The following failure injecion works in cooperation with tests
+ setting @@global.debug= 'd,stop_slave_middle_group'.
+ The sql thread receives the killed status and will proceed
+ to shutdown trying to finish incomplete events group.
+ */
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
+ 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;
+ };);
+ }
+
+end:
+ if (unlikely(sub_id && !thd->is_slave_error))
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, 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
+ probably, so data_buf will be freed, so the thd->... listed above will be
+ pointers to freed memory.
+ So we must set them to 0, so that those bad pointers values are not later
+ used. Note that "cleanup" queries like automatic DROP TEMPORARY TABLE
+ don't suffer from these assignments to 0 as DROP TEMPORARY
+ TABLE uses the db.table syntax.
+ */
+ thd->catalog= 0;
+ thd->set_db(&null_clex_str); /* 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
+ to replace the THD::stmt_depends_on_first_successful_insert_id_in_prev_stmt
+ variable by (THD->first_successful_insert_id_in_prev_stmt > 0) ; with the
+ resetting below we are ready to support that.
+ */
+ thd->first_successful_insert_id_in_prev_stmt_for_binlog= 0;
+ thd->first_successful_insert_id_in_prev_stmt= 0;
+ thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ DBUG_RETURN(thd->is_slave_error);
+}
+
+Log_event::enum_skip_reason
+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_ASSERT(query && q_len > 0);
+ DBUG_ASSERT(thd == rgi->thd);
+
+ /*
+ 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)
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+
+ if (rli->slave_skip_counter > 0)
+ {
+ if (is_begin())
+ {
+ thd->variables.option_bits|= OPTION_BEGIN | OPTION_GTID_BEGIN;
+ DBUG_RETURN(Log_event::continue_group(rgi));
+ }
+
+ if (is_commit() || is_rollback())
+ {
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+ }
+#ifdef WITH_WSREP
+ else if (WSREP(thd) && wsrep_mysql_replication_bundle &&
+ opt_slave_domain_parallel_threads == 0 &&
+ thd->wsrep_mysql_replicated > 0 &&
+ (is_begin() || is_commit()))
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif /* WITH_WSREP */
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+
+
+bool
+Query_log_event::peek_is_commit_rollback(const char *event_start,
+ size_t event_len,
+ enum enum_binlog_checksum_alg 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
+
+
+/**************************************************************************
+ Start_log_event_v3 methods
+**************************************************************************/
+
+Start_log_event_v3::Start_log_event_v3()
+ :Log_event(), created(0), binlog_version(BINLOG_VERSION),
+ dont_set_created(0)
+{
+ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Start_log_event_v3::pack_info(Protocol *protocol)
+{
+ char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos;
+ pos= strmov(buf, "Server ver: ");
+ pos= strmov(pos, server_version);
+ pos= strmov(pos, ", Binlog ver: ");
+ pos= int10_to_str(binlog_version, pos, 10);
+ protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
+}
+#endif
+
+
+bool Start_log_event_v3::write()
+{
+ char buff[START_V3_HEADER_LEN];
+ int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
+ memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
+ if (!dont_set_created)
+ created= get_time(); // this sets when and when_sec_part as a side effect
+ int4store(buff + ST_CREATED_OFFSET,created);
+ return write_header(sizeof(buff)) ||
+ write_data(buff, sizeof(buff)) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+
+/**
+ Start_log_event_v3::do_apply_event() .
+ The master started
+
+ IMPLEMENTATION
+ - To handle the case where the master died without having time to write
+ DROP TEMPORARY TABLE, DO RELEASE_LOCK (prepared statements' deletion is
+ TODO), we clean up all temporary tables that we got, if we are sure we
+ can (see below).
+
+ @todo
+ - Remove all active user locks.
+ Guilhem 2003-06: this is true but not urgent: the worst it can cause is
+ the use of a bit of memory for a user lock which will not be used
+ anymore. If the user lock is later used, the old one will be released. In
+ other words, no deadlock problem.
+*/
+
+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:
+ case 4:
+ /*
+ This can either be 4.x (then a Start_log_event_v3 is only at master
+ startup so we are sure the master has restarted and cleared his temp
+ tables; the event always has 'created'>0) or 5.0 (then we have to test
+ 'created').
+ */
+ if (created)
+ {
+ rli->close_temporary_tables();
+
+ /*
+ The following is only false if we get here with a BINLOG statement
+ */
+ if (rli->mi)
+ cleanup_load_tmpdir(&rli->mi->cmp_connection_name);
+ }
+ break;
+
+ /*
+ Now the older formats; in that case load_tmpdir is cleaned up by the I/O
+ thread.
+ */
+ case 1:
+ if (strncmp(rli->relay_log.description_event_for_exec->server_version,
+ "3.23.57",7) >= 0 && created)
+ {
+ /*
+ Can distinguish, based on the value of 'created': this event was
+ generated at master startup.
+ */
+ rli->close_temporary_tables();
+ }
+ /*
+ Otherwise, can't distinguish a Start_log_event generated at
+ master startup and one generated by master FLUSH LOGS, so cannot
+ be sure temp tables have to be dropped. So do nothing.
+ */
+ break;
+ default:
+ /*
+ This case is not expected. It can be either an event corruption or an
+ unsupported binary log version.
+ */
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "Binlog version not supported");
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(error);
+}
+#endif /* defined(HAVE_REPLICATION) */
+
+/***************************************************************************
+ Format_description_log_event methods
+****************************************************************************/
+
+bool Format_description_log_event::write()
+{
+ bool ret;
+ bool no_checksum;
+ /*
+ We don't call Start_log_event_v3::write() because this would make 2
+ my_b_safe_write().
+ */
+ 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]= common_header_len;
+ /*
+ if checksum is requested
+ record the checksum-algorithm descriptor next to
+ post_header_len vector which will be followed by the checksum value.
+ Master is supposed to trigger checksum computing by binlog_checksum_options,
+ slave does it via marking the event according to
+ FD_queue checksum_alg value.
+ */
+ compile_time_assert(BINLOG_CHECKSUM_ALG_DESC_LEN == 1);
+#ifdef DBUG_ASSERT_EXISTS
+ data_written= 0; // to prepare for need_checksum assert
+#endif
+ uint8 checksum_byte= (uint8)
+ (need_checksum() ? checksum_alg : BINLOG_CHECKSUM_ALG_OFF);
+ /*
+ FD of checksum-aware server is always checksum-equipped, (V) is in,
+ regardless of @@global.binlog_checksum policy.
+ Thereby a combination of (A) == 0, (V) != 0 means
+ it's the checksum-aware server's FD event that heads checksum-free binlog
+ file.
+ Here 0 stands for checksumming OFF to evaluate (V) as 0 is that case.
+ A combination of (A) != 0, (V) != 0 denotes FD of the checksum-aware server
+ heading the checksummed binlog.
+ (A), (V) presence in FD of the checksum-aware server makes the event
+ 1 + 4 bytes bigger comparing to the former FD.
+ */
+
+ if ((no_checksum= (checksum_alg == BINLOG_CHECKSUM_ALG_OFF)))
+ {
+ checksum_alg= BINLOG_CHECKSUM_ALG_CRC32; // Forcing (V) room to fill anyway
+ }
+ ret= write_header(rec_size) ||
+ write_data(buff, sizeof(buff)) ||
+ write_data(post_header_len, number_of_event_types) ||
+ write_data(&checksum_byte, sizeof(checksum_byte)) ||
+ write_footer();
+ if (no_checksum)
+ checksum_alg= BINLOG_CHECKSUM_ALG_OFF;
+ return ret;
+}
+
+#if defined(HAVE_REPLICATION)
+int Format_description_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ int ret= 0;
+ Relay_log_info *rli= rgi->rli;
+ DBUG_ENTER("Format_description_log_event::do_apply_event");
+
+ /*
+ As a transaction NEVER spans on 2 or more binlogs:
+ if we have an active transaction at this point, the master died
+ while writing the transaction to the binary log, i.e. while
+ flushing the binlog cache to the binlog. XA guarantees that master has
+ rolled back. So we roll back.
+ Note: this event could be sent by the master to inform us of the
+ format of its binlog; in other words maybe it is not at its
+ original place when it comes to us; we'll know this by checking
+ log_pos ("artificial" events have log_pos == 0).
+ */
+ 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, 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.");
+ rgi->cleanup_context(thd, 1);
+ }
+
+ /*
+ If this event comes from ourselves, there is no cleaning task to
+ 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) global_system_variables.server_id)
+ {
+ /*
+ If the event was not requested by the slave i.e. the master sent
+ it while the slave asked for a position >4, the event will make
+ rli->group_master_log_pos advance. Say that the slave asked for
+ position 1000, and the Format_desc event's end is 96. Then in
+ the beginning of replication rli->group_master_log_pos will be
+ 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(rgi);
+ }
+
+ if (!ret)
+ {
+ /* Save the information describing this binlog */
+ copy_crypto_data(rli->relay_log.description_event_for_exec);
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= this;
+ }
+
+ DBUG_RETURN(ret);
+}
+
+int Format_description_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ if (server_id == (uint32) global_system_variables.server_id)
+ {
+ /*
+ We only increase the relay log position if we are skipping
+ events and do not touch any group_* variables, nor flush the
+ relay log info. If there is a crash, we will have to re-skip
+ the events again, but that is a minor issue.
+
+ If we do not skip stepping the group log position (and the
+ server id was changed when restarting the server), it might well
+ be that we start executing at a position that is invalid, e.g.,
+ at a Rows_log_event or a Query_log_event preceeded by a
+ Intvar_log_event instead of starting at a Table_map_log_event or
+ the Intvar_log_event respectively.
+ */
+ rgi->inc_event_relay_log_pos();
+ return 0;
+ }
+ else
+ {
+ return Log_event::do_update_pos(rgi);
+ }
+}
+
+Log_event::enum_skip_reason
+Format_description_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ return Log_event::EVENT_SKIP_NOT;
+}
+
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+int Start_encryption_log_event::do_apply_event(rpl_group_info* rgi)
+{
+ return rgi->rli->relay_log.description_event_for_exec->start_decryption(this);
+}
+
+int Start_encryption_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ /*
+ master never sends Start_encryption_log_event, any SELE that a slave
+ might see was created locally in MYSQL_BIN_LOG::open() on the slave
+ */
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+
+#endif
+
+
+/**************************************************************************
+ Load_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+bool Load_log_event::print_query(THD *thd, bool need_db, const char *cs,
+ String *buf, my_off_t *fn_start,
+ my_off_t *fn_end, const char *qualify_db)
+{
+ if (need_db && db && db_len)
+ {
+ buf->append(STRING_WITH_LEN("use "));
+ append_identifier(thd, buf, db, db_len);
+ buf->append(STRING_WITH_LEN("; "));
+ }
+
+ buf->append(STRING_WITH_LEN("LOAD DATA "));
+
+ if (is_concurrent)
+ buf->append(STRING_WITH_LEN("CONCURRENT "));
+
+ if (fn_start)
+ *fn_start= buf->length();
+
+ if (check_fname_outside_temp_buf())
+ buf->append(STRING_WITH_LEN("LOCAL "));
+ buf->append(STRING_WITH_LEN("INFILE '"));
+ buf->append_for_single_quote(fname, fname_len);
+ buf->append(STRING_WITH_LEN("' "));
+
+ if (sql_ex.opt_flags & REPLACE_FLAG)
+ buf->append(STRING_WITH_LEN("REPLACE "));
+ else if (sql_ex.opt_flags & IGNORE_FLAG)
+ buf->append(STRING_WITH_LEN("IGNORE "));
+
+ buf->append(STRING_WITH_LEN("INTO"));
+
+ if (fn_end)
+ *fn_end= buf->length();
+
+ buf->append(STRING_WITH_LEN(" TABLE "));
+ if (qualify_db)
+ {
+ append_identifier(thd, buf, qualify_db, strlen(qualify_db));
+ buf->append(STRING_WITH_LEN("."));
+ }
+ append_identifier(thd, buf, table_name, table_name_len);
+
+ if (cs != NULL)
+ {
+ buf->append(STRING_WITH_LEN(" CHARACTER SET "));
+ buf->append(cs, strlen(cs));
+ }
+
+ /* We have to create all optional fields as the default is not empty */
+ buf->append(STRING_WITH_LEN(" FIELDS TERMINATED BY "));
+ pretty_print_str(buf, sql_ex.field_term, sql_ex.field_term_len);
+ if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
+ buf->append(STRING_WITH_LEN(" OPTIONALLY "));
+ buf->append(STRING_WITH_LEN(" ENCLOSED BY "));
+ pretty_print_str(buf, sql_ex.enclosed, sql_ex.enclosed_len);
+
+ buf->append(STRING_WITH_LEN(" ESCAPED BY "));
+ pretty_print_str(buf, sql_ex.escaped, sql_ex.escaped_len);
+
+ buf->append(STRING_WITH_LEN(" LINES TERMINATED BY "));
+ pretty_print_str(buf, sql_ex.line_term, sql_ex.line_term_len);
+ if (sql_ex.line_start_len)
+ {
+ buf->append(STRING_WITH_LEN(" STARTING BY "));
+ pretty_print_str(buf, sql_ex.line_start, sql_ex.line_start_len);
+ }
+
+ if ((long) skip_lines > 0)
+ {
+ buf->append(STRING_WITH_LEN(" IGNORE "));
+ buf->append_ulonglong(skip_lines);
+ buf->append(STRING_WITH_LEN(" LINES "));
+ }
+
+ if (num_fields)
+ {
+ uint i;
+ const char *field= fields;
+ buf->append(STRING_WITH_LEN(" ("));
+ for (i = 0; i < num_fields; i++)
+ {
+ if (i)
+ {
+ /*
+ Yes, the space and comma is reversed here. But this is mostly dead
+ code, at most used when reading really old binlogs from old servers,
+ so better just leave it as is...
+ */
+ buf->append(STRING_WITH_LEN(" ,"));
+ }
+ append_identifier(thd, buf, field, field_lens[i]);
+ field+= field_lens[i] + 1;
+ }
+ buf->append(STRING_WITH_LEN(")"));
+ }
+ return 0;
+}
+
+
+void Load_log_event::pack_info(Protocol *protocol)
+{
+ char query_buffer[1024];
+ String query_str(query_buffer, sizeof(query_buffer), system_charset_info);
+
+ query_str.length(0);
+ print_query(protocol->thd, TRUE, NULL, &query_str, 0, 0, NULL);
+ protocol->store(query_str.ptr(), query_str.length(), &my_charset_bin);
+}
+#endif /* defined(HAVE_REPLICATION) */
+
+
+bool Load_log_event::write_data_header()
+{
+ char buf[LOAD_HEADER_LEN];
+ int4store(buf + L_THREAD_ID_OFFSET, slave_proxy_id);
+ int4store(buf + L_EXEC_TIME_OFFSET, exec_time);
+ int4store(buf + L_SKIP_LINES_OFFSET, skip_lines);
+ buf[L_TBL_LEN_OFFSET] = (char)table_name_len;
+ buf[L_DB_LEN_OFFSET] = (char)db_len;
+ int4store(buf + L_NUM_FIELDS_OFFSET, num_fields);
+ return write_data(buf, LOAD_HEADER_LEN) != 0;
+}
+
+
+bool Load_log_event::write_data_body()
+{
+ if (sql_ex.write_data(writer))
+ return 1;
+ if (num_fields && fields && field_lens)
+ {
+ if (write_data(field_lens, num_fields) ||
+ write_data(fields, field_block_len))
+ return 1;
+ }
+ return (write_data(table_name, table_name_len + 1) ||
+ write_data(db, db_len + 1) ||
+ write_data(fname, fname_len));
+}
+
+
+Load_log_event::Load_log_event(THD *thd_arg, const sql_exchange *ex,
+ const char *db_arg, const char *table_name_arg,
+ List<Item> &fields_arg,
+ bool is_concurrent_arg,
+ enum enum_duplicates handle_dup,
+ bool ignore, bool using_trans)
+ :Log_event(thd_arg,
+ thd_arg->thread_specific_used ? LOG_EVENT_THREAD_SPECIFIC_F : 0,
+ using_trans),
+ thread_id(thd_arg->thread_id),
+ slave_proxy_id((ulong)thd_arg->variables.pseudo_thread_id),
+ num_fields(0),fields(0),
+ field_lens(0),field_block_len(0),
+ table_name(table_name_arg ? table_name_arg : ""),
+ db(db_arg), fname(ex->file_name), local_fname(FALSE),
+ is_concurrent(is_concurrent_arg)
+{
+ time_t end_time;
+ time(&end_time);
+ exec_time = (ulong) (end_time - thd_arg->start_time);
+ /* db can never be a zero pointer in 4.0 */
+ db_len = (uint32) strlen(db);
+ table_name_len = (uint32) strlen(table_name);
+ fname_len = (fname) ? (uint) strlen(fname) : 0;
+ sql_ex.field_term = ex->field_term->ptr();
+ sql_ex.field_term_len = (uint8) ex->field_term->length();
+ sql_ex.enclosed = ex->enclosed->ptr();
+ sql_ex.enclosed_len = (uint8) ex->enclosed->length();
+ sql_ex.line_term = ex->line_term->ptr();
+ sql_ex.line_term_len = (uint8) ex->line_term->length();
+ sql_ex.line_start = ex->line_start->ptr();
+ sql_ex.line_start_len = (uint8) ex->line_start->length();
+ sql_ex.escaped = ex->escaped->ptr();
+ sql_ex.escaped_len = (uint8) ex->escaped->length();
+ sql_ex.opt_flags = 0;
+ sql_ex.cached_new_format = -1;
+
+ if (ex->dumpfile)
+ sql_ex.opt_flags|= DUMPFILE_FLAG;
+ if (ex->opt_enclosed)
+ sql_ex.opt_flags|= OPT_ENCLOSED_FLAG;
+
+ sql_ex.empty_flags= 0;
+
+ switch (handle_dup) {
+ case DUP_REPLACE:
+ sql_ex.opt_flags|= REPLACE_FLAG;
+ break;
+ case DUP_UPDATE: // Impossible here
+ case DUP_ERROR:
+ break;
+ }
+ if (ignore)
+ sql_ex.opt_flags|= IGNORE_FLAG;
+
+ if (!ex->field_term->length())
+ sql_ex.empty_flags |= FIELD_TERM_EMPTY;
+ if (!ex->enclosed->length())
+ sql_ex.empty_flags |= ENCLOSED_EMPTY;
+ if (!ex->line_term->length())
+ sql_ex.empty_flags |= LINE_TERM_EMPTY;
+ if (!ex->line_start->length())
+ sql_ex.empty_flags |= LINE_START_EMPTY;
+ if (!ex->escaped->length())
+ sql_ex.empty_flags |= ESCAPED_EMPTY;
+
+ skip_lines = ex->skip_lines;
+
+ List_iterator<Item> li(fields_arg);
+ field_lens_buf.length(0);
+ fields_buf.length(0);
+ Item* item;
+ while ((item = li++))
+ {
+ num_fields++;
+ uchar len= (uchar) item->name.length;
+ field_block_len += len + 1;
+ fields_buf.append(item->name.str, len + 1);
+ field_lens_buf.append((char*)&len, 1);
+ }
+
+ field_lens = (const uchar*)field_lens_buf.ptr();
+ fields = fields_buf.ptr();
+}
+
+
+/**
+ Load_log_event::set_fields()
+
+ @note
+ This function can not use the member variable
+ for the database, since LOAD DATA INFILE on the slave
+ can be for a different database than the current one.
+ This is the reason for the affected_db argument to this method.
+*/
+
+void Load_log_event::set_fields(const char* affected_db,
+ List<Item> &field_list,
+ Name_resolution_context *context)
+{
+ uint i;
+ const char* field = fields;
+ for (i= 0; i < num_fields; i++)
+ {
+ LEX_CSTRING field_name= {field, field_lens[i] };
+ field_list.push_back(new (thd->mem_root)
+ Item_field(thd, context,
+ Lex_cstring_strlen(affected_db),
+ Lex_cstring_strlen(table_name),
+ field_name),
+ thd->mem_root);
+ field+= field_lens[i] + 1;
+ }
+}
+
+
+#if defined(HAVE_REPLICATION)
+/**
+ Does the data loading job when executing a LOAD DATA on the slave.
+
+ @param net
+ @param rli
+ @param use_rli_only_for_errors If set to 1, rli is provided to
+ Load_log_event::exec_event only for this
+ function to have RPL_LOG_NAME and
+ rli->last_slave_error, both being used by
+ error reports. rli's position advancing
+ is skipped (done by the caller which is
+ Execute_load_log_event::exec_event).
+ If set to 0, rli is provided for full use,
+ i.e. for error reports and position
+ advancing.
+
+ @todo
+ fix this; this can be done by testing rules in
+ Create_file_log_event::exec_event() and then discarding Append_block and
+ al.
+ @todo
+ this is a bug - this needs to be moved to the I/O thread
+
+ @retval
+ 0 Success
+ @retval
+ 1 Failure
+*/
+
+int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
+ bool use_rli_only_for_errors)
+{
+ Relay_log_info const *rli= rgi->rli;
+ Rpl_filter *rpl_filter= rli->mi->rpl_filter;
+ DBUG_ENTER("Load_log_event::do_apply_event");
+
+ DBUG_ASSERT(thd->query() == 0);
+ set_thd_db(thd, rpl_filter, db, db_len);
+ thd->clear_error(1);
+
+ /* see Query_log_event::do_apply_event() and BUG#13360 */
+ 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().
+ */
+ lex_start(thd);
+ thd->lex->local_file= local_fname;
+ thd->reset_for_next_command(0); // Errors are cleared above
+
+ /*
+ 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
+ now. So it is possible that we did a lot of disk writes for
+ nothing. In other words, a big LOAD DATA INFILE on the master will
+ still consume a lot of space on the slave (space in the relay log
+ + space of temp files: twice the space of the file to load...)
+ even if it will finally be ignored. TODO: fix this; this can be
+ done by testing rules in Create_file_log_event::do_apply_event()
+ and then discarding Append_block and al. Another way is do the
+ filtering in the I/O thread (more efficient: no disk writes at
+ all).
+
+
+ Note: We do not need to execute reset_one_shot_variables() if this
+ db_ok() test fails.
+ Reason: The db stored in binlog events is the same for SET and for
+ its companion query. If the SET is ignored because of
+ db_ok(), the companion query will also be ignored, and if
+ the companion query is ignored in the db_ok() test of
+ ::do_apply_event(), then the companion SET also have so
+ we don't need to reset_one_shot_variables().
+ */
+ if (rpl_filter->db_ok(thd->db.str))
+ {
+ thd->set_time(when, when_sec_part);
+ thd->set_query_id(next_query_id());
+ thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
+
+ TABLE_LIST tables;
+ LEX_CSTRING db_name= { thd->strmake(thd->db.str, thd->db.length), thd->db.length };
+ if (lower_case_table_names)
+ my_casedn_str(system_charset_info, (char *)table_name);
+ LEX_CSTRING tbl_name= { table_name, strlen(table_name) };
+ tables.init_one_table(&db_name, &tbl_name, 0, TL_WRITE);
+ tables.updating= 1;
+
+ // the table will be opened in mysql_load
+ if (rpl_filter->is_on() && !rpl_filter->tables_ok(thd->db.str, &tables))
+ {
+ // TODO: this is a bug - this needs to be moved to the I/O thread
+ if (net)
+ skip_load_data_infile(net);
+ }
+ else
+ {
+ enum enum_duplicates handle_dup;
+ bool ignore= 0;
+ char query_buffer[1024];
+ String query_str(query_buffer, sizeof(query_buffer), system_charset_info);
+ char *load_data_query;
+
+ query_str.length(0);
+ /*
+ Forge LOAD DATA INFILE query which will be used in SHOW PROCESS LIST
+ and written to slave's binlog if binlogging is on.
+ */
+ print_query(thd, FALSE, NULL, &query_str, NULL, NULL, NULL);
+ if (!(load_data_query= (char *)thd->strmake(query_str.ptr(),
+ query_str.length())))
+ {
+ /*
+ This will set thd->fatal_error in case of OOM. So we surely will notice
+ that something is wrong.
+ */
+ goto error;
+ }
+
+ thd->set_query(load_data_query, (uint) (query_str.length()));
+
+ if (sql_ex.opt_flags & REPLACE_FLAG)
+ handle_dup= DUP_REPLACE;
+ else if (sql_ex.opt_flags & IGNORE_FLAG)
+ {
+ ignore= 1;
+ handle_dup= DUP_ERROR;
+ }
+ else
+ {
+ /*
+ When replication is running fine, if it was DUP_ERROR on the
+ master then we could choose IGNORE here, because if DUP_ERROR
+ suceeded on master, and data is identical on the master and slave,
+ then there should be no uniqueness errors on slave, so IGNORE is
+ the same as DUP_ERROR. But in the unlikely case of uniqueness errors
+ (because the data on the master and slave happen to be different
+ (user error or bug), we want LOAD DATA to print an error message on
+ the slave to discover the problem.
+
+ If reading from net (a 3.23 master), mysql_load() will change this
+ to IGNORE.
+ */
+ handle_dup= DUP_ERROR;
+ }
+ /*
+ We need to set thd->lex->sql_command and thd->lex->duplicates
+ since InnoDB tests these variables to decide if this is a LOAD
+ DATA ... REPLACE INTO ... statement even though mysql_parse()
+ is not called. This is not needed in 5.0 since there the LOAD
+ DATA ... statement is replicated using mysql_parse(), which
+ sets the thd->lex fields correctly.
+ */
+ thd->lex->sql_command= SQLCOM_LOAD;
+ thd->lex->duplicates= handle_dup;
+
+ sql_exchange ex((char*)fname, sql_ex.opt_flags & DUMPFILE_FLAG);
+ String field_term(sql_ex.field_term,sql_ex.field_term_len,log_cs);
+ String enclosed(sql_ex.enclosed,sql_ex.enclosed_len,log_cs);
+ String line_term(sql_ex.line_term,sql_ex.line_term_len,log_cs);
+ String line_start(sql_ex.line_start,sql_ex.line_start_len,log_cs);
+ String escaped(sql_ex.escaped,sql_ex.escaped_len, log_cs);
+ ex.field_term= &field_term;
+ ex.enclosed= &enclosed;
+ ex.line_term= &line_term;
+ ex.line_start= &line_start;
+ ex.escaped= &escaped;
+
+ ex.opt_enclosed = (sql_ex.opt_flags & OPT_ENCLOSED_FLAG);
+ if (sql_ex.empty_flags & FIELD_TERM_EMPTY)
+ ex.field_term->length(0);
+
+ ex.skip_lines = skip_lines;
+ List<Item> field_list;
+ thd->lex->first_select_lex()->context.resolve_in_table_list_only(&tables);
+ set_fields(tables.db.str,
+ field_list, &thd->lex->first_select_lex()->context);
+ thd->variables.pseudo_thread_id= thread_id;
+ if (net)
+ {
+ // mysql_load will use thd->net to read the file
+ thd->net.vio = net->vio;
+ // Make sure the client does not get confused about the packet sequence
+ thd->net.pkt_nr = net->pkt_nr;
+ }
+ /*
+ It is safe to use tmp_list twice because we are not going to
+ update it inside mysql_load().
+ */
+ List<Item> tmp_list;
+ if (thd->open_temporary_tables(&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)
+ {
+ /* log_pos is the position of the LOAD event in the master log */
+ sql_print_warning("Slave: load data infile on table '%s' at "
+ "log position %llu in log '%s' produced %ld "
+ "warning(s). Default database: '%s'",
+ (char*) table_name, log_pos, RPL_LOG_NAME,
+ (ulong) thd->cuted_fields,
+ thd->get_db());
+ }
+ if (net)
+ net->pkt_nr= thd->net.pkt_nr;
+ }
+ }
+ else
+ {
+ /*
+ We will just ask the master to send us /dev/null if we do not
+ want to load the data.
+ TODO: this a bug - needs to be done in I/O thread
+ */
+ if (net)
+ skip_load_data_infile(net);
+ }
+
+error:
+ thd->net.vio = 0;
+ const char *remember_db= thd->get_db();
+ thd->catalog= 0;
+ thd->set_db(&null_clex_str); /* will free the current database */
+ thd->reset_query();
+ thd->get_stmt_da()->set_overwrite_status(true);
+ thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
+ 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
+ perform it and release metadata locks.
+ - If inside a multi-statement transaction,
+ defer the release of metadata locks until the current
+ transaction is either committed or rolled back. This prevents
+ other statements from modifying the table for the entire
+ duration of this transaction. This provides commit ordering
+ and guarantees serializability across multiple transactions.
+ - If in autocommit mode, or outside a transactional context,
+ automatically release metadata locks of the current statement.
+ */
+ if (thd->transaction_rollback_request)
+ {
+ trans_rollback_implicit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ else if (! thd->in_multi_stmt_transaction_mode())
+ thd->mdl_context.release_transactional_locks();
+ else
+ thd->mdl_context.release_statement_locks();
+
+ DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
+ thd->is_slave_error= 0; thd->is_fatal_error= 1;);
+
+ if (unlikely(thd->is_slave_error))
+ {
+ /* this err/sql_errno code is copy-paste from net_send_error() */
+ const char *err;
+ int sql_errno;
+ if (thd->is_error())
+ {
+ err= thd->get_stmt_da()->message();
+ sql_errno= thd->get_stmt_da()->sql_errno();
+ }
+ else
+ {
+ sql_errno=ER_UNKNOWN_ERROR;
+ err= ER_THD(thd, 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, remember_db);
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ DBUG_RETURN(1);
+ }
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+
+ if (unlikely(thd->is_fatal_error))
+ {
+ char buf[256];
+ my_snprintf(buf, sizeof(buf),
+ "Running LOAD DATA INFILE on table '%-.64s'."
+ " Default database: '%-.64s'",
+ (char*)table_name,
+ remember_db);
+
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR), buf);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rgi) );
+}
+#endif
+
+
+/**************************************************************************
+ Rotate_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void Rotate_log_event::pack_info(Protocol *protocol)
+{
+ StringBuffer<256> tmp(log_cs);
+ tmp.length(0);
+ tmp.append(new_log_ident, ident_len);
+ tmp.append(STRING_WITH_LEN(";pos="));
+ tmp.append_ulonglong(pos);
+ protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
+}
+#endif
+
+
+Rotate_log_event::Rotate_log_event(const char* new_log_ident_arg,
+ uint ident_len_arg, ulonglong pos_arg,
+ uint flags_arg)
+ :Log_event(), new_log_ident(new_log_ident_arg),
+ pos(pos_arg),ident_len(ident_len_arg ? ident_len_arg :
+ (uint) strlen(new_log_ident_arg)), flags(flags_arg)
+{
+ DBUG_ENTER("Rotate_log_event::Rotate_log_event(...,flags)");
+ DBUG_PRINT("enter",("new_log_ident: %s pos: %llu flags: %lu", new_log_ident_arg,
+ pos_arg, (ulong) flags));
+ cache_type= EVENT_NO_CACHE;
+ if (flags & DUP_NAME)
+ new_log_ident= my_strndup(PSI_INSTRUMENT_ME, new_log_ident_arg, ident_len, MYF(MY_WME));
+ if (flags & RELAY_LOG)
+ set_relay_log_event();
+ DBUG_VOID_RETURN;
+}
+
+
+bool Rotate_log_event::write()
+{
+ char buf[ROTATE_HEADER_LEN];
+ int8store(buf + R_POS_OFFSET, pos);
+ return (write_header(ROTATE_HEADER_LEN + ident_len) ||
+ write_data(buf, ROTATE_HEADER_LEN) ||
+ write_data(new_log_ident, (uint) ident_len) ||
+ write_footer());
+}
+
+
+#if defined(HAVE_REPLICATION)
+
+/*
+ Got a rotate log event from the master.
+
+ This is mainly used so that we can later figure out the logname and
+ position for the master.
+
+ We can't rotate the slave's BINlog as this will cause infinitive rotations
+ in a A -> B -> A setup.
+ The NOTES below is a wrong comment which will disappear when 4.1 is merged.
+
+ This must only be called from the Slave SQL thread, since it calls
+ Relay_log_info::flush().
+
+ @retval
+ 0 ok
+ 1 error
+*/
+int Rotate_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ int error= 0;
+ Relay_log_info *rli= rgi->rli;
+ DBUG_ENTER("Rotate_log_event::do_update_pos");
+
+ DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
+ (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: %llu", this->pos));
+
+ /*
+ If we are in a transaction or in a group: the only normal case is
+ when the I/O thread was copying a big transaction, then it was
+ stopped and restarted: we have this in the relay log:
+
+ BEGIN
+ ...
+ ROTATE (a fake one)
+ ...
+ COMMIT or ROLLBACK
+
+ In that case, we don't want to touch the coordinates which
+ 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 != global_system_variables.server_id ||
+ rli->replicate_same_server_id) &&
+ !is_relay_log_event() &&
+ !rli->is_in_group() &&
+ !rgi->is_parallel_exec)
+ {
+ mysql_mutex_lock(&rli->data_lock);
+ DBUG_PRINT("info", ("old group_master_log_name: '%s' "
+ "old group_master_log_pos: %lu",
+ rli->group_master_log_name,
+ (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, 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);
+ error= rli->flush();
+
+ /*
+ 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, rgi);
+ thd->variables.sql_mode= global_system_variables.sql_mode;
+ thd->variables.auto_increment_increment=
+ thd->variables.auto_increment_offset= 1;
+ }
+ else
+ rgi->inc_event_relay_log_pos();
+
+ DBUG_RETURN(error);
+}
+
+
+Log_event::enum_skip_reason
+Rotate_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ enum_skip_reason reason= Log_event::do_shall_skip(rgi);
+
+ switch (reason) {
+ case Log_event::EVENT_SKIP_NOT:
+ case Log_event::EVENT_SKIP_COUNT:
+ return Log_event::EVENT_SKIP_NOT;
+
+ case Log_event::EVENT_SKIP_IGNORE:
+ return Log_event::EVENT_SKIP_IGNORE;
+ }
+ DBUG_ASSERT(0);
+ return Log_event::EVENT_SKIP_NOT; // To keep compiler happy
+}
+
+#endif
+
+
+/**************************************************************************
+ Binlog_checkpoint_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void Binlog_checkpoint_log_event::pack_info(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
+
+
+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(PSI_INSTRUMENT_ME, binlog_file_name_arg, binlog_file_len_arg,
+ MYF(MY_WME))),
+ binlog_file_len(binlog_file_len_arg)
+{
+ cache_type= EVENT_NO_CACHE;
+}
+
+
+bool Binlog_checkpoint_log_event::write()
+{
+ uchar buf[BINLOG_CHECKPOINT_HEADER_LEN];
+ int4store(buf, binlog_file_len);
+ return write_header(BINLOG_CHECKPOINT_HEADER_LEN + binlog_file_len) ||
+ write_data(buf, BINLOG_CHECKPOINT_HEADER_LEN) ||
+ write_data(binlog_file_name, binlog_file_len) ||
+ write_footer();
+}
+
+
+/**************************************************************************
+ Global transaction ID stuff
+**************************************************************************/
+
+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;
+ bool is_tmp_table= thd_arg->lex->stmt_accessed_temp_table();
+ if (thd_arg->transaction->stmt.trans_did_wait() ||
+ thd_arg->transaction->all.trans_did_wait())
+ flags2|= FL_WAITED;
+ if (thd_arg->transaction->stmt.trans_did_ddl() ||
+ thd_arg->transaction->stmt.has_created_dropped_temp_table() ||
+ thd_arg->transaction->all.trans_did_ddl() ||
+ thd_arg->transaction->all.has_created_dropped_temp_table())
+ flags2|= FL_DDL;
+ else if (is_transactional && !is_tmp_table)
+ flags2|= FL_TRANSACTIONAL;
+ if (!(thd_arg->variables.option_bits & OPTION_RPL_SKIP_PARALLEL))
+ flags2|= FL_ALLOW_PARALLEL;
+ /* Preserve any DDL or WAITED flag in the slave's binlog. */
+ if (thd_arg->rgi_slave)
+ flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+
+ XID_STATE &xid_state= thd->transaction->xid_state;
+ if (is_transactional && xid_state.is_explicit_XA() &&
+ (thd->lex->sql_command == SQLCOM_XA_PREPARE ||
+ xid_state.get_state_code() == XA_PREPARED))
+ {
+ DBUG_ASSERT(thd->lex->xa_opt != XA_ONE_PHASE);
+
+ flags2|= thd->lex->sql_command == SQLCOM_XA_PREPARE ?
+ FL_PREPARED_XA : FL_COMPLETED_XA;
+ xid.set(xid_state.get_xid());
+ }
+}
+
+
+/*
+ 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,
+ enum enum_binlog_checksum_alg 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()
+{
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
+ 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
+ write_len= 13;
+
+ if (flags2 & (FL_PREPARED_XA | FL_COMPLETED_XA))
+ {
+ int4store(&buf[write_len], xid.formatID);
+ buf[write_len +4]= (uchar) xid.gtrid_length;
+ buf[write_len +4+1]= (uchar) xid.bqual_length;
+ write_len+= 6;
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+
+ if (write_len < GTID_HEADER_LEN)
+ {
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
+ write_len= GTID_HEADER_LEN;
+ }
+ return write_header(write_len) ||
+ write_data(buf, write_len) ||
+ write_footer();
+}
+
+
+/*
+ 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,
+ enum enum_binlog_checksum_alg 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(Protocol *protocol)
+{
+ char buf[6+5+10+1+10+1+20+1+4+20+1+ ser_buf_size+5 /* sprintf */];
+ char *p;
+ p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " :
+ flags2 & FL_PREPARED_XA ? "XA START " : "BEGIN GTID "));
+ if (flags2 & FL_PREPARED_XA)
+ {
+ p+= sprintf(p, "%s GTID ", xid.serialize());
+ }
+ 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)
+{
+ ulonglong bits= thd->variables.option_bits;
+ thd->variables.server_id= this->server_id;
+ thd->variables.gtid_domain_id= this->domain_id;
+ thd->variables.gtid_seq_no= this->seq_no;
+ rgi->gtid_ev_flags2= flags2;
+ thd->reset_for_next_command();
+
+ 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((bits & OPTION_GTID_BEGIN) == 0);
+
+ Master_info *mi=rgi->rli->mi;
+ switch (flags2 & (FL_DDL | FL_TRANSACTIONAL))
+ {
+ case FL_TRANSACTIONAL:
+ mi->total_trans_groups++;
+ break;
+ case FL_DDL:
+ mi->total_ddl_groups++;
+ break;
+ default:
+ mi->total_non_trans_groups++;
+ }
+
+ if (flags2 & FL_STANDALONE)
+ return 0;
+
+ /* Execute this like a BEGIN query event. */
+ bits|= OPTION_GTID_BEGIN;
+ if (flags2 & FL_ALLOW_PARALLEL)
+ bits&= ~(ulonglong)OPTION_RPL_SKIP_PARALLEL;
+ else
+ bits|= (ulonglong)OPTION_RPL_SKIP_PARALLEL;
+ thd->variables.option_bits= bits;
+ DBUG_PRINT("info", ("Set OPTION_GTID_BEGIN"));
+ thd->is_slave_error= 0;
+
+ char buf_xa[sizeof("XA START") + 1 + ser_buf_size];
+ if (flags2 & FL_PREPARED_XA)
+ {
+ const char fmt[]= "XA START %s";
+
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ sprintf(buf_xa, fmt, xid.serialize());
+ thd->set_query_and_id(buf_xa, static_cast<uint32>(strlen(buf_xa)),
+ &my_charset_bin, next_query_id());
+ thd->lex->sql_command= SQLCOM_XA_START;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ 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;
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
+ 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 */
+
+
+
+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(PSI_INSTRUMENT_ME,
+ 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(PSI_INSTRUMENT_ME,
+ count * sizeof(*list) + (count == 0), MYF(MY_WME))))
+ {
+ gtid_set->get_gtid_list(list, count);
+#if defined(HAVE_REPLICATION)
+ if (gl_flags & FLAG_IGN_GTIDS)
+ {
+ uint32 i;
+
+ if (!(sub_id_list= (uint64 *)my_malloc(PSI_INSTRUMENT_ME,
+ 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)
+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()
+{
+ char buf[128];
+ String packet(buf, sizeof(buf), system_charset_info);
+
+ packet.length(0);
+ if (to_packet(&packet))
+ return true;
+ return write_header(get_data_size()) ||
+ write_data(packet.ptr(), packet.length()) ||
+ write_footer();
+}
+
+
+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)
+ {
+ void *hton= NULL;
+ 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, &hton)))
+ return ret;
+ rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i],
+ hton, 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;
+ }
+ free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC));
+ 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(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 */
+
+
+
+/**************************************************************************
+ Intvar_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void Intvar_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256], *pos;
+ pos= strmake(buf, get_var_type_name(), sizeof(buf)-23);
+ *pos++= '=';
+ pos= longlong10_to_str(val, pos, -10);
+ protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
+}
+#endif
+
+
+bool Intvar_log_event::write()
+{
+ uchar buf[9];
+ buf[I_TYPE_OFFSET]= (uchar) type;
+ int8store(buf + I_VAL_OFFSET, val);
+ return write_header(sizeof(buf)) ||
+ write_data(buf, sizeof(buf)) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+
+/*
+ Intvar_log_event::do_apply_event()
+*/
+
+int Intvar_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Intvar_log_event::do_apply_event");
+ if (rgi->deferred_events_collecting)
+ {
+ DBUG_PRINT("info",("deferring event"));
+ DBUG_RETURN(rgi->deferred_events->add(this));
+ }
+
+ switch (type) {
+ case LAST_INSERT_ID_EVENT:
+ thd->first_successful_insert_id_in_prev_stmt= val;
+ DBUG_PRINT("info",("last_insert_id_event: %ld", (long) val));
+ break;
+ case INSERT_ID_EVENT:
+ thd->force_one_auto_inc_interval(val);
+ break;
+ }
+ DBUG_RETURN(0);
+}
+
+int Intvar_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+
+
+Log_event::enum_skip_reason
+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
+ 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1, we
+ just say that this event should be skipped by ignoring it, meaning
+ 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(rgi);
+}
+
+#endif
+
+
+/**************************************************************************
+ Rand_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void Rand_log_event::pack_info(Protocol *protocol)
+{
+ char buf1[256], *pos;
+ pos= strmov(buf1,"rand_seed1=");
+ pos= int10_to_str((long) seed1, pos, 10);
+ pos= strmov(pos, ",rand_seed2=");
+ pos= int10_to_str((long) seed2, pos, 10);
+ protocol->store(buf1, (uint) (pos-buf1), &my_charset_bin);
+}
+#endif
+
+
+bool Rand_log_event::write()
+{
+ uchar buf[16];
+ int8store(buf + RAND_SEED1_OFFSET, seed1);
+ int8store(buf + RAND_SEED2_OFFSET, seed2);
+ return write_header(sizeof(buf)) ||
+ write_data(buf, sizeof(buf)) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+int Rand_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ 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(rpl_group_info *rgi)
+{
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+
+
+Log_event::enum_skip_reason
+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
+ 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1, we
+ just say that this event should be skipped by ignoring it, meaning
+ 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(rgi);
+}
+
+/**
+ Exec deferred Int-, Rand- and User- var events prefixing
+ a Query-log-event event.
+
+ @param thd THD handle
+
+ @return false on success, true if a failure in an event applying occurred.
+*/
+bool slave_execute_deferred_events(THD *thd)
+{
+ bool res= false;
+ rpl_group_info *rgi= thd->rgi_slave;
+
+ DBUG_ASSERT(rgi && (!rgi->deferred_events_collecting || rgi->deferred_events));
+
+ if (!rgi->deferred_events_collecting || rgi->deferred_events->is_empty())
+ return res;
+
+ res= rgi->deferred_events->execute(rgi);
+ rgi->deferred_events->rewind();
+
+ return res;
+}
+
+#endif /* HAVE_REPLICATION */
+
+
+/**************************************************************************
+ Xid_apply_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+
+int Xid_apply_log_event::do_record_gtid(THD *thd, rpl_group_info *rgi,
+ bool in_trans, void **out_hton)
+{
+ int err= 0;
+ Relay_log_info const *rli= rgi->rli;
+
+ rgi->gtid_pending= false;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, &rgi->current_gtid,
+ rgi->gtid_sub_id,
+ in_trans, false, out_hton);
+
+ if (unlikely(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;
+}
+
+int Xid_apply_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ bool res;
+ int err;
+ uint64 sub_id= 0;
+ void *hton= NULL;
+ rpl_gtid gtid;
+
+ /*
+ An instance of this class such as XID_EVENT works like a COMMIT
+ statement. It updates mysql.gtid_slave_pos with the GTID of the
+ current transaction.
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ THD::reset_for_next_command() as if starting a new statement.
+
+ XA_PREPARE_LOG_EVENT also updates the gtid table *but* the update gets
+ committed as separate "autocommit" transaction.
+ */
+ thd->reset_for_next_command();
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+#ifdef WITH_WSREP
+ thd->wsrep_affected_rows= 0;
+#endif
+
+ if (rgi->gtid_pending)
+ {
+ sub_id= rgi->gtid_sub_id;
+ gtid= rgi->current_gtid;
+
+ if (!thd->transaction->xid_state.is_explicit_XA())
+ {
+ if ((err= do_record_gtid(thd, rgi, true /* in_trans */, &hton)))
+ 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;
+ });
+ }
+ }
+
+ general_log_print(thd, COM_QUERY, get_query());
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+ res= do_commit();
+ if (!res && rgi->gtid_pending)
+ {
+ DBUG_ASSERT(!thd->transaction->xid_state.is_explicit_XA());
+
+ if ((err= do_record_gtid(thd, rgi, false, &hton)))
+ return err;
+ }
+
+#ifdef WITH_WSREP
+ if (WSREP(thd)) mysql_mutex_lock(&thd->LOCK_thd_data);
+ if ((!res || (WSREP(thd) && thd->wsrep_trx().state() == wsrep::transaction::s_must_replay )) && sub_id)
+#else
+ if (likely(!res) && sub_id)
+#endif /* WITH_WSREP */
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, rgi);
+#ifdef WITH_WSREP
+ if (WSREP(thd)) mysql_mutex_unlock(&thd->LOCK_thd_data);
+#endif /* WITH_WSREP */
+ /*
+ Increment the global status commit count variable
+ */
+ enum enum_sql_command cmd= !thd->transaction->xid_state.is_explicit_XA() ?
+ SQLCOM_COMMIT : SQLCOM_XA_PREPARE;
+ status_var_increment(thd->status_var.com_stat[cmd]);
+
+ return res;
+}
+
+Log_event::enum_skip_reason
+Xid_apply_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Xid_apply_log_event::do_shall_skip");
+ 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
+ else if (wsrep_mysql_replication_bundle && WSREP(thd) &&
+ opt_slave_domain_parallel_threads == 0)
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+#endif /* HAVE_REPLICATION */
+
+/**************************************************************************
+ Xid_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void Xid_log_event::pack_info(Protocol *protocol)
+{
+ char buf[128], *pos;
+ pos= strmov(buf, "COMMIT /* xid=");
+ pos= longlong10_to_str(xid, pos, 10);
+ pos= strmov(pos, " */");
+ protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
+}
+
+
+int Xid_log_event::do_commit()
+{
+ bool res;
+ res= trans_commit(thd); /* Automatically rolls back on error. */
+ thd->mdl_context.release_transactional_locks();
+ return res;
+}
+#endif
+
+
+bool Xid_log_event::write()
+{
+ DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
+ return write_header(sizeof(xid)) ||
+ write_data((uchar*)&xid, sizeof(xid)) ||
+ write_footer();
+}
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + ser_buf_size];
+
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ m_xid.serialize());
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+
+
+int XA_prepare_log_event::do_commit()
+{
+ int res;
+ xid_t xid;
+ xid.set(m_xid.formatID,
+ m_xid.data, m_xid.gtrid_length,
+ m_xid.data + m_xid.gtrid_length, m_xid.bqual_length);
+
+ thd->lex->xid= &xid;
+ if (!one_phase)
+ {
+ if ((res= thd->wait_for_prior_commit()))
+ return res;
+
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ res= trans_xa_prepare(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+ return res;
+}
+#endif // HAVE_REPLICATION
+
+
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4]= {one_phase,};
+ uint8 one_phase_byte= one_phase;
+
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_subheader_no_data == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_subheader_no_data +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+
+
+/**************************************************************************
+ User_var_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+static bool
+user_var_append_name_part(THD *thd, String *buf,
+ const char *name, size_t name_len)
+{
+ return buf->append("@") ||
+ append_identifier(thd, buf, name, name_len) ||
+ buf->append("=");
+}
+
+void User_var_log_event::pack_info(Protocol* protocol)
+{
+ if (is_null)
+ {
+ char buf_mem[FN_REFLEN+7];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.length(0);
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ buf.append("NULL"))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
+ }
+ else
+ {
+ switch (type) {
+ case REAL_RESULT:
+ {
+ double real_val;
+ char buf2[MY_GCVT_MAX_FIELD_WIDTH+1];
+ char buf_mem[FN_REFLEN + MY_GCVT_MAX_FIELD_WIDTH + 1];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ float8get(real_val, val);
+ buf.length(0);
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ buf.append(buf2, my_gcvt(real_val, MY_GCVT_ARG_DOUBLE,
+ MY_GCVT_MAX_FIELD_WIDTH, buf2, NULL)))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
+ break;
+ }
+ case INT_RESULT:
+ {
+ char buf2[22];
+ char buf_mem[FN_REFLEN + 22];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.length(0);
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ buf.append(buf2,
+ longlong10_to_str(uint8korr(val), buf2,
+ ((flags & User_var_log_event::UNSIGNED_F) ? 10 : -10))-buf2))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ char buf_mem[FN_REFLEN + DECIMAL_MAX_STR_LENGTH];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ char buf2[DECIMAL_MAX_STR_LENGTH+1];
+ String str(buf2, sizeof(buf2), &my_charset_bin);
+ buf.length(0);
+ my_decimal((const uchar *) (val + 2), val[0], val[1]).to_string(&str);
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ buf.append(buf2))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
+ break;
+ }
+ case STRING_RESULT:
+ {
+ /* 15 is for 'COLLATE' and other chars */
+ char buf_mem[FN_REFLEN + 512 + 1 + 2*MY_CS_NAME_SIZE+15];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ CHARSET_INFO *cs;
+ buf.length(0);
+ if (!(cs= get_charset(charset_number, MYF(0))))
+ {
+ if (buf.append("???"))
+ return;
+ }
+ else
+ {
+ size_t old_len;
+ char *beg, *end;
+ if (user_var_append_name_part(protocol->thd, &buf, name, name_len) ||
+ buf.append("_") ||
+ buf.append(cs->csname) ||
+ buf.append(" "))
+ return;
+ old_len= buf.length();
+ if (buf.reserve(old_len + val_len * 2 + 3 + sizeof(" COLLATE ") +
+ MY_CS_NAME_SIZE))
+ return;
+ beg= const_cast<char *>(buf.ptr()) + old_len;
+ end= str_to_hex(beg, val, val_len);
+ buf.length(old_len + (end - beg));
+ if (buf.append(" COLLATE ") ||
+ buf.append(cs->name))
+ return;
+ }
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
+ break;
+ }
+ case ROW_RESULT:
+ default:
+ DBUG_ASSERT(0);
+ return;
+ }
+ }
+}
+#endif // HAVE_REPLICATION
+
+
+bool User_var_log_event::write()
+{
+ 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[MY_MAX(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
+ uint unsigned_len= 0;
+ uint buf1_length;
+ size_t event_length;
+
+ int4store(buf, name_len);
+
+ if ((buf1[0]= is_null))
+ {
+ buf1_length= 1;
+ val_len= 0; // Length of 'pos'
+ }
+ else
+ {
+ buf1[1]= type;
+ int4store(buf1 + 2, charset_number);
+
+ switch (type) {
+ case REAL_RESULT:
+ float8store(buf2, *(double*) val);
+ break;
+ case INT_RESULT:
+ int8store(buf2, *(longlong*) val);
+ unsigned_len= 1;
+ break;
+ case DECIMAL_RESULT:
+ {
+ my_decimal *dec= (my_decimal *)val;
+ dec->fix_buffer_pointer();
+ buf2[0]= (char)(dec->intg + dec->frac);
+ buf2[1]= (char)dec->frac;
+ decimal2bin((decimal_t*)val, buf2+2, buf2[0], buf2[1]);
+ val_len= decimal_bin_size(buf2[0], buf2[1]) + 2;
+ break;
+ }
+ case STRING_RESULT:
+ pos= (uchar*) val;
+ break;
+ case ROW_RESULT:
+ default:
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ int4store(buf1 + 2 + UV_CHARSET_NUMBER_SIZE, val_len);
+ buf1_length= 10;
+ }
+
+ /* Length of the whole event */
+ event_length= sizeof(buf)+ name_len + buf1_length + val_len + unsigned_len;
+
+ return write_header(event_length) ||
+ write_data(buf, sizeof(buf)) ||
+ write_data(name, name_len) ||
+ write_data(buf1, buf1_length) ||
+ write_data(pos, val_len) ||
+ write_data(&flags, unsigned_len) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+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 (rgi->deferred_events_collecting)
+ {
+ set_deferred(current_thd->query_id);
+ DBUG_RETURN(rgi->deferred_events->add(this));
+ }
+ else if (is_deferred())
+ {
+ sav_query_id= current_thd->query_id;
+ current_thd->query_id= query_id; /* recreating original time context */
+ }
+
+ if (!(charset= get_charset(charset_number, MYF(MY_WME))))
+ {
+ rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "Invalid character set for User var event");
+ DBUG_RETURN(1);
+ }
+ LEX_CSTRING user_var_name;
+ user_var_name.str= name;
+ user_var_name.length= name_len;
+ double real_val;
+ longlong int_val;
+
+ if (is_null)
+ {
+ it= new (thd->mem_root) Item_null(thd);
+ }
+ else
+ {
+ switch (type) {
+ case REAL_RESULT:
+ if (val_len != 8)
+ {
+ rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "Invalid variable length at User var event");
+ return 1;
+ }
+ float8get(real_val, val);
+ it= new (thd->mem_root) Item_float(thd, real_val, 0);
+ val= (char*) &real_val; // Pointer to value in native format
+ val_len= 8;
+ break;
+ case INT_RESULT:
+ if (val_len != 8)
+ {
+ rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "Invalid variable length at User var event");
+ return 1;
+ }
+ int_val= (longlong) uint8korr(val);
+ it= new (thd->mem_root) Item_int(thd, int_val);
+ val= (char*) &int_val; // Pointer to value in native format
+ val_len= 8;
+ break;
+ case DECIMAL_RESULT:
+ {
+ if (val_len < 3)
+ {
+ rgi->rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR),
+ "Invalid variable length at User var event");
+ return 1;
+ }
+ Item_decimal *dec= new (thd->mem_root) Item_decimal(thd, (uchar*) val+2, val[0], val[1]);
+ it= dec;
+ val= (char *)dec->val_decimal(NULL);
+ val_len= sizeof(my_decimal);
+ break;
+ }
+ case STRING_RESULT:
+ it= new (thd->mem_root) Item_string(thd, val, (uint)val_len, charset);
+ break;
+ case ROW_RESULT:
+ default:
+ DBUG_ASSERT(0);
+ DBUG_RETURN(0);
+ }
+ }
+
+ Item_func_set_user_var *e= new (thd->mem_root) Item_func_set_user_var(thd, &user_var_name, it);
+ /*
+ Item_func_set_user_var can't substitute something else on its place =>
+ 0 can be passed as last argument (reference on item)
+
+ Fix_fields() can fail, in which case a call of update_hash() might
+ crash the server, so if fix fields fails, we just return with an
+ error.
+ */
+ if (e->fix_fields(thd, 0))
+ DBUG_RETURN(1);
+
+ /*
+ A variable can just be considered as a table with
+ a single record and with a single column. Thus, like
+ a column value, it could always have IMPLICIT derivation.
+ */
+ e->update_hash((void*) val, val_len, type, charset,
+ (flags & User_var_log_event::UNSIGNED_F));
+ if (!is_deferred())
+ free_root(thd->mem_root, 0);
+ else
+ current_thd->query_id= sav_query_id; /* restore current query's context */
+
+ DBUG_RETURN(0);
+}
+
+int User_var_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+
+Log_event::enum_skip_reason
+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
+ of 2 when recovering from an insert which used a auto increment,
+ rand, or user var. Therefore, if the slave skip counter is 1, we
+ just say that this event should be skipped by ignoring it, meaning
+ 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(rgi);
+}
+#endif // HAVE_REPLICATION
+
+
+#ifdef HAVE_REPLICATION
+
+/**************************************************************************
+ Stop_log_event methods
+**************************************************************************/
+
+/*
+ The master stopped. We used to clean up all temporary tables but
+ this is useless as, as the master has shut down properly, it has
+ written all DROP TEMPORARY TABLE (prepared statements' deletion is
+ TODO only when we binlog prep stmts). We used to clean up
+ slave_load_tmpdir, but this is useless as it has been cleared at the
+ end of LOAD DATA INFILE. So we have nothing to do here. The place
+ were we must do this cleaning is in
+ Start_log_event_v3::do_apply_event(), not here. Because if we come
+ here, the master was sane.
+
+ This must only be called from the Slave SQL thread, since it calls
+ Relay_log_info::flush().
+*/
+
+int Stop_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ int error= 0;
+ 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.
+ If we updated it, we will have incorrect master coordinates and this
+ could give false triggers in MASTER_POS_WAIT() that we have reached
+ the target position when in fact we have not.
+ */
+ if (rli->get_flag(Relay_log_info::IN_TRANSACTION))
+ rgi->inc_event_relay_log_pos();
+ else if (!rgi->is_parallel_exec)
+ {
+ rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi);
+ rli->inc_group_relay_log_pos(0, rgi);
+ if (rli->flush())
+ error= 1;
+ }
+ DBUG_RETURN(error);
+}
+
+#endif /* HAVE_REPLICATION */
+
+
+/**************************************************************************
+ Create_file_log_event methods
+**************************************************************************/
+
+Create_file_log_event::
+Create_file_log_event(THD* thd_arg, sql_exchange* ex,
+ const char* db_arg, const char* table_name_arg,
+ List<Item>& fields_arg,
+ bool is_concurrent_arg,
+ enum enum_duplicates handle_dup,
+ bool ignore,
+ uchar* block_arg, uint block_len_arg, bool using_trans)
+ :Load_log_event(thd_arg, ex, db_arg, table_name_arg, fields_arg,
+ is_concurrent_arg,
+ handle_dup, ignore, using_trans),
+ fake_base(0), block(block_arg), event_buf(0), block_len(block_len_arg),
+ file_id(thd_arg->file_id = mysql_bin_log.next_file_id())
+{
+ DBUG_ENTER("Create_file_log_event");
+ sql_ex.force_new_format();
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Create_file_log_event::write_data_body()
+*/
+
+bool Create_file_log_event::write_data_body()
+{
+ bool res;
+ if ((res= Load_log_event::write_data_body()) || fake_base)
+ return res;
+ return write_data("", 1) ||
+ write_data(block, block_len);
+}
+
+
+/*
+ Create_file_log_event::write_data_header()
+*/
+
+bool Create_file_log_event::write_data_header()
+{
+ bool res;
+ uchar buf[CREATE_FILE_HEADER_LEN];
+ if ((res= Load_log_event::write_data_header()) || fake_base)
+ return res;
+ int4store(buf + CF_FILE_ID_OFFSET, file_id);
+ return write_data(buf, CREATE_FILE_HEADER_LEN) != 0;
+}
+
+
+/*
+ Create_file_log_event::write_base()
+*/
+
+bool Create_file_log_event::write_base()
+{
+ bool res;
+ fake_base= 1; // pretend we are Load event
+ res= write();
+ fake_base= 0;
+ return res;
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Create_file_log_event::pack_info(Protocol *protocol)
+{
+ char buf[SAFE_NAME_LEN*2 + 30 + 21*2], *pos;
+ pos= strmov(buf, "db=");
+ memcpy(pos, db, db_len);
+ pos= strmov(pos + db_len, ";table=");
+ memcpy(pos, table_name, table_name_len);
+ pos= strmov(pos + table_name_len, ";file_id=");
+ pos= int10_to_str((long) file_id, pos, 10);
+ pos= strmov(pos, ";block_len=");
+ pos= int10_to_str((long) block_len, pos, 10);
+ protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
+}
+#endif /* defined(HAVE_REPLICATION) */
+
+
+/**
+ Create_file_log_event::do_apply_event()
+ Constructor for Create_file_log_event to intantiate an event
+ from the relay log on the slave.
+
+ @retval
+ 0 Success
+ @retval
+ 1 Failure
+*/
+
+#if defined(HAVE_REPLICATION)
+int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ char fname_buf[FN_REFLEN];
+ char *ext;
+ int fd = -1;
+ IO_CACHE file;
+ Log_event_writer lew(&file, 0);
+ 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));
+ 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,
+ fname_buf, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0 ||
+ 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, rgi->gtid_info(),
+ "Error in Create_file event: could not open file '%s'",
+ fname_buf);
+ goto err;
+ }
+
+ // a trick to avoid allocating another buffer
+ fname= fname_buf;
+ fname_len= (uint) (strmov(ext, ".data") - fname);
+ writer= &lew;
+ if (write_base())
+ {
+ strmov(ext, ".info"); // to have it right in the error message
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
+ "Error in Create_file event: could not write to file '%s'",
+ fname_buf);
+ goto err;
+ }
+ end_io_cache(&file);
+ mysql_file_close(fd, MYF(0));
+
+ // fname_buf now already has .data, not .info, because we did our trick
+ /* old copy may exist already */
+ mysql_file_delete(key_file_log_event_data, fname_buf, MYF(0));
+ if ((fd= mysql_file_create(key_file_log_event_data,
+ fname_buf, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
+ {
+ 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, rgi->gtid_info(),
+ "Error in Create_file event: write to '%s' failed",
+ fname_buf);
+ goto err;
+ }
+ error=0; // Everything is ok
+
+err:
+ if (unlikely(error))
+ end_io_cache(&file);
+ if (likely(fd >= 0))
+ mysql_file_close(fd, MYF(0));
+ return error != 0;
+}
+#endif /* defined(HAVE_REPLICATION) */
+
+
+/**************************************************************************
+ Append_block_log_event methods
+**************************************************************************/
+
+Append_block_log_event::Append_block_log_event(THD *thd_arg,
+ const char *db_arg,
+ uchar *block_arg,
+ uint block_len_arg,
+ bool using_trans)
+ :Log_event(thd_arg,0, using_trans), block(block_arg),
+ block_len(block_len_arg), file_id(thd_arg->file_id), db(db_arg)
+{
+}
+
+
+bool Append_block_log_event::write()
+{
+ uchar buf[APPEND_BLOCK_HEADER_LEN];
+ int4store(buf + AB_FILE_ID_OFFSET, file_id);
+ return write_header(APPEND_BLOCK_HEADER_LEN + block_len) ||
+ write_data(buf, APPEND_BLOCK_HEADER_LEN) ||
+ write_data(block, block_len) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Append_block_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ uint length;
+ length= (uint) sprintf(buf, ";file_id=%u;block_len=%u", file_id, block_len);
+ protocol->store(buf, length, &my_charset_bin);
+}
+
+
+/*
+ Append_block_log_event::get_create_or_append()
+*/
+
+int Append_block_log_event::get_create_or_append() const
+{
+ return 0; /* append to the file, fail if not exists */
+}
+
+/*
+ Append_block_log_event::do_apply_event()
+*/
+
+int Append_block_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ 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");
+
+ 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())
+ {
+ /*
+ Usually lex_start() is called by mysql_parse(), but we need it here
+ as the present method does not call mysql_parse().
+ */
+ lex_start(thd);
+ thd->reset_for_next_command();
+ /* old copy may exist already */
+ mysql_file_delete(key_file_log_event_data, fname, MYF(0));
+ if ((fd= mysql_file_create(key_file_log_event_data,
+ fname, CREATE_MODE,
+ O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
+ {
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
+ "Error in %s event: could not create file '%s'",
+ get_type_str(), fname);
+ goto err;
+ }
+ }
+ else if ((fd= mysql_file_open(key_file_log_event_data,
+ fname,
+ O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
+ MYF(MY_WME))) < 0)
+ {
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
+ "Error in %s event: could not open file '%s'",
+ get_type_str(), fname);
+ goto err;
+ }
+
+ DBUG_EXECUTE_IF("remove_slave_load_file_before_write",
+ {
+ my_delete(fname, MYF(0));
+ });
+
+ if (mysql_file_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
+ {
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
+ "Error in %s event: write to '%s' failed",
+ get_type_str(), fname);
+ goto err;
+ }
+ error=0;
+
+err:
+ if (fd >= 0)
+ mysql_file_close(fd, MYF(0));
+ DBUG_RETURN(error);
+}
+#endif // HAVE_REPLICATION
+
+
+/**************************************************************************
+ Delete_file_log_event methods
+**************************************************************************/
+
+Delete_file_log_event::Delete_file_log_event(THD *thd_arg, const char* db_arg,
+ bool using_trans)
+ :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
+{
+}
+
+
+bool Delete_file_log_event::write()
+{
+ uchar buf[DELETE_FILE_HEADER_LEN];
+ int4store(buf + DF_FILE_ID_OFFSET, file_id);
+ return write_header(sizeof(buf)) ||
+ write_data(buf, sizeof(buf)) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Delete_file_log_event::pack_info(Protocol *protocol)
+{
+ char buf[64];
+ uint length;
+ length= (uint) sprintf(buf, ";file_id=%u", (uint) file_id);
+ protocol->store(buf, (int32) length, &my_charset_bin);
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+int Delete_file_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ char fname[FN_REFLEN+10];
+ 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));
+ return 0;
+}
+#endif /* defined(HAVE_REPLICATION) */
+
+
+/**************************************************************************
+ Execute_load_log_event methods
+**************************************************************************/
+
+Execute_load_log_event::Execute_load_log_event(THD *thd_arg,
+ const char* db_arg,
+ bool using_trans)
+ :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id), db(db_arg)
+{
+}
+
+
+bool Execute_load_log_event::write()
+{
+ uchar buf[EXEC_LOAD_HEADER_LEN];
+ int4store(buf + EL_FILE_ID_OFFSET, file_id);
+ return write_header(sizeof(buf)) ||
+ write_data(buf, sizeof(buf)) ||
+ write_footer();
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Execute_load_log_event::pack_info(Protocol *protocol)
+{
+ char buf[64];
+ uint length;
+ length= (uint) sprintf(buf, ";file_id=%u", (uint) file_id);
+ protocol->store(buf, (int32) length, &my_charset_bin);
+}
+
+
+/*
+ Execute_load_log_event::do_apply_event()
+*/
+
+int Execute_load_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ char fname[FN_REFLEN+10];
+ char *ext;
+ int fd;
+ 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",
+ &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, rgi->gtid_info(),
+ "Error in Exec_load event: could not open file '%s'",
+ fname);
+ goto err;
+ }
+ if (!(lev= (Load_log_event*)
+ Log_event::read_log_event(&file,
+ rli->relay_log.description_event_for_exec,
+ opt_slave_sql_verify_checksum)) ||
+ lev->get_type_code() != NEW_LOAD_EVENT)
+ {
+ rli->report(ERROR_LEVEL, 0, rgi->gtid_info(), "Error in Exec_load event: "
+ "file '%s' appears corrupted", fname);
+ goto err;
+ }
+ lev->thd = thd;
+ /*
+ lev->do_apply_event should use rli only for errors i.e. should
+ not advance rli's position.
+
+ lev->do_apply_event is the place where the table is loaded (it
+ calls mysql_load()).
+ */
+
+ if (lev->do_apply_event(0,rgi,1))
+ {
+ /*
+ We want to indicate the name of the file that could not be loaded
+ (SQL_LOADxxx).
+ But as we are here we are sure the error is in rli->last_slave_error and
+ rli->last_slave_errno (example of error: duplicate entry for key), so we
+ don't want to overwrite it with the filename.
+ What we want instead is add the filename to the current error message.
+ */
+ char *tmp= my_strdup(PSI_INSTRUMENT_ME, rli->last_error().message, MYF(MY_WME));
+ if (tmp)
+ {
+ rli->report(ERROR_LEVEL, rli->last_error().number, rgi->gtid_info(),
+ "%s. Failed executing load from '%s'", tmp, fname);
+ my_free(tmp);
+ }
+ goto err;
+ }
+ /*
+ We have an open file descriptor to the .info file; we need to close it
+ or Windows will refuse to delete the file in mysql_file_delete().
+ */
+ if (fd >= 0)
+ {
+ mysql_file_close(fd, MYF(0));
+ end_io_cache(&file);
+ fd= -1;
+ }
+ mysql_file_delete(key_file_log_event_info, fname, MYF(MY_WME));
+ memcpy(ext, ".data", 6);
+ mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
+ error = 0;
+
+err:
+ delete lev;
+ if (fd >= 0)
+ {
+ mysql_file_close(fd, MYF(0));
+ end_io_cache(&file);
+ }
+ return error;
+}
+
+#endif /* defined(HAVE_REPLICATION) */
+
+/**************************************************************************
+ Begin_load_query_log_event methods
+**************************************************************************/
+
+Begin_load_query_log_event::
+Begin_load_query_log_event(THD* thd_arg, const char* db_arg, uchar* block_arg,
+ uint block_len_arg, bool using_trans)
+ :Append_block_log_event(thd_arg, db_arg, block_arg, block_len_arg,
+ using_trans)
+{
+ file_id= thd_arg->file_id= mysql_bin_log.next_file_id();
+}
+
+
+#if defined( HAVE_REPLICATION)
+int Begin_load_query_log_event::get_create_or_append() const
+{
+ return 1; /* create the file */
+}
+
+
+Log_event::enum_skip_reason
+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(rgi);
+}
+#endif /* defined( HAVE_REPLICATION) */
+
+
+/**************************************************************************
+ Execute_load_query_log_event methods
+**************************************************************************/
+
+Execute_load_query_log_event::
+Execute_load_query_log_event(THD *thd_arg, const char* query_arg,
+ ulong query_length_arg, uint fn_pos_start_arg,
+ uint fn_pos_end_arg,
+ enum_load_dup_handling dup_handling_arg,
+ bool using_trans, bool direct, bool suppress_use,
+ int errcode):
+ Query_log_event(thd_arg, query_arg, query_length_arg, using_trans, direct,
+ suppress_use, errcode),
+ file_id(thd_arg->file_id), fn_pos_start(fn_pos_start_arg),
+ fn_pos_end(fn_pos_end_arg), dup_handling(dup_handling_arg)
+{
+}
+
+
+bool
+Execute_load_query_log_event::write_post_header_for_derived()
+{
+ uchar buf[EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN];
+ int4store(buf, file_id);
+ int4store(buf + 4, fn_pos_start);
+ int4store(buf + 4 + 4, fn_pos_end);
+ *(buf + 4 + 4 + 4)= (uchar) dup_handling;
+ return write_data(buf, EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN);
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Execute_load_query_log_event::pack_info(Protocol *protocol)
+{
+ char buf_mem[1024];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ buf.real_alloc(9 + db_len + q_len + 10 + 21);
+ if (db && db_len)
+ {
+ if (buf.append(STRING_WITH_LEN("use ")) ||
+ append_identifier(protocol->thd, &buf, db, db_len) ||
+ buf.append(STRING_WITH_LEN("; ")))
+ return;
+ }
+ if (query && q_len && buf.append(query, q_len))
+ return;
+ if (buf.append(" ;file_id=") ||
+ buf.append_ulonglong(file_id))
+ return;
+ protocol->store(buf.ptr(), buf.length(), &my_charset_bin);
+}
+
+
+int
+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(PSI_INSTRUMENT_ME, q_len + 1 -
+ (fn_pos_end - fn_pos_start) + (FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME));
+
+ DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", my_free(buf); buf= NULL;);
+
+ /* Replace filename and LOCAL keyword in query before executing it */
+ if (buf == NULL)
+ {
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
+ ER_THD(rgi->thd, ER_SLAVE_FATAL_ERROR), "Not enough memory");
+ return 1;
+ }
+
+ p= buf;
+ 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",
+ &rli->mi->cmp_connection_name);
+ fname_end= p= strend(p); // Safer than p=p+5
+ *(p++)='\'';
+ switch (dup_handling) {
+ case LOAD_DUP_IGNORE:
+ p= strmake(p, STRING_WITH_LEN(" IGNORE"));
+ break;
+ case LOAD_DUP_REPLACE:
+ p= strmake(p, STRING_WITH_LEN(" REPLACE"));
+ break;
+ default:
+ /* Ordinary load data */
+ break;
+ }
+ 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(rgi, buf, (uint32)(p-buf));
+
+ /* Forging file name for deletion in same buffer */
+ *fname_end= 0;
+
+ /*
+ If there was an error the slave is going to stop, leave the
+ file so that we can re-execute this event at START SLAVE.
+ */
+ if (unlikely(!error))
+ mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
+
+ my_free(buf);
+ return error;
+}
+#endif // HAVE_REPLICATION
+
+
+/**************************************************************************
+ sql_ex_info methods
+**************************************************************************/
+
+static bool write_str(Log_event_writer *writer, const char *str, uint length)
+{
+ uchar tmp[1];
+ tmp[0]= (uchar) length;
+ return (writer->write_data(tmp, sizeof(tmp)) ||
+ writer->write_data((uchar*) str, length));
+}
+
+bool sql_ex_info::write_data(Log_event_writer *writer)
+{
+ if (new_format())
+ {
+ return write_str(writer, field_term, field_term_len) ||
+ write_str(writer, enclosed, enclosed_len) ||
+ write_str(writer, line_term, line_term_len) ||
+ write_str(writer, line_start, line_start_len) ||
+ write_str(writer, escaped, escaped_len) ||
+ writer->write_data((uchar*) &opt_flags, 1);
+ }
+ else
+ {
+ uchar old_ex[7];
+ old_ex[0]= *field_term;
+ old_ex[1]= *enclosed;
+ old_ex[2]= *line_term;
+ old_ex[3]= *line_start;
+ old_ex[4]= *escaped;
+ old_ex[5]= opt_flags;
+ old_ex[6]= empty_flags;
+ return writer->write_data(old_ex, sizeof(old_ex));
+ }
+}
+
+
+
+/**************************************************************************
+ Rows_log_event member functions
+**************************************************************************/
+
+Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
+ 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_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),
+ master_had_triggers(0)
+#endif
+{
+ /*
+ We allow a special form of dummy event when the table, and cols
+ are null and the table id is ~0UL. This is a temporary
+ solution, to be able to terminate a started statement in the
+ binary log: the extraneous events will be removed in the future.
+ */
+ DBUG_ASSERT((tbl_arg && tbl_arg->s && tid != ~0UL) ||
+ (!tbl_arg && !cols && tid == ~0UL));
+
+ if (thd_arg->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)
+ set_flags(NO_FOREIGN_KEY_CHECKS_F);
+ if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS)
+ set_flags(RELAXED_UNIQUE_CHECKS_F);
+ if (thd_arg->variables.option_bits & OPTION_NO_CHECK_CONSTRAINT_CHECKS)
+ set_flags(NO_CHECK_CONSTRAINT_CHECKS_F);
+ /* 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)))
+ {
+ /* Cols can be zero if this is a dummy binrows event */
+ if (likely(cols != NULL))
+ {
+ memcpy(m_cols.bitmap, cols->bitmap, no_bytes_in_map(cols));
+ create_last_word_mask(&m_cols);
+ }
+ }
+ else
+ {
+ // Needed because my_bitmap_init() does not set it to null on failure
+ m_cols.bitmap= 0;
+ }
+}
+
+
+int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
+{
+ /*
+ When the table has a primary key, we would probably want, by default, to
+ log only the primary key value instead of the entire "before image". This
+ would save binlog space. TODO
+ */
+ DBUG_ENTER("Rows_log_event::do_add_row_data");
+ DBUG_PRINT("enter", ("row_data:%p length: %lu", row_data,
+ (ulong) length));
+
+ /*
+ If length is zero, there is nothing to write, so we just
+ return. Note that this is not an optimization, since calling
+ realloc() with size 0 means free().
+ */
+ if (length == 0)
+ {
+ m_row_count++;
+ DBUG_RETURN(0);
+ }
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_valgrind
+ DBUG_DUMP("row_data", row_data, MY_MIN(length, 32));
+#endif
+
+ DBUG_ASSERT(m_rows_buf <= m_rows_cur);
+ DBUG_ASSERT(!m_rows_buf || (m_rows_end && m_rows_buf < m_rows_end));
+ DBUG_ASSERT(m_rows_cur <= m_rows_end);
+
+ /* The cast will always work since m_rows_cur <= m_rows_end */
+ if (static_cast<size_t>(m_rows_end - m_rows_cur) <= length)
+ {
+ size_t const block_size= 1024;
+ size_t cur_size= m_rows_cur - m_rows_buf;
+ DBUG_EXECUTE_IF("simulate_too_big_row_case1",
+ cur_size= UINT_MAX32 - (block_size * 10);
+ length= UINT_MAX32 - (block_size * 10););
+ DBUG_EXECUTE_IF("simulate_too_big_row_case2",
+ cur_size= UINT_MAX32 - (block_size * 10);
+ length= block_size * 10;);
+ DBUG_EXECUTE_IF("simulate_too_big_row_case3",
+ cur_size= block_size * 10;
+ length= UINT_MAX32 - (block_size * 10););
+ DBUG_EXECUTE_IF("simulate_too_big_row_case4",
+ cur_size= UINT_MAX32 - (block_size * 10);
+ length= (block_size * 10) - block_size + 1;);
+ size_t remaining_space= UINT_MAX32 - cur_size;
+ /* Check that the new data fits within remaining space and we can add
+ block_size without wrapping.
+ */
+ if (cur_size > UINT_MAX32 || length > remaining_space ||
+ ((length + block_size) > remaining_space))
+ {
+ sql_print_error("The row data is greater than 4GB, which is too big to "
+ "write to the binary log.");
+ DBUG_RETURN(ER_BINLOG_ROW_LOGGING_FAILED);
+ }
+ size_t const new_alloc=
+ block_size * ((cur_size + length + block_size - 1) / block_size);
+
+ uchar* const new_buf= (uchar*)my_realloc(PSI_INSTRUMENT_ME, m_rows_buf,
+ new_alloc, MYF(MY_ALLOW_ZERO_PTR|MY_WME));
+ if (unlikely(!new_buf))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ /* If the memory moved, we need to move the pointers */
+ if (new_buf != m_rows_buf)
+ {
+ m_rows_buf= new_buf;
+ m_rows_cur= m_rows_buf + cur_size;
+ }
+
+ /*
+ The end pointer should always be changed to point to the end of
+ the allocated memory.
+ */
+ m_rows_end= m_rows_buf + new_alloc;
+ }
+
+ DBUG_ASSERT(m_rows_cur + length <= m_rows_end);
+ memcpy(m_rows_cur, row_data, length);
+ m_rows_cur+= length;
+ m_row_count++;
+ DBUG_RETURN(0);
+}
+
+
+#if 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)
+{
+ 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;
+}
+
+
+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;
+ /*
+ If m_table_id == ~0ULL, then we have a dummy event that does not
+ contain any data. In that case, we just remove all tables in the
+ tables_to_lock list, close the thread tables, and return with
+ success.
+ */
+ if (m_table_id == ~0ULL)
+ {
+ /*
+ This one is supposed to be set: just an extra check so that
+ nothing strange has happened.
+ */
+ DBUG_ASSERT(get_flags(STMT_END_F));
+
+ rgi->slave_close_thread_tables(thd);
+ thd->clear_error();
+ DBUG_RETURN(0);
+ }
+
+ /*
+ 'thd' has been set by exec_relay_log_event(), just before calling
+ do_apply_event(). We still check here to prevent future coding
+ errors.
+ */
+ DBUG_ASSERT(rgi->thd == thd);
+
+ /*
+ If there is no locks taken, this is the first binrow event seen
+ after the table map events. We should then lock all the tables
+ used in the transaction and proceed with execution of the actual
+ event.
+ */
+ if (!thd->lock)
+ {
+ /*
+ Lock_tables() reads the contents of thd->lex, so they must be
+ initialized.
+
+ We also call the THD::reset_for_next_command(), since this
+ is the logical start of the next "statement". Note that this
+ 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);
+ thd->reset_for_next_command();
+ /*
+ The current statement is just about to begin and
+ has not yet modified anything. Note, all.modified is reset
+ by THD::reset_for_next_command().
+ */
+ thd->transaction->stmt.modified_non_trans_table= FALSE;
+ thd->transaction->stmt.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
+ /*
+ This is a row injection, so we flag the "statement" as
+ such. Note that this code is called both when the slave does row
+ injections and when the BINLOG statement is used to do row
+ injections.
+ */
+ thd->lex->set_stmt_row_injection();
+
+ /*
+ There are a few flags that are replicated with each row event.
+ Make sure to set/clear them before executing the main body of
+ the event.
+ */
+ if (get_flags(NO_FOREIGN_KEY_CHECKS_F))
+ thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
+ else
+ thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
+
+ if (get_flags(RELAXED_UNIQUE_CHECKS_F))
+ thd->variables.option_bits|= OPTION_RELAXED_UNIQUE_CHECKS;
+ else
+ thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
+
+ if (get_flags(NO_CHECK_CONSTRAINT_CHECKS_F))
+ thd->variables.option_bits|= OPTION_NO_CHECK_CONSTRAINT_CHECKS;
+ else
+ thd->variables.option_bits&= ~OPTION_NO_CHECK_CONSTRAINT_CHECKS;
+
+ /* A small test to verify that objects have consistent types */
+ DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
+
+ DBUG_EXECUTE_IF("rows_log_event_before_open_table",
+ {
+ const char action[] = "now SIGNAL before_open_table WAIT_FOR go_ahead_sql";
+ DBUG_ASSERT(!debug_sync_set_action(thd, STRING_WITH_LEN(action)));
+ };);
+
+ 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 (unlikely(open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0)))
+ {
+#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->get_stmt_da()->sql_errno(),
+ thd->is_fatal_error,
+ thd->wsrep_cs().mode(),
+ thd->wsrep_trx().state(),
+ (long long) wsrep_thd_trx_seqno(thd));
+ }
+#endif /* WITH_WSREP */
+ if (thd->is_error() &&
+ !is_parallel_retry_error(rgi, error= thd->get_stmt_da()->sql_errno()))
+ {
+ /*
+ Error reporting borrowed from Query_log_event with many excessive
+ simplifications.
+ We should not honour --slave-skip-errors at this point as we are
+ having severe errors which should not be skipped.
+ */
+ rli->report(ERROR_LEVEL, error, rgi->gtid_info(),
+ "Error executing row event: '%s'",
+ (error ? thd->get_stmt_da()->message() :
+ "unexpected success or fatal error"));
+ thd->is_slave_error= 1;
+ }
+ /* remove trigger's tables */
+ goto err;
+ }
+
+ /*
+ When the open and locking succeeded, we check all tables to
+ ensure that they still have the correct type.
+ */
+
+ {
+ DBUG_PRINT("debug", ("Checking compability of tables to lock - tables_to_lock: %p",
+ rgi->tables_to_lock));
+
+ /**
+ When using RBR and MyISAM MERGE tables the base tables that make
+ up the MERGE table can be appended to the list of tables to lock.
+
+ Thus, we just check compatibility for those that tables that have
+ a correspondent table map event (ie, those that are actually going
+ to be accessed while applying the event). That's why the loop stops
+ at rli->tables_to_lock_count .
+
+ NOTE: The base tables are added here are removed when
+ close_thread_tables is called.
+ */
+ TABLE_LIST *table_list_ptr= rgi->tables_to_lock;
+ for (uint i=0 ; table_list_ptr && (i < rgi->tables_to_lock_count);
+ table_list_ptr= table_list_ptr->next_global, i++)
+ {
+ /*
+ Below if condition takes care of skipping base tables that
+ make up the MERGE table (which are added by open_tables()
+ call). They are added next to the merge table in the list.
+ For eg: If RPL_TABLE_LIST is t3->t1->t2 (where t1 and t2
+ are base tables for merge table 't3'), open_tables will modify
+ the list by adding t1 and t2 again immediately after t3 in the
+ list (*not at the end of the list*). New table_to_lock list will
+ look like t3->t1'->t2'->t1->t2 (where t1' and t2' are TABLE_LIST
+ objects added by open_tables() call). There is no flag(or logic) in
+ open_tables() that can skip adding these base tables to the list.
+ So the logic here should take care of skipping them.
+
+ tables_to_lock_count logic will take care of skipping base tables
+ that are added at the end of the list.
+ For eg: If RPL_TABLE_LIST is t1->t2->t3, open_tables will modify
+ the list into t1->t2->t3->t1'->t2'. t1' and t2' will be skipped
+ because tables_to_lock_count logic in this for loop.
+ */
+ if (table_list_ptr->parent_l)
+ continue;
+ /*
+ We can use a down cast here since we know that every table added
+ to the tables_to_lock is a RPL_TABLE_LIST (or child table which is
+ skipped above).
+ */
+ RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(table_list_ptr);
+ DBUG_ASSERT(ptr->m_tabledef_valid);
+ 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,
+ ptr->table->s->table_name.str));
+ /*
+ We should not honour --slave-skip-errors at this point as we are
+ having severe errors which should not be skiped.
+ */
+ thd->is_slave_error= 1;
+ /* remove trigger's tables */
+ error= ERR_BAD_TABLE_DEF;
+ goto err;
+ }
+ DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
+ " - conv_table: %p",
+ ptr->table->s->db.str,
+ ptr->table->s->table_name.str, conv_table));
+ ptr->m_conv_table= conv_table;
+ }
+ }
+
+ /*
+ ... and then we add all the tables to the table map and but keep
+ them in the tables to lock list.
+
+ We also invalidate the query cache for all the tables, since
+ they will now be changed.
+
+ TODO [/Matz]: Maybe the query cache should not be invalidated
+ here? It might be that a table is not changed, even though it
+ was locked for the statement. We do know that each
+ Rows_log_event contain at least one row, so after processing one
+ Rows_log_event, we can invalidate the query cache for the
+ associated table.
+ */
+ TABLE_LIST *ptr= rgi->tables_to_lock;
+ for (uint i=0 ; ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
+ {
+ /*
+ Please see comment in above 'for' loop to know the reason
+ for this if condition
+ */
+ if (ptr->parent_l)
+ continue;
+ 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
+ /*
+ Moved invalidation right before the call to rows_event_stmt_cleanup(),
+ to avoid query cache being polluted with stale entries,
+ */
+ if (! (WSREP(thd) && wsrep_thd_is_applying(thd)))
+ {
+#endif /* WITH_WSREP */
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
+#ifdef WITH_WSREP
+ }
+#endif /* WITH_WSREP */
+#endif
+ }
+
+ table= m_table= rgi->m_table_map.get_table(m_table_id);
+
+ DBUG_PRINT("debug", ("m_table:%p, m_table_id: %llu%s",
+ 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_and_rollback();
+ table->file->prepare_for_insert(get_general_type_code() != WRITE_ROWS_EVENT);
+
+ /*
+ table == NULL means that this table should not be replicated
+ (this was set up by Table_map_log_event::do_apply_event()
+ which tested replicate-* rules).
+ */
+
+ /*
+ It's not needed to set_time() but
+ 1) it continues the property that "Time" in SHOW PROCESSLIST shows how
+ much slave is behind
+ 2) it will be needed when we allow replication from a table with no
+ TIMESTAMP column to a table with one.
+ So we call set_time(), like in SBR. Presently it changes nothing.
+ */
+ thd->set_time(when, when_sec_part);
+
+ if (m_width == table->s->fields && bitmap_is_set_all(&m_cols))
+ set_flags(COMPLETE_ROWS_F);
+
+ /*
+ Set tables write and read sets.
+
+ Read_set contains all slave columns (in case we are going to fetch
+ a complete record from slave)
+
+ Write_set equals the m_cols bitmap sent from master but it can be
+ longer if slave has extra columns.
+ */
+
+ DBUG_PRINT_BITSET("debug", "Setting table's read_set from: %s", &m_cols);
+
+ bitmap_set_all(table->read_set);
+ if (get_general_type_code() == DELETE_ROWS_EVENT ||
+ get_general_type_code() == UPDATE_ROWS_EVENT)
+ bitmap_intersect(table->read_set,&m_cols);
+
+ bitmap_set_all(table->write_set);
+ table->rpl_write_set= table->write_set;
+
+ /* WRITE ROWS EVENTS store the bitmap in m_cols instead of m_cols_ai */
+ MY_BITMAP *after_image= ((get_general_type_code() == UPDATE_ROWS_EVENT) ?
+ &m_cols_ai : &m_cols);
+ bitmap_intersect(table->write_set, after_image);
+
+ this->slave_exec_mode= slave_exec_mode_options; // fix the mode
+
+ // Do event specific preparations
+ error= do_before_row_operations(rli);
+
+ /*
+ Bug#56662 Assertion failed: next_insert_id == 0, file handler.cc
+ Don't allow generation of auto_increment value when processing
+ rows event by setting 'MODE_NO_AUTO_VALUE_ON_ZERO'. The exception
+ to this rule happens when the auto_inc column exists on some
+ extra columns on the slave. In that case, do not force
+ MODE_NO_AUTO_VALUE_ON_ZERO.
+ */
+ sql_mode_t saved_sql_mode= thd->variables.sql_mode;
+ if (!is_auto_inc_in_extra_columns())
+ thd->variables.sql_mode= MODE_NO_AUTO_VALUE_ON_ZERO;
+
+ // row processing loop
+
+ /*
+ set the initial time of this ROWS statement if it was not done
+ before in some other ROWS event.
+ */
+ rgi->set_row_stmt_start_timestamp();
+
+ THD_STAGE_INFO(thd, stage_executing);
+ do
+ {
+ /* in_use can have been set to NULL in close_tables_for_reopen */
+ THD* old_thd= table->in_use;
+ if (!table->in_use)
+ table->in_use= thd;
+
+ error= do_exec_row(rgi);
+
+ if (unlikely(error))
+ DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
+ DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
+
+ table->in_use = old_thd;
+
+ if (unlikely(error))
+ {
+ int actual_error= convert_handler_error(error, thd, table);
+ bool idempotent_error= (idempotent_error_code(error) &&
+ (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT));
+ bool ignored_error= (idempotent_error == 0 ?
+ ignored_error_code(actual_error) : 0);
+
+#ifdef WITH_WSREP
+ if (WSREP(thd) && wsrep_ignored_error_code(this, actual_error))
+ {
+ idempotent_error= true;
+ thd->wsrep_has_ignored_error= true;
+ }
+#endif /* WITH_WSREP */
+ if (idempotent_error || ignored_error)
+ {
+ if (global_system_variables.log_warnings)
+ slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
+ get_type_str(),
+ RPL_LOG_NAME, log_pos);
+ thd->clear_error(1);
+ error= 0;
+ if (idempotent_error == 0)
+ break;
+ }
+ }
+
+ /*
+ If m_curr_row_end was not set during event execution (e.g., because
+ of errors) we can't proceed to the next row. If the error is transient
+ (i.e., error==0 at this point) we must call unpack_current_row() to set
+ m_curr_row_end.
+ */
+
+ DBUG_PRINT("info", ("curr_row: %p; curr_row_end: %p; rows_end:%p",
+ m_curr_row, m_curr_row_end, m_rows_end));
+
+ if (!m_curr_row_end && likely(!error))
+ error= unpack_current_row(rgi);
+
+ m_curr_row= m_curr_row_end;
+
+ if (likely(error == 0) && !transactional_table)
+ thd->transaction->all.modified_non_trans_table=
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
+ } // row processing loop
+ while (error == 0 && (m_curr_row != m_rows_end));
+
+ /*
+ Restore the sql_mode after the rows event is processed.
+ */
+ thd->variables.sql_mode= saved_sql_mode;
+
+ {/**
+ The following failure injecion works in cooperation with tests
+ setting @@global.debug= 'd,stop_slave_middle_group'.
+ The sql thread receives the killed status and will proceed
+ to shutdown trying to finish incomplete events group.
+ */
+ DBUG_EXECUTE_IF("stop_slave_middle_group",
+ if (thd->transaction->all.modified_non_trans_table)
+ const_cast<Relay_log_info*>(rli)->abort_slave= 1;);
+ }
+
+ if (unlikely(error= do_after_row_operations(rli, error)) &&
+ ignored_error_code(convert_handler_error(error, thd, table)))
+ {
+
+ if (global_system_variables.log_warnings)
+ slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
+ get_type_str(),
+ RPL_LOG_NAME, log_pos);
+ thd->clear_error(1);
+ error= 0;
+ }
+ } // if (table)
+
+
+ if (unlikely(error))
+ {
+ slave_rows_error_report(ERROR_LEVEL, error, rgi, thd, table,
+ get_type_str(),
+ RPL_LOG_NAME, log_pos);
+ /*
+ @todo We should probably not call
+ reset_current_stmt_binlog_format_row() from here.
+
+ Note: this applies to log_event_old.cc too.
+ /Sven
+ */
+ thd->reset_current_stmt_binlog_format_row();
+ thd->is_slave_error= 1;
+ /* 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 (WSREP(thd) && wsrep_thd_is_applying(thd))
+ {
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
+ }
+#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
+
+ if (unlikely(get_flags(STMT_END_F) &&
+ (error= rows_event_stmt_cleanup(rgi, thd))))
+ slave_rows_error_report(ERROR_LEVEL,
+ thd->is_error() ? 0 : error,
+ rgi, thd, table,
+ get_type_str(),
+ RPL_LOG_NAME, 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(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 (rgi->rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
+ return Log_event::EVENT_SKIP_IGNORE;
+ else
+ return Log_event::do_shall_skip(rgi);
+}
+
+/**
+ The function is called at Rows_log_event statement commit time,
+ normally from Rows_log_event::do_update_pos() and possibly from
+ Query_log_event::do_apply_event() of the COMMIT.
+ The function commits the last statement for engines, binlog and
+ releases resources have been allocated for the statement.
+
+ @retval 0 Ok.
+ @retval non-zero Error at the commit.
+ */
+
+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
+ unlock) the tables we opened when processing the
+ Table_map_log_event starting the statement.
+
+ OBSERVER. This will clear *all* mappings, not only those that
+ are open for the table. There is not good handle for on-close
+ actions for tables.
+
+ NOTE. Even if we have no table ('table' == 0) we still need to be
+ here, so that we increase the group relay log position. If we didn't, we
+ could have a group relay log position which lags behind "forever"
+ (assume the last master's transaction is ignored by the slave because of
+ replicate-ignore rules).
+ */
+ error= thd->binlog_flush_pending_rows_event(TRUE);
+
+ /*
+ If this event is not in a transaction, the call below will, if some
+ transactional storage engines are involved, commit the statement into
+ them and flush the pending event to binlog.
+ If this event is in a transaction, the call will do nothing, but a
+ Xid_log_event will come next which will, if some transactional engines
+ are involved, commit the transaction and flush the pending event to the
+ binlog.
+ If there was a deadlock the transaction should have been rolled back
+ already. So there should be no need to rollback the transaction.
+ */
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+ error|= (int)(error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd));
+
+ /*
+ Now what if this is not a transactional engine? we still need to
+ flush the pending event to the binlog; we did it with
+ thd->binlog_flush_pending_rows_event(). Note that we imitate
+ what is done for real queries: a call to
+ ha_autocommit_or_rollback() (sometimes only if involves a
+ transactional engine), and a call to be sure to have the pending
+ event flushed.
+ */
+
+ /*
+ @todo We should probably not call
+ reset_current_stmt_binlog_format_row() from here.
+
+ Note: this applies to log_event_old.cc too
+
+ Btw, the previous comment about transactional engines does not
+ seem related to anything that happens here.
+ /Sven
+ */
+ thd->reset_current_stmt_binlog_format_row();
+
+ /*
+ 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;
+ thd->transaction->all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
+ }
+
+ rgi->cleanup_context(thd, 0);
+ }
+ DBUG_RETURN(error);
+}
+
+/**
+ The method either increments the relay log position or
+ commits the current statement and increments the master group
+ possition if the event is STMT_END_F flagged and
+ the statement corresponds to the autocommit query (i.e replicated
+ without wrapping in BEGIN/COMMIT)
+
+ @retval 0 Success
+ @retval non-zero Error in the statement commit
+ */
+int
+Rows_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ Relay_log_info *rli= rgi->rli;
+ int error= 0;
+ DBUG_ENTER("Rows_log_event::do_update_pos");
+
+ DBUG_PRINT("info", ("flags: %s",
+ get_flags(STMT_END_F) ? "STMT_END_F " : ""));
+
+ if (get_flags(STMT_END_F))
+ {
+ /*
+ Indicate that a statement is finished.
+ Step the group log position if we are not in a transaction,
+ otherwise increase the event log position.
+ */
+ error= 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
+ thd->net.last_err* are allowed. Examples of errors are "key not
+ found", which is produced in the test case rpl_row_conflicts.test
+ */
+ thd->clear_error();
+ }
+ else
+ {
+ rgi->inc_event_relay_log_pos();
+ }
+
+ DBUG_RETURN(error);
+}
+
+#endif /* defined(HAVE_REPLICATION) */
+
+
+bool Rows_log_event::write_data_header()
+{
+ uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer
+ DBUG_ASSERT(m_table_id != ~0ULL);
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ {
+ int4store(buf + 0, m_table_id);
+ int2store(buf + 4, m_flags);
+ return (write_data(buf, 6));
+ });
+ int6store(buf + RW_MAPID_OFFSET, m_table_id);
+ int2store(buf + RW_FLAGS_OFFSET, m_flags);
+ return write_data(buf, ROWS_HEADER_LEN);
+}
+
+bool Rows_log_event::write_data_body()
+{
+ /*
+ Note that this should be the number of *bits*, not the number of
+ bytes.
+ */
+ 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);
+ DBUG_ASSERT(static_cast<size_t>(sbuf_end - sbuf) <= sizeof(sbuf));
+
+ DBUG_DUMP("m_width", sbuf, (size_t) (sbuf_end - sbuf));
+ res= res || write_data(sbuf, (size_t) (sbuf_end - sbuf));
+
+ DBUG_DUMP("m_cols", (uchar*) m_cols.bitmap, no_bytes_in_map(&m_cols));
+ res= res || write_data((uchar*)m_cols.bitmap, no_bytes_in_map(&m_cols));
+ /*
+ TODO[refactor write]: Remove the "down cast" here (and elsewhere).
+ */
+ 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));
+ res= res || write_data((uchar*)m_cols_ai.bitmap,
+ no_bytes_in_map(&m_cols_ai));
+ }
+ DBUG_DUMP("rows", m_rows_buf, data_size);
+ res= res || write_data(m_rows_buf, (size_t) data_size);
+
+ return res;
+
+}
+
+bool Rows_log_event::write_compressed()
+{
+ uchar *m_rows_buf_tmp = m_rows_buf;
+ uchar *m_rows_cur_tmp = m_rows_cur;
+ bool ret = true;
+ uint32 comlen, alloc_size;
+ comlen= alloc_size= binlog_get_compress_len((uint32)(m_rows_cur_tmp - m_rows_buf_tmp));
+ m_rows_buf = (uchar *)my_safe_alloca(alloc_size);
+ if(m_rows_buf &&
+ !binlog_buf_compress((const char *)m_rows_buf_tmp, (char *)m_rows_buf,
+ (uint32)(m_rows_cur_tmp - m_rows_buf_tmp), &comlen))
+ {
+ m_rows_cur= comlen + m_rows_buf;
+ ret= Log_event::write();
+ }
+ my_safe_afree(m_rows_buf, alloc_size);
+ m_rows_buf= m_rows_buf_tmp;
+ m_rows_cur= m_rows_cur_tmp;
+ return ret;
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Rows_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ char const *const flagstr=
+ get_flags(STMT_END_F) ? " flags: STMT_END_F" : "";
+ size_t bytes= my_snprintf(buf, sizeof(buf),
+ "table_id: %llu%s", m_table_id, flagstr);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+/**************************************************************************
+ Annotate_rows_log_event member functions
+**************************************************************************/
+
+Annotate_rows_log_event::Annotate_rows_log_event(THD *thd,
+ bool using_trans,
+ bool direct)
+ : Log_event(thd, 0, using_trans),
+ m_save_thd_query_txt(0),
+ m_save_thd_query_len(0),
+ m_saved_thd_query(false),
+ m_used_query_txt(0)
+{
+ m_query_txt= thd->query();
+ m_query_len= thd->query_length();
+ if (direct)
+ cache_type= Log_event::EVENT_NO_CACHE;
+}
+
+
+bool Annotate_rows_log_event::write_data_header()
+{
+ return 0;
+}
+
+
+bool Annotate_rows_log_event::write_data_body()
+{
+ return write_data(m_query_txt, m_query_len);
+}
+
+
+#if defined(HAVE_REPLICATION)
+void Annotate_rows_log_event::pack_info(Protocol* protocol)
+{
+ if (m_query_txt && m_query_len)
+ protocol->store(m_query_txt, m_query_len, &my_charset_bin);
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ rgi->free_annotate_event();
+ m_save_thd_query_txt= thd->query();
+ m_save_thd_query_len= thd->query_length();
+ m_saved_thd_query= true;
+ m_used_query_txt= 1;
+ thd->set_query(m_query_txt, m_query_len);
+ return 0;
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+int Annotate_rows_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+Log_event::enum_skip_reason
+Annotate_rows_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ return continue_group(rgi);
+}
+#endif
+
+/**************************************************************************
+ Table_map_log_event member functions and support functions
+**************************************************************************/
+
+/**
+ Save the field metadata based on the real_type of the field.
+ The metadata saved depends on the type of the field. Some fields
+ store a single byte for pack_length() while others store two bytes
+ for field_length (max length).
+
+ @retval 0 Ok.
+
+ @todo
+ We may want to consider changing the encoding of the information.
+ Currently, the code attempts to minimize the number of bytes written to
+ the tablemap. There are at least two other alternatives; 1) using
+ net_store_length() to store the data allowing it to choose the number of
+ bytes that are appropriate thereby making the code much easier to
+ maintain (only 1 place to change the encoding), or 2) use a fixed number
+ of bytes for each field. The problem with option 1 is that net_store_length()
+ will use one byte if the value < 251, but 3 bytes if it is > 250. Thus,
+ for fields like CHAR which can be no larger than 255 characters, the method
+ will use 3 bytes when the value is > 250. Further, every value that is
+ encoded using 2 parts (e.g., pack_length, field_length) will be numerically
+ > 250 therefore will use 3 bytes for eah value. The problem with option 2
+ is less wasteful for space but does waste 1 byte for every field that does
+ not encode 2 parts.
+*/
+int Table_map_log_event::save_field_metadata()
+{
+ DBUG_ENTER("Table_map_log_event::save_field_metadata");
+ int index= 0;
+ Binlog_type_info *info;
+ for (unsigned int i= 0 ; i < m_table->s->fields ; i++)
+ {
+ DBUG_PRINT("debug", ("field_type: %d", m_coltype[i]));
+ info= binlog_type_info_array + i;
+ int2store(&m_field_metadata[index], info->m_metadata);
+ index+= info->m_metadata_size;
+ DBUG_EXECUTE_IF("inject_invalid_blob_size",
+ {
+ if (m_coltype[i] == MYSQL_TYPE_BLOB)
+ m_field_metadata[index-1] = 5;
+ });
+ }
+ DBUG_RETURN(index);
+}
+
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ Mats says tbl->s lives longer than this event so it's ok to copy pointers
+ (tbl->s->db etc) and not pointer content.
+ */
+Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
+ bool is_transactional)
+ : Log_event(thd, 0, is_transactional),
+ m_table(tbl),
+ m_dbnam(tbl->s->db.str),
+ m_dblen(m_dbnam ? tbl->s->db.length : 0),
+ m_tblnam(tbl->s->table_name.str),
+ m_tbllen(tbl->s->table_name.length),
+ m_colcnt(tbl->s->fields),
+ m_memory(NULL),
+ m_table_id(tid),
+ m_flags(TM_BIT_LEN_EXACT_F),
+ m_data_size(0),
+ m_field_metadata(0),
+ m_field_metadata_size(0),
+ m_null_bits(0),
+ m_meta_memory(NULL),
+ m_optional_metadata_len(0),
+ m_optional_metadata(NULL)
+{
+ uchar cbuf[MAX_INT_WIDTH];
+ uchar *cbuf_end;
+ DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
+ DBUG_ASSERT(m_table_id != ~0ULL);
+ /*
+ In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
+ table.cc / alloc_table_share():
+ Use the fact the key is db/0/table_name/0
+ As we rely on this let's assert it.
+ */
+ DBUG_ASSERT((tbl->s->db.str == 0) ||
+ (tbl->s->db.str[tbl->s->db.length] == 0));
+ DBUG_ASSERT(tbl->s->table_name.str[tbl->s->table_name.length] == 0);
+
+ binlog_type_info_array= (Binlog_type_info *)thd->alloc(m_table->s->fields *
+ sizeof(Binlog_type_info));
+ for (uint i= 0; i < m_table->s->fields; i++)
+ binlog_type_info_array[i]= m_table->field[i]->binlog_type_info();
+
+ m_data_size= TABLE_MAP_HEADER_LEN;
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master", m_data_size= 6;);
+ m_data_size+= m_dblen + 2; // Include length and terminating \0
+ m_data_size+= m_tbllen + 2; // Include length and terminating \0
+ cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
+ DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
+ m_data_size+= (cbuf_end - cbuf) + m_colcnt; // COLCNT and column types
+
+ if (tbl->triggers)
+ m_flags|= TM_BIT_HAS_TRIGGERS_F;
+
+ /* If malloc fails, caught in is_valid() */
+ if ((m_memory= (uchar*) my_malloc(PSI_INSTRUMENT_ME, 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]= binlog_type_info_array[i].m_type_code;
+ DBUG_EXECUTE_IF("inject_invalid_column_type", m_coltype[1]= 230;);
+ }
+
+ /*
+ Calculate a bitmap for the results of maybe_null() for all columns.
+ The bitmap is used to determine when there is a column from the master
+ that is not on the slave and is null and thus not in the row data during
+ replication.
+ */
+ uint num_null_bytes= (m_table->s->fields + 7) / 8;
+ m_data_size+= num_null_bytes;
+ m_meta_memory= (uchar *)my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
+ &m_null_bits, num_null_bytes,
+ &m_field_metadata, (m_colcnt * 2),
+ NULL);
+
+ bzero(m_field_metadata, (m_colcnt * 2));
+
+ /*
+ Create an array for the field metadata and store it.
+ */
+ m_field_metadata_size= save_field_metadata();
+ DBUG_ASSERT(m_field_metadata_size <= (m_colcnt * 2));
+
+ /*
+ Now set the size of the data to the size of the field metadata array
+ plus one or three bytes (see pack.c:net_store_length) for number of
+ elements in the field metadata array.
+ */
+ if (m_field_metadata_size < 251)
+ m_data_size+= m_field_metadata_size + 1;
+ else
+ m_data_size+= m_field_metadata_size + 3;
+
+ bzero(m_null_bits, num_null_bytes);
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ if (m_table->field[i]->maybe_null())
+ m_null_bits[(i / 8)]+= 1 << (i % 8);
+
+ init_metadata_fields();
+ m_data_size+= m_metadata_buf.length();
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Return value is an error code, one of:
+
+ -1 Failure to open table [from open_tables()]
+ 0 Success
+ 1 No room for more tables [from set_table()]
+ 2 Out of memory [from set_table()]
+ 3 Wrong table definition
+ 4 Daisy-chaining RBR with SBR not possible
+ */
+
+#if defined(HAVE_REPLICATION)
+
+enum enum_tbl_map_status
+{
+ /* no duplicate identifier found */
+ OK_TO_PROCESS= 0,
+
+ /* this table map must be filtered out */
+ FILTERED_OUT= 1,
+
+ /* identifier mapping table with different properties */
+ SAME_ID_MAPPING_DIFFERENT_TABLE= 2,
+
+ /* a duplicate identifier was found mapping the same table */
+ SAME_ID_MAPPING_SAME_TABLE= 3
+};
+
+/*
+ Checks if this table map event should be processed or not. First
+ it checks the filtering rules, and then looks for duplicate identifiers
+ in the existing list of rli->tables_to_lock.
+
+ It checks that there hasn't been any corruption by verifying that there
+ are no duplicate entries with different properties.
+
+ In some cases, some binary logs could get corrupted, showing several
+ tables mapped to the same table_id, 0 (see: BUG#56226). Thus we do this
+ early sanity check for such cases and avoid that the server crashes
+ later.
+
+ In some corner cases, the master logs duplicate table map events, i.e.,
+ same id, same database name, same table name (see: BUG#37137). This is
+ different from the above as it's the same table that is mapped again
+ to the same identifier. Thus we cannot just check for same ids and
+ assume that the event is corrupted we need to check every property.
+
+ NOTE: in the event that BUG#37137 ever gets fixed, this extra check
+ will still be valid because we would need to support old binary
+ logs anyway.
+
+ @param rli The relay log info reference.
+ @param table_list A list element containing the table to check against.
+ @return OK_TO_PROCESS
+ if there was no identifier already in rli->tables_to_lock
+
+ FILTERED_OUT
+ if the event is filtered according to the filtering rules
+
+ SAME_ID_MAPPING_DIFFERENT_TABLE
+ if the same identifier already maps a different table in
+ rli->tables_to_lock
+
+ SAME_ID_MAPPING_SAME_TABLE
+ if the same identifier already maps the same table in
+ rli->tables_to_lock.
+*/
+static enum_tbl_map_status
+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;
+ if ((rgi->thd->slave_thread /* filtering is for slave only */ ||
+ IF_WSREP((WSREP(rgi->thd) && rgi->thd->wsrep_applier), 0)) &&
+ (!rli->mi->rpl_filter->db_ok(table_list->db.str) ||
+ (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*>(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)
+ {
+
+ if (cmp(&ptr->db, &table_list->db) ||
+ cmp(&ptr->alias, &table_list->table_name) ||
+ ptr->lock_type != TL_WRITE) // the ::do_apply_event always sets TL_WRITE
+ res= SAME_ID_MAPPING_DIFFERENT_TABLE;
+ else
+ res= SAME_ID_MAPPING_SAME_TABLE;
+
+ break;
+ }
+ }
+ }
+
+ DBUG_PRINT("debug", ("check of table map ended up with: %u", res));
+
+ DBUG_RETURN(res);
+}
+
+int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ RPL_TABLE_LIST *table_list;
+ char *db_mem, *tname_mem, *ptr;
+ size_t dummy_len, db_mem_length, tname_mem_length;
+ void *memory;
+ Rpl_filter *filter;
+ Relay_log_info const *rli= rgi->rli;
+ DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
+
+ /* Step the query id to mark what columns that are actually used. */
+ thd->set_query_id(next_query_id());
+
+ if (!(memory= my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
+ &table_list, (uint) sizeof(RPL_TABLE_LIST),
+ &db_mem, (uint) NAME_LEN + 1,
+ &tname_mem, (uint) NAME_LEN + 1,
+ NullS)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ db_mem_length= strmov(db_mem, m_dbnam) - db_mem;
+ tname_mem_length= strmov(tname_mem, m_tblnam) - tname_mem;
+ if (lower_case_table_names)
+ {
+ my_casedn_str(files_charset_info, (char*)tname_mem);
+ my_casedn_str(files_charset_info, (char*)db_mem);
+ }
+
+ /* call from mysql_client_binlog_statement() will not set rli->mi */
+ filter= rgi->thd->slave_thread ? rli->mi->rpl_filter : global_rpl_filter;
+
+ /* rewrite rules changed the database */
+ if (((ptr= (char*) filter->get_rewrite_db(db_mem, &dummy_len)) != db_mem))
+ db_mem_length= strmov(db_mem, ptr) - db_mem;
+
+ LEX_CSTRING tmp_db_name= {db_mem, db_mem_length };
+ LEX_CSTRING tmp_tbl_name= {tname_mem, tname_mem_length };
+
+ table_list->init_one_table(&tmp_db_name, &tmp_tbl_name, 0, TL_WRITE);
+ 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= TABLE_TYPE_NORMAL;
+
+ DBUG_PRINT("debug", ("table: %s is mapped to %llu",
+ table_list->table_name.str,
+ table_list->table_id));
+ 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));
+
+ 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);
+
+ /*
+ Use placement new to construct the table_def instance in the
+ memory allocated for it inside table_list.
+
+ The memory allocated by the table_def structure (i.e., not the
+ memory allocated *for* the table_def structure) is released
+ inside Relay_log_info::clear_tables_to_lock() by calling the
+ table_def destructor explicitly.
+ */
+ new (&table_list->m_tabledef)
+ table_def(m_coltype, m_colcnt,
+ m_field_metadata, m_field_metadata_size,
+ m_null_bits, m_flags);
+ table_list->m_tabledef_valid= TRUE;
+ table_list->m_conv_table= NULL;
+ table_list->open_type= OT_BASE_ONLY;
+
+ /*
+ 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= 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_*
+ {
+ /*
+ If mapped already but with different properties, we raise an
+ error.
+ If mapped already but with same properties we skip the event.
+ If filtered out we skip the event.
+
+ In all three cases, we need to free the memory previously
+ allocated.
+ */
+ if (tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE)
+ {
+ /*
+ Something bad has happened. We need to stop the slave as strange things
+ could happen if we proceed: slave crash, wrong table being updated, ...
+ As a consequence we push an error in this case.
+ */
+
+ char buf[256];
+
+ my_snprintf(buf, sizeof(buf),
+ "Found table map event mapping table id %u which "
+ "was already mapped but with different settings.",
+ table_list->table_id);
+
+ if (thd->slave_thread)
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
+ ER_THD(thd, ER_SLAVE_FATAL_ERROR), buf);
+ else
+ /*
+ For the cases in which a 'BINLOG' statement is set to
+ execute in a user session
+ */
+ my_error(ER_SLAVE_FATAL_ERROR, MYF(0), buf);
+ }
+
+ my_free(memory);
+ }
+
+ DBUG_RETURN(tblmap_status == SAME_ID_MAPPING_DIFFERENT_TABLE);
+}
+
+Log_event::enum_skip_reason
+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(rgi);
+}
+
+int Table_map_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+
+#endif /* defined(HAVE_REPLICATION) */
+
+bool Table_map_log_event::write_data_header()
+{
+ DBUG_ASSERT(m_table_id != ~0ULL);
+ uchar buf[TABLE_MAP_HEADER_LEN];
+ DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
+ {
+ int4store(buf + 0, m_table_id);
+ int2store(buf + 4, m_flags);
+ return (write_data(buf, 6));
+ });
+ int6store(buf + TM_MAPID_OFFSET, m_table_id);
+ int2store(buf + TM_FLAGS_OFFSET, m_flags);
+ return write_data(buf, TABLE_MAP_HEADER_LEN);
+}
+
+bool Table_map_log_event::write_data_body()
+{
+ 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 <= 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[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[MAX_INT_WIDTH];
+ uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
+
+ return write_data(dbuf, sizeof(dbuf)) ||
+ write_data(m_dbnam, m_dblen+1) ||
+ write_data(tbuf, sizeof(tbuf)) ||
+ write_data(m_tblnam, m_tbllen+1) ||
+ write_data(cbuf, (size_t) (cbuf_end - cbuf)) ||
+ write_data(m_coltype, m_colcnt) ||
+ write_data(mbuf, (size_t) (mbuf_end - mbuf)) ||
+ write_data(m_field_metadata, m_field_metadata_size),
+ write_data(m_null_bits, (m_colcnt + 7) / 8) ||
+ write_data((const uchar*) m_metadata_buf.ptr(),
+ m_metadata_buf.length());
+ }
+
+/**
+ stores an integer into packed format.
+
+ @param[out] str_buf a buffer where the packed integer will be stored.
+ @param[in] length the integer will be packed.
+ */
+static inline
+void store_compressed_length(String &str_buf, ulonglong length)
+{
+ // Store Type and packed length
+ uchar buf[4];
+ uchar *buf_ptr = net_store_length(buf, length);
+
+ str_buf.append(reinterpret_cast<char *>(buf), buf_ptr-buf);
+}
+
+/**
+ Write data into str_buf with Type|Length|Value(TLV) format.
+
+ @param[out] str_buf a buffer where the field is stored.
+ @param[in] type type of the field
+ @param[in] length length of the field value
+ @param[in] value value of the field
+*/
+static inline
+bool write_tlv_field(String &str_buf,
+ enum Table_map_log_event::Optional_metadata_field_type
+ type, uint length, const uchar *value)
+{
+ /* type is stored in one byte, so it should never bigger than 255. */
+ DBUG_ASSERT(static_cast<int>(type) <= 255);
+ str_buf.append((char) type);
+ store_compressed_length(str_buf, length);
+ return str_buf.append(reinterpret_cast<const char *>(value), length);
+}
+
+/**
+ Write data into str_buf with Type|Length|Value(TLV) format.
+
+ @param[out] str_buf a buffer where the field is stored.
+ @param[in] type type of the field
+ @param[in] value value of the field
+*/
+static inline
+bool write_tlv_field(String &str_buf,
+ enum Table_map_log_event::Optional_metadata_field_type
+ type, const String &value)
+{
+ return write_tlv_field(str_buf, type, value.length(),
+ reinterpret_cast<const uchar *>(value.ptr()));
+}
+
+static inline bool is_character_field(Binlog_type_info *info_array, Field *field)
+{
+ Binlog_type_info *info= info_array + field->field_index;
+ if (!info->m_cs)
+ return 0;
+ if (info->m_set_typelib || info->m_enum_typelib)
+ return 0;
+ return 1;
+}
+
+static inline bool is_enum_or_set_field(Binlog_type_info *info_array, Field *field) {
+ Binlog_type_info *info= info_array + field->field_index;
+ if (info->m_set_typelib || info->m_enum_typelib)
+ return 1;
+ return 0;
+}
+
+
+void Table_map_log_event::init_metadata_fields()
+{
+ DBUG_ENTER("init_metadata_fields");
+ DBUG_EXECUTE_IF("simulate_no_optional_metadata", DBUG_VOID_RETURN;);
+
+ if (binlog_row_metadata == BINLOG_ROW_METADATA_NO_LOG)
+ DBUG_VOID_RETURN;
+ if (init_signedness_field() ||
+ init_charset_field(&is_character_field, DEFAULT_CHARSET,
+ COLUMN_CHARSET) ||
+ init_geometry_type_field())
+ {
+ m_metadata_buf.length(0);
+ DBUG_VOID_RETURN;
+ }
+
+ if (binlog_row_metadata == BINLOG_ROW_METADATA_FULL)
+ {
+ if (DBUG_EVALUATE_IF("dont_log_column_name", 0, init_column_name_field()) ||
+ init_charset_field(&is_enum_or_set_field, ENUM_AND_SET_DEFAULT_CHARSET,
+ ENUM_AND_SET_COLUMN_CHARSET) ||
+ init_set_str_value_field() ||
+ init_enum_str_value_field() ||
+ init_primary_key_field())
+ m_metadata_buf.length(0);
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool Table_map_log_event::init_signedness_field()
+{
+ /* use it to store signed flags, each numeric column take a bit. */
+ StringBuffer<128> buf;
+ unsigned char flag= 0;
+ unsigned char mask= 0x80;
+ Binlog_type_info *info;
+
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ info= binlog_type_info_array + i;
+ if (info->m_signedness != Binlog_type_info::SIGN_NOT_APPLICABLE)
+ {
+ if (info->m_signedness == Binlog_type_info::SIGN_UNSIGNED)
+ flag|= mask;
+ mask >>= 1;
+
+ // 8 fields are tested, store the result and clear the flag.
+ if (mask == 0)
+ {
+ buf.append(flag);
+ flag= 0;
+ mask= 0x80;
+ }
+ }
+ }
+
+ // Stores the signedness flags of last few columns
+ if (mask != 0x80)
+ buf.append(flag);
+
+ // The table has no numeric column, so don't log SIGNEDNESS field
+ if (buf.is_empty())
+ return false;
+
+ return write_tlv_field(m_metadata_buf, SIGNEDNESS, buf);
+}
+
+bool Table_map_log_event::init_charset_field(
+ bool (* include_type)(Binlog_type_info *, Field *),
+ Optional_metadata_field_type default_charset_type,
+ Optional_metadata_field_type column_charset_type)
+{
+ DBUG_EXECUTE_IF("simulate_init_charset_field_error", return true;);
+
+ std::map<uint, uint> collation_map;
+ // For counting characters columns
+ uint char_col_cnt= 0;
+
+ /* Find the collation number used by most fields */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if ((*include_type)(binlog_type_info_array, m_table->field[i]))
+ {
+ collation_map[binlog_type_info_array[i].m_cs->number]++;
+ char_col_cnt++;
+ }
+ }
+
+ if (char_col_cnt == 0)
+ return false;
+
+ /* Find the most used collation */
+ uint most_used_collation= 0;
+ uint most_used_count= 0;
+ for (std::map<uint, uint>::iterator it= collation_map.begin();
+ it != collation_map.end(); it++)
+ {
+ if (it->second > most_used_count)
+ {
+ most_used_count= it->second;
+ most_used_collation= it->first;
+ }
+ }
+
+ /*
+ Comparing length of COLUMN_CHARSET field and COLUMN_CHARSET_WITH_DEFAULT
+ field to decide which field should be logged.
+
+ Length of COLUMN_CHARSET = character column count * collation id size.
+ Length of COLUMN_CHARSET_WITH_DEFAULT =
+ default collation_id size + count of columns not use default charset *
+ (column index size + collation id size)
+
+ Assume column index just uses 1 byte and collation number also uses 1 byte.
+ */
+ if (char_col_cnt * 1 < (1 + (char_col_cnt - most_used_count) * 2))
+ {
+ StringBuffer<512> buf;
+
+ /*
+ Stores character set information into COLUMN_CHARSET format,
+ character sets of all columns are stored one by one.
+ -----------------------------------------
+ | Charset number | .... |Charset number |
+ -----------------------------------------
+ */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (include_type(binlog_type_info_array, m_table->field[i]))
+ store_compressed_length(buf, binlog_type_info_array[i].m_cs->number);
+ }
+ return write_tlv_field(m_metadata_buf, column_charset_type, buf);
+ }
+ else
+ {
+ StringBuffer<512> buf;
+ uint char_column_index= 0;
+ uint default_collation= most_used_collation;
+
+ /*
+ Stores character set information into DEFAULT_CHARSET format,
+ First stores the default character set, and then stores the character
+ sets different to default character with their column index one by one.
+ --------------------------------------------------------
+ | Default Charset | Col Index | Charset number | ... |
+ --------------------------------------------------------
+ */
+
+ // Store the default collation number
+ store_compressed_length(buf, default_collation);
+
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (include_type(binlog_type_info_array, m_table->field[i]))
+ {
+ CHARSET_INFO *cs= binlog_type_info_array[i].m_cs;
+ DBUG_ASSERT(cs);
+ if (cs->number != default_collation)
+ {
+ store_compressed_length(buf, char_column_index);
+ store_compressed_length(buf, cs->number);
+ }
+ char_column_index++;
+ }
+ }
+ return write_tlv_field(m_metadata_buf, default_charset_type, buf);
+ }
+}
+
+bool Table_map_log_event::init_column_name_field()
+{
+ StringBuffer<2048> buf;
+
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ size_t len= m_table->field[i]->field_name.length;
+
+ store_compressed_length(buf, len);
+ buf.append(m_table->field[i]->field_name.str, len);
+ }
+ return write_tlv_field(m_metadata_buf, COLUMN_NAME, buf);
+}
+
+bool Table_map_log_event::init_set_str_value_field()
+{
+ StringBuffer<1024> buf;
+ TYPELIB *typelib;
+
+ /*
+ SET string values are stored in the same format:
+ ----------------------------------------------
+ | Value number | value1 len | value 1| .... | // first SET column
+ ----------------------------------------------
+ | Value number | value1 len | value 1| .... | // second SET column
+ ----------------------------------------------
+ */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if ((typelib= binlog_type_info_array[i].m_set_typelib))
+ {
+ store_compressed_length(buf, typelib->count);
+ for (unsigned int i= 0; i < typelib->count; i++)
+ {
+ store_compressed_length(buf, typelib->type_lengths[i]);
+ buf.append(typelib->type_names[i], typelib->type_lengths[i]);
+ }
+ }
+ }
+ if (buf.length() > 0)
+ return write_tlv_field(m_metadata_buf, SET_STR_VALUE, buf);
+ return false;
+}
+
+bool Table_map_log_event::init_enum_str_value_field()
+{
+ StringBuffer<1024> buf;
+ TYPELIB *typelib;
+
+ /* ENUM is same to SET columns, see comment in init_set_str_value_field */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if ((typelib= binlog_type_info_array[i].m_enum_typelib))
+ {
+ store_compressed_length(buf, typelib->count);
+ for (unsigned int i= 0; i < typelib->count; i++)
+ {
+ store_compressed_length(buf, typelib->type_lengths[i]);
+ buf.append(typelib->type_names[i], typelib->type_lengths[i]);
+ }
+ }
+ }
+
+ if (buf.length() > 0)
+ return write_tlv_field(m_metadata_buf, ENUM_STR_VALUE, buf);
+ return false;
+}
+
+bool Table_map_log_event::init_geometry_type_field()
+{
+ StringBuffer<256> buf;
+ uint geom_type;
+
+ /* Geometry type of geometry columns is stored one by one as packed length */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (binlog_type_info_array[i].m_type_code == MYSQL_TYPE_GEOMETRY)
+ {
+ geom_type= binlog_type_info_array[i].m_geom_type;
+ DBUG_EXECUTE_IF("inject_invalid_geometry_type", geom_type= 100;);
+ store_compressed_length(buf, geom_type);
+ }
+ }
+
+ if (buf.length() > 0)
+ return write_tlv_field(m_metadata_buf, GEOMETRY_TYPE, buf);
+ return false;
+}
+
+bool Table_map_log_event::init_primary_key_field()
+{
+ DBUG_EXECUTE_IF("simulate_init_primary_key_field_error", return true;);
+
+ if (unlikely(m_table->s->primary_key == MAX_KEY))
+ return false;
+
+ // If any key column uses prefix like KEY(c1(10)) */
+ bool has_prefix= false;
+ KEY *pk= m_table->key_info + m_table->s->primary_key;
+
+ DBUG_ASSERT(pk->user_defined_key_parts > 0);
+
+ /* Check if any key column uses prefix */
+ for (uint i= 0; i < pk->user_defined_key_parts; i++)
+ {
+ KEY_PART_INFO *key_part= pk->key_part+i;
+ if (key_part->length != m_table->field[key_part->fieldnr-1]->key_length())
+ {
+ has_prefix= true;
+ break;
+ }
+ }
+
+ StringBuffer<128> buf;
+
+ if (!has_prefix)
+ {
+ /* Index of PK columns are stored one by one. */
+ for (uint i= 0; i < pk->user_defined_key_parts; i++)
+ {
+ KEY_PART_INFO *key_part= pk->key_part+i;
+ store_compressed_length(buf, key_part->fieldnr-1);
+ }
+ return write_tlv_field(m_metadata_buf, SIMPLE_PRIMARY_KEY, buf);
+ }
+ else
+ {
+ /* Index of PK columns are stored with a prefix length one by one. */
+ for (uint i= 0; i < pk->user_defined_key_parts; i++)
+ {
+ KEY_PART_INFO *key_part= pk->key_part+i;
+ size_t prefix= 0;
+
+ store_compressed_length(buf, key_part->fieldnr-1);
+
+ // Store character length but not octet length
+ if (key_part->length != m_table->field[key_part->fieldnr-1]->key_length())
+ prefix= key_part->length / key_part->field->charset()->mbmaxlen;
+ store_compressed_length(buf, prefix);
+ }
+ return write_tlv_field(m_metadata_buf, PRIMARY_KEY_WITH_PREFIX, buf);
+ }
+}
+
+#if defined(HAVE_REPLICATION)
+/*
+ Print some useful information for the SHOW BINARY LOG information
+ field.
+ */
+
+void Table_map_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ size_t bytes= my_snprintf(buf, sizeof(buf),
+ "table_id: %llu (%s.%s)",
+ m_table_id, m_dbnam, m_tblnam);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+
+/**************************************************************************
+ Write_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid_arg,
+ bool is_transactional)
+ :Rows_log_event(thd_arg, tbl_arg, tid_arg, tbl_arg->rpl_write_set,
+ is_transactional, WRITE_ROWS_EVENT_V1)
+{
+}
+
+Write_rows_compressed_log_event::Write_rows_compressed_log_event(
+ THD *thd_arg,
+ TABLE *tbl_arg,
+ ulong tid_arg,
+ bool is_transactional)
+ : Write_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional)
+{
+ m_type = WRITE_ROWS_COMPRESSED_EVENT_V1;
+}
+
+bool Write_rows_compressed_log_event::write()
+{
+ return Rows_log_event::write_compressed();
+}
+
+
+#if defined(HAVE_REPLICATION)
+int
+Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ int error= 0;
+
+ /*
+ Increment the global status insert count variable
+ */
+ if (get_flags(STMT_END_F))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_INSERT]);
+
+ /**
+ todo: to introduce a property for the event (handler?) which forces
+ applying the event in the replace (idempotent) fashion.
+ */
+ if (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT)
+ {
+ /*
+ We are using REPLACE semantics and not INSERT IGNORE semantics
+ when writing rows, that is: new rows replace old rows. We need to
+ inform the storage engine that it should use this behaviour.
+ */
+
+ /* Tell the storage engine that we are using REPLACE semantics. */
+ thd->lex->duplicates= DUP_REPLACE;
+
+ /*
+ Pretend we're executing a REPLACE command: this is needed for
+ InnoDB since it is not (properly) checking the lex->duplicates flag.
+ */
+ thd->lex->sql_command= SQLCOM_REPLACE;
+ /*
+ Do not raise the error flag in case of hitting to an unique attribute
+ */
+ m_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ /*
+ The following is needed in case if we have AFTER DELETE triggers.
+ */
+ m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
+ m_table->file->extra(HA_EXTRA_IGNORE_NO_KEY);
+ }
+ if (m_table->triggers && do_invoke_trigger())
+ m_table->prepare_triggers_for_insert_stmt_or_event();
+
+ /* Honor next number column if present */
+ m_table->next_number_field= m_table->found_next_number_field;
+ /*
+ * Fixed Bug#45999, In RBR, Store engine of Slave auto-generates new
+ * sequence numbers for auto_increment fields if the values of them are 0.
+ * If generateing a sequence number is decided by the values of
+ * table->auto_increment_field_not_null and SQL_MODE(if includes
+ * MODE_NO_AUTO_VALUE_ON_ZERO) in update_auto_increment function.
+ * SQL_MODE of slave sql thread is always consistency with master's.
+ * In RBR, auto_increment fields never are NULL, except if the auto_inc
+ * column exists only on the slave side (i.e., in an extra column
+ * on the slave's table).
+ */
+ if (!is_auto_inc_in_extra_columns())
+ m_table->auto_increment_field_not_null= TRUE;
+ else
+ {
+ /*
+ Here we have checked that there is an extra field
+ on this server's table that has an auto_inc column.
+
+ Mark that the auto_increment field is null and mark
+ the read and write set bits.
+
+ (There can only be one AUTO_INC column, it is always
+ indexed and it cannot have a DEFAULT value).
+ */
+ m_table->auto_increment_field_not_null= FALSE;
+ m_table->mark_auto_increment_column();
+ }
+
+ return error;
+}
+
+int
+Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ int local_error= 0;
+
+ /**
+ Clear the write_set bit for auto_inc field that only
+ existed on the destination table as an extra column.
+ */
+ if (is_auto_inc_in_extra_columns())
+ {
+ bitmap_clear_bit(m_table->rpl_write_set,
+ m_table->next_number_field->field_index);
+ bitmap_clear_bit(m_table->read_set,
+ m_table->next_number_field->field_index);
+
+ if (get_flags(STMT_END_F))
+ m_table->file->ha_release_auto_increment();
+ }
+ m_table->next_number_field=0;
+ m_table->auto_increment_field_not_null= FALSE;
+ if (slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT)
+ {
+ m_table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ m_table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ resetting the extra with
+ table->file->extra(HA_EXTRA_NO_IGNORE_NO_KEY);
+ fires bug#27077
+ explanation: file->reset() performs this duty
+ ultimately. Still todo: fix
+ */
+ }
+ if (unlikely((local_error= m_table->file->ha_end_bulk_insert())))
+ {
+ m_table->file->print_error(local_error, MYF(0));
+ }
+ return error? error : local_error;
+}
+
+bool Rows_log_event::process_triggers(trg_event_type event,
+ trg_action_time_type time_type,
+ bool old_row_is_record1)
+{
+ bool result;
+ DBUG_ENTER("Rows_log_event::process_triggers");
+ m_table->triggers->mark_fields_used(event);
+ if (slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_YES)
+ {
+ result= m_table->triggers->process_triggers(thd, event,
+ time_type,
+ old_row_is_record1);
+ }
+ else
+ result= m_table->triggers->process_triggers(thd, event,
+ time_type,
+ old_row_is_record1);
+
+ DBUG_RETURN(result);
+}
+/*
+ Check if there are more UNIQUE keys after the given key.
+*/
+static int
+last_uniq_key(TABLE *table, uint keyno)
+{
+ while (++keyno < table->s->keys)
+ if (table->key_info[keyno].flags & HA_NOSAME)
+ return 0;
+ return 1;
+}
+
+/**
+ Check if an error is a duplicate key error.
+
+ This function is used to check if an error code is one of the
+ duplicate key error, i.e., and error code for which it is sensible
+ to do a <code>get_dup_key()</code> to retrieve the duplicate key.
+
+ @param errcode The error code to check.
+
+ @return <code>true</code> if the error code is such that
+ <code>get_dup_key()</code> will return true, <code>false</code>
+ otherwise.
+ */
+bool
+is_duplicate_key_error(int errcode)
+{
+ switch (errcode)
+ {
+ case HA_ERR_FOUND_DUPP_KEY:
+ case HA_ERR_FOUND_DUPP_UNIQUE:
+ return true;
+ }
+ return false;
+}
+
+/**
+ Write the current row into event's table.
+
+ The row is located in the row buffer, pointed by @c m_curr_row member.
+ Number of columns of the row is stored in @c m_width member (it can be
+ different from the number of columns in the table to which we insert).
+ Bitmap @c m_cols indicates which columns are present in the row. It is assumed
+ that event's table is already open and pointed by @c m_table.
+
+ If the same record already exists in the table it can be either overwritten
+ or an error is reported depending on the value of @c overwrite flag
+ (error reporting not yet implemented). Note that the matching record can be
+ different from the row we insert if we use primary keys to identify records in
+ the table.
+
+ The row to be inserted can contain values only for selected columns. The
+ missing columns are filled with default values using @c prepare_record()
+ function. If a matching record is found in the table and @c overwritte is
+ true, the missing columns are taken from it.
+
+ @param rli Relay log info (needed for row unpacking).
+ @param overwrite
+ Shall we overwrite if the row already exists or signal
+ error (currently ignored).
+
+ @returns Error code on failure, 0 on success.
+
+ This method, if successful, sets @c m_curr_row_end pointer to point at the
+ next row in the rows buffer. This is done when unpacking the row to be
+ inserted.
+
+ @note If a matching record is found, it is either updated using
+ @c ha_update_row() or first deleted and then new record written.
+*/
+
+int
+Rows_log_event::write_row(rpl_group_info *rgi,
+ const bool overwrite)
+{
+ DBUG_ENTER("write_row");
+ DBUG_ASSERT(m_table != NULL && thd != NULL);
+
+ TABLE *table= m_table; // pointer to event's table
+ int error;
+ int UNINIT_VAR(keynum);
+ const bool invoke_triggers= (m_table->triggers && do_invoke_trigger());
+ auto_afree_ptr<char> key(NULL);
+
+ prepare_record(table, m_width, true);
+
+ /* unpack row into table->record[0] */
+ if (unlikely((error= unpack_current_row(rgi))))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ if (m_curr_row == m_rows_buf && !invoke_triggers)
+ {
+ /*
+ 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.
+ */
+ /* 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 */
+ DBUG_ASSERT(!(m_curr_row > m_curr_row_end));
+ ha_rows estimated_rows= 0;
+ if (m_curr_row < m_curr_row_end)
+ estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
+ else if (m_curr_row == m_curr_row_end)
+ estimated_rows= 1;
+
+ table->file->ha_start_bulk_insert(estimated_rows);
+ }
+
+ /*
+ Explicitly set the auto_inc to null to make sure that
+ it gets an auto_generated value.
+ */
+ if (is_auto_inc_in_extra_columns())
+ m_table->next_number_field->set_null();
+
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+ DBUG_PRINT_BITSET("debug", "rpl_write_set: %s", table->rpl_write_set);
+ DBUG_PRINT_BITSET("debug", "read_set: %s", table->read_set);
+
+ if (invoke_triggers &&
+ unlikely(process_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE, TRUE)))
+ {
+ DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
+ }
+
+ // Handle INSERT.
+ if (table->versioned(VERS_TIMESTAMP))
+ {
+ ulong sec_part;
+ bitmap_set_bit(table->read_set, table->vers_start_field()->field_index);
+ table->file->column_bitmaps_signal();
+ // Check whether a row came from unversioned table and fix vers fields.
+ if (table->vers_start_field()->get_timestamp(&sec_part) == 0 && sec_part == 0)
+ table->vers_update_fields();
+ }
+
+ /*
+ 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
+ it and repeat the whole process again.
+
+ TODO: Add safety measures against infinite looping.
+ */
+
+ if (table->s->sequence)
+ error= update_sequence();
+ else while (unlikely(error= table->file->ha_write_row(table->record[0])))
+ {
+ if (error == HA_ERR_LOCK_DEADLOCK ||
+ error == HA_ERR_LOCK_WAIT_TIMEOUT ||
+ (keynum= table->file->get_dup_key(error)) < 0 ||
+ !overwrite)
+ {
+ DBUG_PRINT("info",("get_dup_key returns %d)", keynum));
+ /*
+ Deadlock, waiting for lock or just an error from the handler
+ such as HA_ERR_FOUND_DUPP_KEY when overwrite is false.
+ Retrieval of the duplicate key number may fail
+ - either because the error was not "duplicate key" error
+ - or because the information which key is not available
+ */
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ /*
+ We need to retrieve the old row into record[1] to be able to
+ either update or delete the offending record. We either:
+
+ - use rnd_pos() with a row-id (available as dupp_row) to the
+ offending row, if that is possible (MyISAM and Blackhole), or else
+
+ - use index_read_idx() with the key that is duplicated, to
+ retrieve the offending row.
+ */
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ DBUG_PRINT("info",("Locating offending record using rnd_pos()"));
+
+ if ((error= table->file->ha_rnd_init_with_error(0)))
+ {
+ DBUG_RETURN(error);
+ }
+
+ error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref);
+ if (unlikely(error))
+ {
+ DBUG_PRINT("info",("rnd_pos() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ table->file->ha_rnd_end();
+ }
+ else
+ {
+ DBUG_PRINT("info",("Locating offending record using index_read_idx()"));
+
+ if (table->file->extra(HA_EXTRA_FLUSH_CACHE))
+ {
+ DBUG_PRINT("info",("Error when setting HA_EXTRA_FLUSH_CACHE"));
+ DBUG_RETURN(my_errno);
+ }
+
+ if (key.get() == NULL)
+ {
+ key.assign(static_cast<char*>(my_alloca(table->s->max_unique_length)));
+ if (key.get() == NULL)
+ {
+ DBUG_PRINT("info",("Can't allocate key buffer"));
+ DBUG_RETURN(ENOMEM);
+ }
+ }
+
+ key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum,
+ 0);
+ error= table->file->ha_index_read_idx_map(table->record[1], keynum,
+ (const uchar*)key.get(),
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT);
+ if (unlikely(error))
+ {
+ DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error)));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ /*
+ Now, record[1] should contain the offending row. That
+ will enable us to update it or, alternatively, delete it (so
+ that we can insert the new row afterwards).
+ */
+
+ /*
+ If row is incomplete we will use the record found to fill
+ missing columns.
+ */
+ if (!get_flags(COMPLETE_ROWS_F))
+ {
+ restore_record(table,record[1]);
+ error= unpack_current_row(rgi);
+ }
+
+ DBUG_PRINT("debug",("preparing for update: before and after image"));
+ DBUG_DUMP("record[1] (before)", table->record[1], table->s->reclength);
+ DBUG_DUMP("record[0] (after)", table->record[0], table->s->reclength);
+
+ /*
+ REPLACE is defined as either INSERT or DELETE + INSERT. If
+ possible, we can replace it with an UPDATE, but that will not
+ work on InnoDB if FOREIGN KEY checks are necessary.
+
+ I (Matz) am not sure of the reason for the last_uniq_key()
+ check as, but I'm guessing that it's something along the
+ following lines.
+
+ Suppose that we got the duplicate key to be a key that is not
+ the last unique key for the table and we perform an update:
+ then there might be another key for which the unique check will
+ fail, so we're better off just deleting the row and inserting
+ the correct row.
+
+ Additionally we don't use UPDATE if rbr triggers should be invoked -
+ when triggers are used we want a simple and predictable execution path.
+ */
+ if (last_uniq_key(table, keynum) && !invoke_triggers &&
+ !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));
+ }
+
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
+ if (invoke_triggers &&
+ unlikely(process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE,
+ TRUE)))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ else
+ {
+ if (unlikely((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 &&
+ unlikely(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 &&
+ unlikely(process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+
+ DBUG_RETURN(error);
+}
+
+
+int Rows_log_event::update_sequence()
+{
+ TABLE *table= m_table; // pointer to event's table
+
+ if (!bitmap_is_set(table->rpl_write_set, MIN_VALUE_FIELD_NO))
+ {
+ /* This event come from a setval function executed on the master.
+ Update the sequence next_number and round, like we do with setval()
+ */
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table,
+ table->read_set);
+ longlong nextval= table->field[NEXT_FIELD_NO]->val_int();
+ longlong round= table->field[ROUND_FIELD_NO]->val_int();
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+
+ return table->s->sequence->set_value(table, nextval, round, 0) > 0;
+ }
+
+ /*
+ Update all fields in table and update the active sequence, like with
+ ALTER SEQUENCE
+ */
+ return table->file->ha_write_row(table->record[0]);
+}
+
+
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+
+int
+Write_rows_log_event::do_exec_row(rpl_group_info *rgi)
+{
+ DBUG_ASSERT(m_table != NULL);
+ const char *tmp= thd->get_proc_info();
+ const char *message= "Write_rows_log_event::write_row()";
+ int error;
+
+#ifdef WSREP_PROC_INFO
+ my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
+ "Write_rows_log_event::write_row(%lld)",
+ (long long) wsrep_thd_trx_seqno(thd));
+ message= thd->wsrep_info;
+#endif /* WSREP_PROC_INFO */
+
+ thd_proc_info(thd, message);
+ error= write_row(rgi, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
+ thd_proc_info(thd, tmp);
+
+ if (unlikely(error) && unlikely(!thd->is_error()))
+ {
+ DBUG_ASSERT(0);
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ }
+
+ return error;
+}
+
+#endif /* defined(HAVE_REPLICATION) */
+
+
+#if defined(HAVE_REPLICATION)
+uint8 Write_rows_log_event::get_trg_event_map()
+{
+ return trg2bit(TRG_EVENT_INSERT) | trg2bit(TRG_EVENT_UPDATE) |
+ trg2bit(TRG_EVENT_DELETE);
+}
+#endif
+
+/**************************************************************************
+ Delete_rows_log_event member functions
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+/*
+ Compares table->record[0] and table->record[1]
+
+ Returns TRUE if different.
+*/
+static bool record_compare(TABLE *table)
+{
+ bool result= FALSE;
+ /**
+ Compare full record only if:
+ - there are no blob fields (otherwise we would also need
+ to compare blobs contents as well);
+ - there are no varchar fields (otherwise we would also need
+ to compare varchar contents as well);
+ - there are no null fields, otherwise NULLed fields
+ contents (i.e., the don't care bytes) may show arbitrary
+ values, depending on how each engine handles internally.
+ */
+ if ((table->s->blob_fields +
+ table->s->varchar_fields +
+ table->s->null_fields) == 0)
+ {
+ result= cmp_record(table,record[1]);
+ goto record_compare_exit;
+ }
+
+ /* Compare null bits */
+ if (memcmp(table->null_flags,
+ table->null_flags+table->s->rec_buff_length,
+ table->s->null_bytes))
+ {
+ result= TRUE; // Diff in NULL value
+ goto record_compare_exit;
+ }
+
+ /* Compare fields */
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if (table->versioned() && (*ptr)->vers_sys_field())
+ {
+ continue;
+ }
+ /**
+ We only compare field contents that are not null.
+ NULL fields (i.e., their null bits) were compared
+ earlier.
+ */
+ if (!(*(ptr))->is_null())
+ {
+ if ((*ptr)->cmp_binary_offset(table->s->rec_buff_length))
+ {
+ result= TRUE;
+ goto record_compare_exit;
+ }
+ }
+ }
+
+record_compare_exit:
+ return result;
+}
+
+
+/**
+ Find the best key to use when locating the row in @c find_row().
+
+ A primary key is preferred if it exists; otherwise a unique index is
+ preferred. Else we pick the index with the smalles rec_per_key value.
+
+ If a suitable key is found, set @c m_key, @c m_key_nr and @c m_key_info
+ member fields appropriately.
+
+ @returns Error code on failure, 0 on success.
+*/
+int Rows_log_event::find_key()
+{
+ uint i, best_key_nr, last_part;
+ KEY *key, *UNINIT_VAR(best_key);
+ ulong UNINIT_VAR(best_rec_per_key), tmp;
+ DBUG_ENTER("Rows_log_event::find_key");
+ DBUG_ASSERT(m_table);
+
+ best_key_nr= MAX_KEY;
+
+ /*
+ Keys are sorted so that any primary key is first, followed by unique keys,
+ followed by any other. So we will automatically pick the primary key if
+ it exists.
+ */
+ for (i= 0, key= m_table->key_info; i < m_table->s->keys; i++, key++)
+ {
+ if (!m_table->s->keys_in_use.is_set(i))
+ continue;
+ /*
+ We cannot use a unique key with NULL-able columns to uniquely identify
+ a row (but we can still select it for range scan below if nothing better
+ is available).
+ */
+ if ((key->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
+ {
+ best_key_nr= i;
+ best_key= key;
+ break;
+ }
+ /*
+ We can only use a non-unique key if it allows range scans (ie. skip
+ FULLTEXT indexes and such).
+ */
+ last_part= key->user_defined_key_parts - 1;
+ DBUG_PRINT("info", ("Index %s rec_per_key[%u]= %lu",
+ key->name.str, last_part, key->rec_per_key[last_part]));
+ if (!(m_table->file->index_flags(i, last_part, 1) & HA_READ_NEXT))
+ continue;
+
+ tmp= key->rec_per_key[last_part];
+ if (best_key_nr == MAX_KEY || (tmp > 0 && tmp < best_rec_per_key))
+ {
+ best_key_nr= i;
+ best_key= key;
+ best_rec_per_key= tmp;
+ }
+ }
+
+ if (best_key_nr == MAX_KEY)
+ {
+ m_key_info= NULL;
+ DBUG_RETURN(0);
+ }
+
+ // Allocate buffer for key searches
+ m_key= (uchar *) my_malloc(PSI_INSTRUMENT_ME, best_key->key_length, MYF(MY_WME));
+ if (m_key == NULL)
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ m_key_info= best_key;
+ m_key_nr= best_key_nr;
+
+ DBUG_RETURN(0);;
+}
+
+
+/*
+ Check if we are already spending too much time on this statement.
+ if we are, warn user that it might be because table does not have
+ a PK, but only if the warning was not printed before for this STMT.
+
+ @param type The event type code.
+ @param table_name The name of the table that the slave is
+ operating.
+ @param is_index_scan States whether the slave is doing an index scan
+ or not.
+ @param rli The relay metadata info.
+*/
+static inline
+void issue_long_find_row_warning(Log_event_type type,
+ const char *table_name,
+ bool is_index_scan,
+ rpl_group_info *rgi)
+{
+ if ((global_system_variables.log_warnings > 1 &&
+ !rgi->is_long_find_row_note_printed()))
+ {
+ ulonglong now= microsecond_interval_timer();
+ ulonglong stmt_ts= rgi->get_row_stmt_start_timestamp();
+
+ DBUG_EXECUTE_IF("inject_long_find_row_note",
+ stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2*HRTIME_RESOLUTION););
+
+ longlong delta= (now - stmt_ts)/HRTIME_RESOLUTION;
+
+ if (delta > LONG_FIND_ROW_THRESHOLD)
+ {
+ rgi->set_long_find_row_note_printed();
+ const char* evt_type= LOG_EVENT_IS_DELETE_ROW(type) ? " DELETE" : "n UPDATE";
+ const char* scan_type= is_index_scan ? "scanning an index" : "scanning the table";
+
+ sql_print_information("The slave is applying a ROW event on behalf of a%s statement "
+ "on table %s and is currently taking a considerable amount "
+ "of time (%lld seconds). This is due to the fact that it is %s "
+ "while looking up records to be processed. Consider adding a "
+ "primary key (or unique key) to the table to improve "
+ "performance.",
+ evt_type, table_name, (long) delta, scan_type);
+ }
+ }
+}
+
+
+/*
+ HA_ERR_KEY_NOT_FOUND is a fatal error normally, but it's an expected
+ error in speculate optimistic mode, so use something non-fatal instead
+*/
+static int row_not_found_error(rpl_group_info *rgi)
+{
+ return rgi->speculation != rpl_group_info::SPECULATE_OPTIMISTIC
+ ? HA_ERR_KEY_NOT_FOUND : HA_ERR_RECORD_CHANGED;
+}
+
+/**
+ Locate the current row in event's table.
+
+ The current row is pointed by @c m_curr_row. Member @c m_width tells
+ how many columns are there in the row (this can be differnet from
+ the number of columns in the table). It is assumed that event's
+ table is already open and pointed by @c m_table.
+
+ If a corresponding record is found in the table it is stored in
+ @c m_table->record[0]. Note that when record is located based on a primary
+ key, it is possible that the record found differs from the row being located.
+
+ If no key is specified or table does not have keys, a table scan is used to
+ find the row. In that case the row should be complete and contain values for
+ all columns. However, it can still be shorter than the table, i.e. the table
+ can contain extra columns not present in the row. It is also possible that
+ the table has fewer columns than the row being located.
+
+ @returns Error code on failure, 0 on success.
+
+ @post In case of success @c m_table->record[0] contains the record found.
+ Also, the internal "cursor" of the table is positioned at the record found.
+
+ @note If the engine allows random access of the records, a combination of
+ @c position() and @c rnd_pos() will be used.
+
+ Note that one MUST call ha_index_or_rnd_end() after this function if
+ it returns 0 as we must leave the row position in the handler intact
+ for any following update/delete command.
+*/
+
+int Rows_log_event::find_row(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Rows_log_event::find_row");
+
+ DBUG_ASSERT(m_table && m_table->in_use != NULL);
+
+ TABLE *table= m_table;
+ int error= 0;
+ bool is_table_scan= false, is_index_scan= false;
+
+ /*
+ rpl_row_tabledefs.test specifies that
+ if the extra field on the slave does not have a default value
+ and this is okay with Delete or Update events.
+ Todo: fix wl3228 hld that requires defauls for all types of events
+ */
+
+ prepare_record(table, m_width, FALSE);
+ error= unpack_current_row(rgi);
+
+ m_vers_from_plain= false;
+ if (table->versioned())
+ {
+ Field *row_end= table->vers_end_field();
+ DBUG_ASSERT(table->read_set);
+ bitmap_set_bit(table->read_set, row_end->field_index);
+ // check whether master table is unversioned
+ if (row_end->val_int() == 0)
+ {
+ bitmap_set_bit(table->write_set, row_end->field_index);
+ // Plain source table may have a PRIMARY KEY. And row_end is always
+ // a part of PRIMARY KEY. Set it to max value for engine to find it in
+ // index. Needed for an UPDATE/DELETE cases.
+ table->vers_end_field()->set_max();
+ m_vers_from_plain= true;
+ }
+ table->file->column_bitmaps_signal();
+ }
+
+ DBUG_PRINT("info",("looking for the following record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+
+ if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ table->s->primary_key < MAX_KEY)
+ {
+ /*
+ Use a more efficient method to fetch the record given by
+ table->record[0] if the engine allows it. We first compute a
+ row reference using the position() member function (it will be
+ stored in table->file->ref) and the use rnd_pos() to position
+ the "cursor" (i.e., record[0] in this case) at the correct row.
+
+ TODO: Add a check that the correct record has been fetched by
+ comparing with the original record. Take into account that the
+ record on the master and slave can be of different
+ length. Something along these lines should work:
+
+ ADD>>> store_record(table,record[1]);
+ int error= table->file->ha_rnd_pos(table->record[0],
+ table->file->ref);
+ ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0],
+ table->s->reclength) == 0);
+
+ */
+ int error;
+ DBUG_PRINT("info",("locating record using primary key (position)"));
+
+ error= table->file->ha_rnd_pos_by_record(table->record[0]);
+ if (unlikely(error))
+ {
+ DBUG_PRINT("info",("rnd_pos returns error %d",error));
+ if (error == HA_ERR_KEY_NOT_FOUND)
+ error= row_not_found_error(rgi);
+ table->file->print_error(error, MYF(0));
+ }
+ DBUG_RETURN(error);
+ }
+
+ // We can't use position() - try other methods.
+
+ /*
+ We need to retrieve all fields
+ TODO: Move this out from this function to main loop
+ */
+ table->use_all_columns();
+
+ /*
+ Save copy of the record in table->record[1]. It might be needed
+ later if linear search is used to find exact match.
+ */
+ store_record(table,record[1]);
+
+ if (m_key_info)
+ {
+ DBUG_PRINT("info",("locating record using key #%u [%s] (index_read)",
+ m_key_nr, m_key_info->name.str));
+ /* We use this to test that the correct key is used in test cases. */
+ DBUG_EXECUTE_IF("slave_crash_if_wrong_index",
+ if(0 != strcmp(m_key_info->name.str,"expected_key")) abort(););
+
+ /* The key is active: search the table using the index */
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(m_key_nr, FALSE)))
+ {
+ DBUG_PRINT("info",("ha_index_init returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ goto end;
+ }
+
+ /* Fill key data for the row */
+
+ DBUG_ASSERT(m_key);
+ key_copy(m_key, table->record[0], m_key_info, 0);
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_valgrind
+ DBUG_DUMP("key data", m_key, m_key_info->key_length);
+#endif
+
+ /*
+ We need to set the null bytes to ensure that the filler bit are
+ all set when returning. There are storage engines that just set
+ the necessary bits on the bytes and don't set the filler bits
+ correctly.
+ */
+ if (table->s->null_bytes > 0)
+ table->record[0][table->s->null_bytes - 1]|=
+ 256U - (1U << table->s->last_null_bit_pos);
+
+ if (unlikely((error= table->file->ha_index_read_map(table->record[0],
+ m_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))))
+ {
+ DBUG_PRINT("info",("no record matching the key found in the table"));
+ if (error == HA_ERR_KEY_NOT_FOUND)
+ error= row_not_found_error(rgi);
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ goto end;
+ }
+
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+#ifndef HAVE_valgrind
+ DBUG_PRINT("info",("found first matching record"));
+ DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
+#endif
+ /*
+ Below is a minor "optimization". If the key (i.e., key number
+ 0) has the HA_NOSAME flag set, we know that we have found the
+ correct record (since there can be no duplicates); otherwise, we
+ have to compare the record with the one found to see if it is
+ the correct one.
+
+ CAVEAT! This behaviour is essential for the replication of,
+ e.g., the mysql.proc table since the correct record *shall* be
+ found using the primary key *only*. There shall be no
+ comparison of non-PK columns to decide if the correct record is
+ found. I can see no scenario where it would be incorrect to
+ chose the row to change only using a PK or an UNNI.
+ */
+ if (table->key_info->flags & HA_NOSAME)
+ {
+ /* Unique does not have non nullable part */
+ if (!(table->key_info->flags & (HA_NULL_PART_KEY)))
+ {
+ error= 0;
+ goto end;
+ }
+ else
+ {
+ KEY *keyinfo= table->key_info;
+ /*
+ Unique has nullable part. We need to check if there is any
+ field in the BI image that is null and part of UNNI.
+ */
+ bool null_found= FALSE;
+ 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;
+ null_found= (*f)->is_null();
+ }
+
+ if (!null_found)
+ {
+ error= 0;
+ goto end;
+ }
+
+ /* else fall through to index scan */
+ }
+ }
+
+ is_index_scan=true;
+
+ /*
+ In case key is not unique, we still have to iterate over records found
+ and find the one which is identical to the row given. A copy of the
+ record we are looking for is stored in record[1].
+ */
+ DBUG_PRINT("info",("non-unique index, scanning it to find matching record"));
+ /* We use this to test that the correct key is used in test cases. */
+ DBUG_EXECUTE_IF("slave_crash_if_index_scan", abort(););
+
+ while (record_compare(table))
+ {
+ while ((error= table->file->ha_index_next(table->record[0])))
+ {
+ DBUG_PRINT("info",("no record matching the given row found"));
+ table->file->print_error(error, MYF(0));
+ table->file->ha_index_end();
+ goto end;
+ }
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info",("locating record using table scan (rnd_next)"));
+ /* We use this to test that the correct key is used in test cases. */
+ DBUG_EXECUTE_IF("slave_crash_if_table_scan", abort(););
+
+ /* We don't have a key: search the table using rnd_next() */
+ if (unlikely((error= table->file->ha_rnd_init_with_error(1))))
+ {
+ DBUG_PRINT("info",("error initializing table scan"
+ " (ha_rnd_init returns %d)",error));
+ goto end;
+ }
+
+ is_table_scan= true;
+
+ /* Continue until we find the right record or have made a full loop */
+ do
+ {
+ error= table->file->ha_rnd_next(table->record[0]);
+
+ if (unlikely(error))
+ DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
+ switch (error) {
+
+ case 0:
+ DBUG_DUMP("record found", table->record[0], table->s->reclength);
+ break;
+
+ case HA_ERR_END_OF_FILE:
+ DBUG_PRINT("info", ("Record not found"));
+ table->file->ha_rnd_end();
+ goto end;
+
+ default:
+ DBUG_PRINT("info", ("Failed to get next record"
+ " (rnd_next returns %d)",error));
+ table->file->print_error(error, MYF(0));
+ table->file->ha_rnd_end();
+ goto end;
+ }
+ }
+ while (record_compare(table));
+
+ /*
+ Note: above record_compare will take into accout all record fields
+ which might be incorrect in case a partial row was given in the event
+ */
+
+ DBUG_ASSERT(error == HA_ERR_END_OF_FILE || error == 0);
+ }
+
+end:
+ if (is_table_scan || is_index_scan)
+ issue_long_find_row_warning(get_general_type_code(), m_table->alias.c_ptr(),
+ is_index_scan, rgi);
+ DBUG_RETURN(error);
+}
+
+#endif
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+
+Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid, bool is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional,
+ DELETE_ROWS_EVENT_V1)
+{
+}
+
+Delete_rows_compressed_log_event::Delete_rows_compressed_log_event(
+ THD *thd_arg, TABLE *tbl_arg,
+ ulong tid_arg,
+ bool is_transactional)
+ : Delete_rows_log_event(thd_arg, tbl_arg, tid_arg, is_transactional)
+{
+ m_type= DELETE_ROWS_COMPRESSED_EVENT_V1;
+}
+
+bool Delete_rows_compressed_log_event::write()
+{
+ return Rows_log_event::write_compressed();
+}
+
+
+#if defined(HAVE_REPLICATION)
+
+int
+Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ /*
+ Increment the global status delete count variable
+ */
+ if (get_flags(STMT_END_F))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_DELETE]);
+
+ if ((m_table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) &&
+ m_table->s->primary_key < MAX_KEY)
+ {
+ /*
+ We don't need to allocate any memory for m_key since it is not used.
+ */
+ return 0;
+ }
+ if (do_invoke_trigger())
+ m_table->prepare_triggers_for_delete_stmt_or_event();
+
+ return find_key();
+}
+
+int
+Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key);
+ m_key= NULL;
+ m_key_info= NULL;
+
+ return error;
+}
+
+int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
+{
+ int error;
+ const char *tmp= thd->get_proc_info();
+ const char *message= "Delete_rows_log_event::find_row()";
+ const bool invoke_triggers= (m_table->triggers && do_invoke_trigger());
+ DBUG_ASSERT(m_table != NULL);
+
+#ifdef WSREP_PROC_INFO
+ my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
+ "Delete_rows_log_event::find_row(%lld)",
+ (long long) wsrep_thd_trx_seqno(thd));
+ message= thd->wsrep_info;
+#endif /* WSREP_PROC_INFO */
+
+ thd_proc_info(thd, message);
+ if (likely(!(error= find_row(rgi))))
+ {
+ /*
+ Delete the record found, located in record[0]
+ */
+ message= "Delete_rows_log_event::ha_delete_row()";
+#ifdef WSREP_PROC_INFO
+ snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
+ "Delete_rows_log_event::ha_delete_row(%lld)",
+ (long long) wsrep_thd_trx_seqno(thd));
+ message= thd->wsrep_info;
+#endif
+ thd_proc_info(thd, message);
+
+ if (invoke_triggers &&
+ unlikely(process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ if (likely(!error))
+ {
+ m_table->mark_columns_per_binlog_row_image();
+ if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
+ {
+ Field *end= m_table->vers_end_field();
+ bitmap_set_bit(m_table->write_set, end->field_index);
+ store_record(m_table, record[1]);
+ end->set_time();
+ error= m_table->file->ha_update_row(m_table->record[1],
+ m_table->record[0]);
+ }
+ else
+ {
+ error= m_table->file->ha_delete_row(m_table->record[0]);
+ }
+ m_table->default_column_bitmaps();
+ }
+ if (invoke_triggers && likely(!error) &&
+ unlikely(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();
+ }
+ thd_proc_info(thd, tmp);
+ return error;
+}
+
+#endif /* defined(HAVE_REPLICATION) */
+
+#if defined(HAVE_REPLICATION)
+uint8 Delete_rows_log_event::get_trg_event_map()
+{
+ return trg2bit(TRG_EVENT_DELETE);
+}
+#endif
+
+/**************************************************************************
+ Update_rows_log_event member functions
+**************************************************************************/
+
+/*
+ Constructor used to build an event for writing to the binary log.
+ */
+Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid,
+ bool is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, tbl_arg->read_set, is_transactional,
+ UPDATE_ROWS_EVENT_V1)
+{
+ init(tbl_arg->rpl_write_set);
+}
+
+Update_rows_compressed_log_event::Update_rows_compressed_log_event(THD *thd_arg, TABLE *tbl_arg,
+ ulong tid,
+ bool is_transactional)
+: Update_rows_log_event(thd_arg, tbl_arg, tid, is_transactional)
+{
+ m_type = UPDATE_ROWS_COMPRESSED_EVENT_V1;
+}
+
+bool Update_rows_compressed_log_event::write()
+{
+ return Rows_log_event::write_compressed();
+}
+
+void Update_rows_log_event::init(MY_BITMAP const *cols)
+{
+ /* 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)))
+ {
+ /* Cols can be zero if this is a dummy binrows event */
+ if (likely(cols != NULL))
+ {
+ memcpy(m_cols_ai.bitmap, cols->bitmap, no_bytes_in_map(cols));
+ create_last_word_mask(&m_cols_ai);
+ }
+ }
+}
+
+
+#if defined(HAVE_REPLICATION)
+
+int
+Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability *const)
+{
+ /*
+ Increment the global status update count variable
+ */
+ if (get_flags(STMT_END_F))
+ status_var_increment(thd->status_var.com_stat[SQLCOM_UPDATE]);
+
+ int err;
+ if ((err= find_key()))
+ return err;
+
+ if (do_invoke_trigger())
+ m_table->prepare_triggers_for_update_stmt_or_event();
+
+ return 0;
+}
+
+int
+Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability *const,
+ int error)
+{
+ /*error= ToDo:find out what this should really be, this triggers close_scan in nbd, returning error?*/
+ m_table->file->ha_index_or_rnd_end();
+ my_free(m_key); // Free for multi_malloc
+ m_key= NULL;
+ m_key_info= NULL;
+
+ return error;
+}
+
+int
+Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
+{
+ const bool invoke_triggers= (m_table->triggers && do_invoke_trigger());
+ const char *tmp= thd->get_proc_info();
+ const char *message= "Update_rows_log_event::find_row()";
+ DBUG_ASSERT(m_table != NULL);
+
+#ifdef WSREP_PROC_INFO
+ my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
+ "Update_rows_log_event::find_row(%lld)",
+ (long long) wsrep_thd_trx_seqno(thd));
+ message= thd->wsrep_info;
+#endif /* WSREP_PROC_INFO */
+
+ thd_proc_info(thd, message);
+ // Temporary fix to find out why it fails [/Matz]
+ memcpy(m_table->read_set->bitmap, m_cols.bitmap, (m_table->read_set->n_bits + 7) / 8);
+ memcpy(m_table->write_set->bitmap, m_cols_ai.bitmap, (m_table->write_set->n_bits + 7) / 8);
+
+ m_table->mark_columns_per_binlog_row_image();
+
+ int error= find_row(rgi);
+ if (unlikely(error))
+ {
+ /*
+ We need to read the second image in the event of error to be
+ able to skip to the next pair of updates
+ */
+ if ((m_curr_row= m_curr_row_end))
+ unpack_current_row(rgi, &m_cols_ai);
+ thd_proc_info(thd, tmp);
+ return error;
+ }
+
+ /*
+ This is the situation after locating BI:
+
+ ===|=== before image ====|=== after image ===|===
+ ^ ^
+ m_curr_row m_curr_row_end
+
+ BI found in the table is stored in record[0]. We copy it to record[1]
+ and unpack AI to record[0].
+ */
+
+ store_record(m_table,record[1]);
+
+ m_curr_row= m_curr_row_end;
+ message= "Update_rows_log_event::unpack_current_row()";
+#ifdef WSREP_PROC_INFO
+ my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
+ "Update_rows_log_event::unpack_current_row(%lld)",
+ (long long) wsrep_thd_trx_seqno(thd));
+ message= thd->wsrep_info;
+#endif /* WSREP_PROC_INFO */
+
+ /* this also updates m_curr_row_end */
+ thd_proc_info(thd, message);
+ if (unlikely((error= unpack_current_row(rgi, &m_cols_ai))))
+ goto err;
+
+ /*
+ Now we have the right row to update. The old row (the one we're
+ looking for) is in record[1] and the new row is in record[0].
+ */
+#ifndef HAVE_valgrind
+ /*
+ Don't print debug messages when running valgrind since they can
+ trigger false warnings.
+ */
+ DBUG_PRINT("info",("Updating row in table"));
+ DBUG_DUMP("old record", m_table->record[1], m_table->s->reclength);
+ DBUG_DUMP("new values", m_table->record[0], m_table->s->reclength);
+#endif
+
+ message= "Update_rows_log_event::ha_update_row()";
+#ifdef WSREP_PROC_INFO
+ my_snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
+ "Update_rows_log_event::ha_update_row(%lld)",
+ (long long) wsrep_thd_trx_seqno(thd));
+ message= thd->wsrep_info;
+#endif /* WSREP_PROC_INFO */
+
+ thd_proc_info(thd, message);
+ if (invoke_triggers &&
+ unlikely(process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE)))
+ {
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ goto err;
+ }
+
+ if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
+ m_table->vers_update_fields();
+ error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
+ if (unlikely(error == HA_ERR_RECORD_IS_THE_SAME))
+ error= 0;
+ if (m_vers_from_plain && m_table->versioned(VERS_TIMESTAMP))
+ {
+ store_record(m_table, record[2]);
+ error= vers_insert_history_row(m_table);
+ restore_record(m_table, record[2]);
+ }
+ m_table->default_column_bitmaps();
+
+ if (invoke_triggers && likely(!error) &&
+ unlikely(process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE)))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+
+ thd_proc_info(thd, tmp);
+
+err:
+ m_table->file->ha_index_or_rnd_end();
+ return error;
+}
+
+#endif /* defined(HAVE_REPLICATION) */
+
+
+#if defined(HAVE_REPLICATION)
+uint8 Update_rows_log_event::get_trg_event_map()
+{
+ return trg2bit(TRG_EVENT_UPDATE);
+}
+#endif
+
+
+void Incident_log_event::pack_info(Protocol *protocol)
+{
+ char buf[256];
+ size_t bytes;
+ if (m_message.length > 0)
+ bytes= my_snprintf(buf, sizeof(buf), "#%d (%s)",
+ m_incident, description());
+ else
+ bytes= my_snprintf(buf, sizeof(buf), "#%d (%s): %s",
+ m_incident, description(), m_message.str);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+
+
+#if defined(WITH_WSREP)
+/*
+ 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)
+{
+ char *head= (*arg_buf);
+ uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
+ char *buf= (*arg_buf);
+ const char *error= 0;
+ Log_event *res= 0;
+ DBUG_ENTER("wsrep_read_log_event");
+
+ 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,(uchar)head[EVENT_TYPE_OFFSET]);
+ }
+ (*arg_buf)+= data_len;
+ (*arg_buf_len)-= data_len;
+ DBUG_RETURN(res);
+}
+#endif
+
+
+#if defined(HAVE_REPLICATION)
+int
+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");
+
+ if (ignored_error_code(ER_SLAVE_INCIDENT))
+ {
+ DBUG_PRINT("info", ("Ignoring Incident"));
+ DBUG_RETURN(0);
+ }
+
+ rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT, NULL,
+ ER_THD(rgi->thd, ER_SLAVE_INCIDENT),
+ description(),
+ m_message.length > 0 ? m_message.str : "<none>");
+ DBUG_RETURN(1);
+}
+#endif
+
+
+bool
+Incident_log_event::write_data_header()
+{
+ DBUG_ENTER("Incident_log_event::write_data_header");
+ DBUG_PRINT("enter", ("m_incident: %d", m_incident));
+ uchar buf[sizeof(int16)];
+ int2store(buf, (int16) m_incident);
+ DBUG_RETURN(write_data(buf, sizeof(buf)));
+}
+
+bool
+Incident_log_event::write_data_body()
+{
+ uchar tmp[1];
+ DBUG_ENTER("Incident_log_event::write_data_body");
+ tmp[0]= (uchar) m_message.length;
+ DBUG_RETURN(write_data(tmp, sizeof(tmp)) ||
+ write_data(m_message.str, m_message.length));
+}
+
+
+/* Pack info for its unrecognized ignorable event */
+void Ignorable_log_event::pack_info(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);
+}
+
+
+#if defined(HAVE_REPLICATION)
+Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event)
+{
+ uint8 header_size= description_event->common_header_len;
+ ident_len = event_len - header_size;
+ set_if_smaller(ident_len,FN_REFLEN-1);
+ log_ident= buf + header_size;
+}
+#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 ||
+ event_type == TRANSACTION_CONTEXT_EVENT ||
+ event_type == VIEW_CHANGE_EVENT ||
+ (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
+ return 1;
+ return 0;
+}
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 54128c2ee97..4772dc017f9 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2007, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2020, 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
@@ -24,6 +25,14 @@
#include <mysql/plugin.h>
#include <mysql/service_thd_wait.h>
#include <mysql/psi/mysql_stage.h>
+#include <tpool.h>
+#include <pfs_metadata_provider.h>
+#include <mysql/psi/mysql_mdl.h>
+#include <algorithm>
+#include <array>
+
+static PSI_memory_key key_memory_MDL_context_acquire_locks;
+
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_MDL_wait_LOCK_wait_status;
@@ -48,6 +57,11 @@ static PSI_cond_info all_mdl_conds[]=
{ &key_MDL_wait_COND_wait_status, "MDL_context::COND_wait_status", 0}
};
+static PSI_memory_info all_mdl_memory[]=
+{
+ { &key_memory_MDL_context_acquire_locks, "MDL_context::acquire_locks", 0}
+};
+
/**
Initialise all the performance schema instrumentation points
used by the MDL subsystem.
@@ -65,6 +79,9 @@ static void init_mdl_psi_keys(void)
count= array_elements(all_mdl_conds);
mysql_cond_register("sql", all_mdl_conds, count);
+ count= array_elements(all_mdl_memory);
+ mysql_memory_register("sql", all_mdl_memory, count);
+
MDL_key::init_psi_keys();
}
#endif /* HAVE_PSI_INTERFACE */
@@ -326,32 +343,24 @@ public:
class Ticket_list
{
+ using List= ilist<MDL_ticket>;
public:
- typedef I_P_List<MDL_ticket,
- I_P_List_adapter<MDL_ticket,
- &MDL_ticket::next_in_lock,
- &MDL_ticket::prev_in_lock>,
- I_P_List_null_counter,
- I_P_List_fast_push_back<MDL_ticket> >
- List;
- operator const List &() const { return m_list; }
- Ticket_list() :m_bitmap(0) {}
+ Ticket_list() :m_bitmap(0) { m_type_counters.fill(0); }
void add_ticket(MDL_ticket *ticket);
void remove_ticket(MDL_ticket *ticket);
- bool is_empty() const { return m_list.is_empty(); }
+ bool is_empty() const { return m_list.empty(); }
bitmap_t bitmap() const { return m_bitmap; }
- private:
- void clear_bit_if_not_in_list(enum_mdl_type type);
+ List::const_iterator begin() const { return m_list.begin(); }
+ List::const_iterator end() const { return m_list.end(); }
private:
/** List of tickets. */
List m_list;
/** Bitmap of types of tickets in this list. */
bitmap_t m_bitmap;
+ std::array<uint32_t, MDL_BACKUP_END> m_type_counters; // hash table
};
- typedef Ticket_list::List::Iterator Ticket_iterator;
-
/**
Helper struct which defines how different types of locks are handled
@@ -560,14 +569,12 @@ public:
{ return m_strategy->needs_notification(ticket); }
void notify_conflicting_locks(MDL_context *ctx)
{
- Ticket_iterator it(m_granted);
- MDL_ticket *conflicting_ticket;
- while ((conflicting_ticket= it++))
+ for (const auto &conflicting_ticket : m_granted)
{
- if (conflicting_ticket->get_ctx() != ctx &&
- m_strategy->conflicting_locks(conflicting_ticket))
+ if (conflicting_ticket.get_ctx() != ctx &&
+ m_strategy->conflicting_locks(&conflicting_ticket))
{
- MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+ MDL_context *conflicting_ctx= conflicting_ticket.get_ctx();
ctx->get_owner()->
notify_shared_lock(conflicting_ctx->get_owner(),
@@ -709,21 +716,21 @@ struct mdl_iterate_arg
static my_bool mdl_iterate_lock(MDL_lock *lock, mdl_iterate_arg *arg)
{
- int res= FALSE;
/*
We can skip check for m_strategy here, becase m_granted
must be empty for such locks anyway.
*/
mysql_prlock_rdlock(&lock->m_rwlock);
- MDL_lock::Ticket_iterator granted_it(lock->m_granted);
- MDL_lock::Ticket_iterator waiting_it(lock->m_waiting);
- MDL_ticket *ticket;
- while ((ticket= granted_it++) && !(res= arg->callback(ticket, arg->argument, true)))
- /* no-op */;
- while ((ticket= waiting_it++) && !(res= arg->callback(ticket, arg->argument, false)))
- /* no-op */;
+ bool res= std::any_of(lock->m_granted.begin(), lock->m_granted.end(),
+ [arg](MDL_ticket &ticket) {
+ return arg->callback(&ticket, arg->argument, true);
+ });
+ res= std::any_of(lock->m_waiting.begin(), lock->m_waiting.end(),
+ [arg](MDL_ticket &ticket) {
+ return arg->callback(&ticket, arg->argument, false);
+ });
mysql_prlock_unlock(&lock->m_rwlock);
- return MY_TEST(res);
+ return res;
}
@@ -953,16 +960,20 @@ bool MDL_context::fix_pins()
@param mdl_type The MDL lock type for the request.
*/
-void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace,
+void MDL_request::init_with_source(MDL_key::enum_mdl_namespace mdl_namespace,
const char *db_arg,
const char *name_arg,
enum_mdl_type mdl_type_arg,
- enum_mdl_duration mdl_duration_arg)
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file,
+ uint src_line)
{
key.mdl_key_init(mdl_namespace, db_arg, name_arg);
type= mdl_type_arg;
duration= mdl_duration_arg;
ticket= NULL;
+ m_src_file= src_file;
+ m_src_line= src_line;
}
@@ -975,14 +986,18 @@ void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace,
@param mdl_type_arg The MDL lock type for the request.
*/
-void MDL_request::init(const MDL_key *key_arg,
+void MDL_request::init_by_key_with_source(const MDL_key *key_arg,
enum_mdl_type mdl_type_arg,
- enum_mdl_duration mdl_duration_arg)
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file,
+ uint src_line)
{
key.mdl_key_init(key_arg);
type= mdl_type_arg;
duration= mdl_duration_arg;
ticket= NULL;
+ m_src_file= src_file;
+ m_src_line= src_line;
}
@@ -1011,13 +1026,16 @@ MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg
void MDL_ticket::destroy(MDL_ticket *ticket)
{
+ mysql_mdl_destroy(ticket->m_psi);
+ ticket->m_psi= NULL;
+
delete ticket;
}
/**
Return the 'weight' of this ticket for the
- victim selection algorithm. Requests with
+ victim selection algorithm. Requests with
lower weight are preferred to requests
with higher weight when choosing a victim.
*/
@@ -1125,6 +1143,7 @@ MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout,
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);
+ tpool::tpool_wait_begin();
while (!m_wait_status && !owner->is_killed() &&
wait_result != ETIMEDOUT && wait_result != ETIME)
{
@@ -1147,6 +1166,7 @@ MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout,
wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status,
abs_timeout);
}
+ tpool::tpool_wait_end();
thd_wait_end(NULL);
if (m_wait_status == EMPTY)
@@ -1177,27 +1197,6 @@ MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout,
/**
- Clear bit corresponding to the type of metadata lock in bitmap representing
- set of such types if list of tickets does not contain ticket with such type.
-
- @param[in,out] bitmap Bitmap representing set of types of locks.
- @param[in] list List to inspect.
- @param[in] type Type of metadata lock to look up in the list.
-*/
-
-void MDL_lock::Ticket_list::clear_bit_if_not_in_list(enum_mdl_type type)
-{
- MDL_lock::Ticket_iterator it(m_list);
- const MDL_ticket *ticket;
-
- while ((ticket= it++))
- if (ticket->get_type() == type)
- return;
- m_bitmap&= ~ MDL_BIT(type);
-}
-
-
-/**
Add ticket to MDL_lock's list of waiting requests and
update corresponding bitmap of lock types.
*/
@@ -1214,30 +1213,15 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
if ((this == &(ticket->get_lock()->m_waiting)) &&
wsrep_thd_is_BF(ticket->get_ctx()->get_thd(), false))
{
- Ticket_iterator itw(ticket->get_lock()->m_waiting);
- MDL_ticket *waiting;
- MDL_ticket *prev=NULL;
- bool added= false;
-
DBUG_ASSERT(WSREP(ticket->get_ctx()->get_thd()));
- while ((waiting= itw++) && !added)
- {
- if (!wsrep_thd_is_BF(waiting->get_ctx()->get_thd(), true))
- {
- WSREP_DEBUG("MDL add_ticket inserted before: %lu %s",
- thd_get_thread_id(waiting->get_ctx()->get_thd()),
- wsrep_thd_query(waiting->get_ctx()->get_thd()));
- /* Insert the ticket before the first non-BF waiting thd. */
- m_list.insert_after(prev, ticket);
- added= true;
- }
- prev= waiting;
- }
-
- /* Otherwise, insert the ticket at the back of the waiting list. */
- if (!added)
- m_list.push_back(ticket);
+ m_list.insert(std::find_if(ticket->get_lock()->m_waiting.begin(),
+ ticket->get_lock()->m_waiting.end(),
+ [](const MDL_ticket &waiting) {
+ return !wsrep_thd_is_BF(
+ waiting.get_ctx()->get_thd(), true);
+ }),
+ *ticket);
}
else
#endif /* WITH_WSREP */
@@ -1246,9 +1230,10 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
Add ticket to the *back* of the queue to ensure fairness
among requests with the same priority.
*/
- m_list.push_back(ticket);
+ m_list.push_back(*ticket);
}
m_bitmap|= MDL_BIT(ticket->get_type());
+ m_type_counters[ticket->get_type()]++;
}
@@ -1259,18 +1244,15 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
void MDL_lock::Ticket_list::remove_ticket(MDL_ticket *ticket)
{
- m_list.remove(ticket);
+ m_list.remove(*ticket);
/*
Check if waiting queue has another ticket with the same type as
one which was removed. If there is no such ticket, i.e. we have
removed last ticket of particular type, then we need to update
bitmap of waiting ticket's types.
- Note that in most common case, i.e. when shared lock is removed
- from waiting queue, we are likely to find ticket of the same
- type early without performing full iteration through the list.
- So this method should not be too expensive.
*/
- clear_bit_if_not_in_list(ticket->get_type());
+ if (--m_type_counters[ticket->get_type()] == 0)
+ m_bitmap&= ~MDL_BIT(ticket->get_type());
}
@@ -1287,8 +1269,6 @@ void MDL_lock::Ticket_list::remove_ticket(MDL_ticket *ticket)
void MDL_lock::reschedule_waiters()
{
- MDL_lock::Ticket_iterator it(m_waiting);
- MDL_ticket *ticket;
bool skip_high_priority= false;
bitmap_t hog_lock_types= hog_lock_types_bitmap();
@@ -1343,20 +1323,20 @@ void MDL_lock::reschedule_waiters()
grant SNRW lock and there are no pending S or
SH locks.
*/
- while ((ticket= it++))
+ for (auto it= m_waiting.begin(); it != m_waiting.end(); ++it)
{
/*
Skip high-prio, strong locks if earlier we have decided to give way to
low-prio, weaker locks.
*/
if (skip_high_priority &&
- ((MDL_BIT(ticket->get_type()) & hog_lock_types) != 0))
+ ((MDL_BIT(it->get_type()) & hog_lock_types) != 0))
continue;
- if (can_grant_lock(ticket->get_type(), ticket->get_ctx(),
+ if (can_grant_lock(it->get_type(), it->get_ctx(),
skip_high_priority))
{
- if (! ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))
+ if (!it->get_ctx()->m_wait.set_status(MDL_wait::GRANTED))
{
/*
Satisfy the found request by updating lock structures.
@@ -1366,15 +1346,19 @@ void MDL_lock::reschedule_waiters()
when manages to do so, already sees an updated state of the
MDL_lock object.
*/
- m_waiting.remove_ticket(ticket);
- m_granted.add_ticket(ticket);
+ auto prev_it= std::prev(it); // this might be begin()-- but the hack
+ // works because list is circular
+ m_waiting.remove_ticket(&*it);
+ m_granted.add_ticket(&*it);
/*
Increase counter of successively granted high-priority strong locks,
if we have granted one.
*/
- if ((MDL_BIT(ticket->get_type()) & hog_lock_types) != 0)
+ if ((MDL_BIT(it->get_type()) & hog_lock_types) != 0)
m_hog_lock_count++;
+
+ it= prev_it;
}
/*
If we could not update the wait slot of the waiter,
@@ -1747,14 +1731,13 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
if (m_granted.bitmap() & granted_incompat_map)
{
- Ticket_iterator it(m_granted);
bool can_grant= true;
/* Check that the incompatible lock belongs to some other context. */
- while (auto ticket= it++)
+ for (const auto &ticket : m_granted)
{
- if (ticket->get_ctx() != requestor_ctx &&
- ticket->is_incompatible_when_granted(type_arg))
+ if (ticket.get_ctx() != requestor_ctx &&
+ ticket.is_incompatible_when_granted(type_arg))
{
can_grant= false;
#ifdef WITH_WSREP
@@ -1766,12 +1749,12 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
requestor_ctx->get_thd()->wsrep_cs().mode() ==
wsrep::client_state::m_rsu)
{
- wsrep_handle_mdl_conflict(requestor_ctx, ticket, &key);
+ wsrep_handle_mdl_conflict(requestor_ctx, &ticket, &key);
if (wsrep_log_conflicts)
{
- auto key= ticket->get_key();
+ auto key= ticket.get_key();
WSREP_INFO("MDL conflict db=%s table=%s ticket=%d solved by abort",
- key->db_name(), key->name(), ticket->get_type());
+ key->db_name(), key->name(), ticket.get_type());
}
continue;
}
@@ -1793,12 +1776,10 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
inline unsigned long
MDL_lock::get_lock_owner() const
{
- Ticket_iterator it(m_granted);
- MDL_ticket *ticket;
+ if (m_granted.is_empty())
+ return 0;
- if ((ticket= it++))
- return ticket->get_ctx()->get_thread_id();
- return 0;
+ return m_granted.begin()->get_ctx()->get_thread_id();
}
@@ -2095,6 +2076,15 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
return TRUE;
}
+ DBUG_ASSERT(ticket->m_psi == NULL);
+ ticket->m_psi= mysql_mdl_create(ticket,
+ &mdl_request->key,
+ mdl_request->type,
+ mdl_request->duration,
+ MDL_ticket::PENDING,
+ mdl_request->m_src_file,
+ mdl_request->m_src_line);
+
ticket->m_lock= lock;
if (lock->can_grant_lock(mdl_request->type, this, false))
@@ -2106,6 +2096,8 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
m_tickets[mdl_request->duration].push_front(ticket);
mdl_request->ticket= ticket;
+
+ mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
}
else
*out_ticket= ticket;
@@ -2155,6 +2147,15 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
)))
return TRUE;
+ DBUG_ASSERT(ticket->m_psi == NULL);
+ ticket->m_psi= mysql_mdl_create(ticket,
+ &mdl_request->key,
+ mdl_request->type,
+ mdl_request->duration,
+ MDL_ticket::PENDING,
+ mdl_request->m_src_file,
+ mdl_request->m_src_line);
+
/* clone() is not supposed to be used to get a stronger lock. */
DBUG_ASSERT(mdl_request->ticket->has_stronger_or_equal_type(ticket->m_type));
@@ -2167,6 +2168,8 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
m_tickets[mdl_request->duration].push_front(ticket);
+ mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
+
return FALSE;
}
@@ -2176,7 +2179,7 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
to wait for another thread which is not ready to commit.
This is always an error, as the upper level of parallel replication
should not allow a scheduling of a conflicting DDL until all earlier
- transactions has commited.
+ transactions have been committed.
This function is only called for a slave using parallel replication
and trying to get an exclusive lock for the table.
@@ -2185,18 +2188,16 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
#ifndef DBUG_OFF
bool MDL_lock::check_if_conflicting_replication_locks(MDL_context *ctx)
{
- Ticket_iterator it(m_granted);
- MDL_ticket *conflicting_ticket;
rpl_group_info *rgi_slave= ctx->get_thd()->rgi_slave;
if (!rgi_slave->gtid_sub_id)
return 0;
- while ((conflicting_ticket= it++))
+ for (const auto &conflicting_ticket : m_granted)
{
- if (conflicting_ticket->get_ctx() != ctx)
+ if (conflicting_ticket.get_ctx() != ctx)
{
- MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+ MDL_context *conflicting_ctx= conflicting_ticket.get_ctx();
rpl_group_info *conflicting_rgi_slave;
conflicting_rgi_slave= conflicting_ctx->get_thd()->rgi_slave;
@@ -2306,6 +2307,12 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
mysql_prlock_unlock(&lock->m_rwlock);
+ PSI_metadata_locker_state state __attribute__((unused));
+ PSI_metadata_locker *locker= NULL;
+
+ if (ticket->m_psi != NULL)
+ locker= PSI_CALL_start_metadata_wait(&state, ticket->m_psi, __FILE__, __LINE__);
+
will_wait_for(ticket);
/* There is a shared or exclusive lock on the object. */
@@ -2351,6 +2358,9 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
done_waiting_for();
+ if (locker != NULL)
+ PSI_CALL_end_metadata_wait(locker, 0);
+
if (wait_status != MDL_wait::GRANTED)
{
lock->remove_ticket(m_pins, &MDL_lock::m_waiting, ticket);
@@ -2386,6 +2396,8 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
mdl_request->ticket= ticket;
+ mysql_mdl_set_status(ticket->m_psi, MDL_ticket::GRANTED);
+
DBUG_RETURN(FALSE);
}
@@ -2431,8 +2443,8 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests,
DBUG_RETURN(FALSE);
/* Sort requests according to MDL_key. */
- if (! (sort_buf= (MDL_request **)my_malloc(req_count *
- sizeof(MDL_request*),
+ if (! (sort_buf= (MDL_request **)my_malloc(key_memory_MDL_context_acquire_locks,
+ req_count * sizeof(MDL_request*),
MYF(MY_WME))))
DBUG_RETURN(TRUE);
@@ -2517,8 +2529,8 @@ MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
mdl_ticket->get_key()->mdl_namespace() != MDL_key::BACKUP)
DBUG_RETURN(FALSE);
- mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT_BY_KEY(&mdl_xlock_request, &mdl_ticket->m_lock->key,
+ new_type, MDL_TRANSACTION);
if (acquire_lock(&mdl_xlock_request, lock_wait_timeout))
DBUG_RETURN(TRUE);
@@ -2562,16 +2574,11 @@ MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
bool MDL_lock::visit_subgraph(MDL_ticket *waiting_ticket,
MDL_wait_for_graph_visitor *gvisitor)
{
- MDL_ticket *ticket;
MDL_context *src_ctx= waiting_ticket->get_ctx();
bool result= TRUE;
mysql_prlock_rdlock(&m_rwlock);
- /* Must be initialized after taking a read lock. */
- Ticket_iterator granted_it(m_granted);
- Ticket_iterator waiting_it(m_waiting);
-
/*
MDL_lock's waiting and granted queues and MDL_context::m_waiting_for
member are updated by different threads when the lock is granted
@@ -2638,46 +2645,44 @@ bool MDL_lock::visit_subgraph(MDL_ticket *waiting_ticket,
node. In workloads that involve wait-for graph loops this
has proven to be a more efficient strategy [citation missing].
*/
- while ((ticket= granted_it++))
+ for (const auto& ticket : m_granted)
{
/* Filter out edges that point to the same node. */
- if (ticket->get_ctx() != src_ctx &&
- ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
- gvisitor->inspect_edge(ticket->get_ctx()))
+ if (ticket.get_ctx() != src_ctx &&
+ ticket.is_incompatible_when_granted(waiting_ticket->get_type()) &&
+ gvisitor->inspect_edge(ticket.get_ctx()))
{
goto end_leave_node;
}
}
- while ((ticket= waiting_it++))
+ for (const auto &ticket : m_waiting)
{
/* Filter out edges that point to the same node. */
- if (ticket->get_ctx() != src_ctx &&
- ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
- gvisitor->inspect_edge(ticket->get_ctx()))
+ if (ticket.get_ctx() != src_ctx &&
+ ticket.is_incompatible_when_waiting(waiting_ticket->get_type()) &&
+ gvisitor->inspect_edge(ticket.get_ctx()))
{
goto end_leave_node;
}
}
/* Recurse and inspect all adjacent nodes. */
- granted_it.rewind();
- while ((ticket= granted_it++))
+ for (const auto &ticket : m_granted)
{
- if (ticket->get_ctx() != src_ctx &&
- ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
- ticket->get_ctx()->visit_subgraph(gvisitor))
+ if (ticket.get_ctx() != src_ctx &&
+ ticket.is_incompatible_when_granted(waiting_ticket->get_type()) &&
+ ticket.get_ctx()->visit_subgraph(gvisitor))
{
goto end_leave_node;
}
}
- waiting_it.rewind();
- while ((ticket= waiting_it++))
+ for (const auto &ticket : m_waiting)
{
- if (ticket->get_ctx() != src_ctx &&
- ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
- ticket->get_ctx()->visit_subgraph(gvisitor))
+ if (ticket.get_ctx() != src_ctx &&
+ ticket.is_incompatible_when_waiting(waiting_ticket->get_type()) &&
+ ticket.get_ctx()->visit_subgraph(gvisitor))
{
goto end_leave_node;
}
@@ -2958,7 +2963,8 @@ MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
MDL_request mdl_request;
enum_mdl_duration not_unused;
/* We don't care about exact duration of lock here. */
- mdl_request.init(mdl_namespace, db, name, mdl_type, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&mdl_request, mdl_namespace, db, name, mdl_type,
+ MDL_TRANSACTION);
MDL_ticket *ticket= find_ticket(&mdl_request, &not_unused);
DBUG_ASSERT(ticket == NULL || ticket->m_lock);
@@ -3225,7 +3231,7 @@ const char *wsrep_get_mdl_namespace_name(MDL_key::enum_mdl_namespace ns)
return "UNKNOWN";
}
-void MDL_ticket::wsrep_report(bool debug)
+void MDL_ticket::wsrep_report(bool debug) const
{
if (!debug) return;
diff --git a/sql/mdl.h b/sql/mdl.h
index 123ffbcade6..f6b7154fba0 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -1,6 +1,7 @@
#ifndef MDL_H
#define MDL_H
/* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2020, 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,6 +17,7 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include "sql_plist.h"
+#include "ilist.h"
#include <my_sys.h>
#include <m_string.h>
#include <mysql_com.h>
@@ -355,7 +357,7 @@ enum enum_mdl_duration {
or "name".
*/
-class MDL_key
+struct MDL_key
{
public:
#ifdef HAVE_PSI_INTERFACE
@@ -537,18 +539,23 @@ public:
/** A lock is requested based on a fully qualified name and type. */
MDL_key key;
+ const char *m_src_file;
+ uint m_src_line;
+
public:
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
{ return alloc_root(mem_root, size); }
static void operator delete(void *, MEM_ROOT *) {}
- void init(MDL_key::enum_mdl_namespace namespace_arg,
+ void init_with_source(MDL_key::enum_mdl_namespace namespace_arg,
const char *db_arg, const char *name_arg,
enum_mdl_type mdl_type_arg,
- enum_mdl_duration mdl_duration_arg);
- void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg,
- enum_mdl_duration mdl_duration_arg);
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file, uint src_line);
+ void init_by_key_with_source(const MDL_key *key_arg, enum_mdl_type mdl_type_arg,
+ enum_mdl_duration mdl_duration_arg,
+ const char *src_file, uint src_line);
/** Set type of lock request. Can be only applied to pending locks. */
inline void set_type(enum_mdl_type type_arg)
{
@@ -613,6 +620,12 @@ public:
typedef void (*mdl_cached_object_release_hook)(void *);
+#define MDL_REQUEST_INIT(R, P1, P2, P3, P4, P5) \
+ (*R).init_with_source(P1, P2, P3, P4, P5, __FILE__, __LINE__)
+
+#define MDL_REQUEST_INIT_BY_KEY(R, P1, P2, P3) \
+ (*R).init_by_key_with_source(P1, P2, P3, __FILE__, __LINE__)
+
/**
An abstract class for inspection of a connected
@@ -677,7 +690,7 @@ public:
threads/contexts.
*/
-class MDL_ticket : public MDL_wait_for_subgraph
+class MDL_ticket : public MDL_wait_for_subgraph, public ilist_node<>
{
public:
/**
@@ -686,15 +699,9 @@ public:
*/
MDL_ticket *next_in_context;
MDL_ticket **prev_in_context;
- /**
- Pointers for participating in the list of satisfied/pending requests
- for the lock. Externally accessible.
- */
- MDL_ticket *next_in_lock;
- MDL_ticket **prev_in_lock;
public:
#ifdef WITH_WSREP
- void wsrep_report(bool debug);
+ void wsrep_report(bool debug) const;
#endif /* WITH_WSREP */
bool has_pending_conflicting_lock() const;
@@ -721,6 +728,11 @@ public:
/** Implement MDL_wait_for_subgraph interface. */
virtual bool accept_visitor(MDL_wait_for_graph_visitor *dvisitor);
virtual uint get_deadlock_weight() const;
+ /**
+ Status of lock request represented by the ticket as reflected in P_S.
+ */
+ enum enum_psi_status { PENDING = 0, GRANTED,
+ PRE_ACQUIRE_NOTIFY, POST_RELEASE_NOTIFY };
private:
friend class MDL_context;
@@ -734,9 +746,15 @@ private:
m_duration(duration_arg),
#endif
m_ctx(ctx_arg),
- m_lock(NULL)
+ m_lock(NULL),
+ m_psi(NULL)
{}
+ virtual ~MDL_ticket()
+ {
+ DBUG_ASSERT(m_psi == NULL);
+ }
+
static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg
#ifndef DBUG_OFF
, enum_mdl_duration duration_arg
@@ -763,6 +781,8 @@ private:
*/
MDL_lock *m_lock;
+ PSI_metadata_lock *m_psi;
+
private:
MDL_ticket(const MDL_ticket &); /* not implemented */
MDL_ticket &operator=(const MDL_ticket &); /* not implemented */
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 601268ec2f6..ec039962be9 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -56,28 +56,67 @@
contain scan parameters.
*/
-ha_rows
+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_estimate *cost)
+ uint *bufsz, uint *flags,
+ Cost_estimate *cost)
{
KEY_MULTI_RANGE range;
range_seq_t seq_it;
ha_rows total_rows= 0;
uint n_ranges=0;
- uint n_eq_ranges= 0;
- ulonglong total_touched_blocks= 0;
ha_rows max_rows= stats.records;
THD *thd= table->in_use;
+ ulonglong io_blocks;
+
+ /*
+ Counter of blocks that contain range edges for those ranges
+ for which records_in_range() is called
+ */
+ ulonglong edge_blocks_cnt= 0;
+ /*
+ Counter of blocks that contain index tuples for those ranges
+ for which records_in_range() is called
+ */
+ ulonglong range_blocks_cnt= 0;
+ /*
+ The position of the block containing the last record of the previous range
+ for which the info about range position is provided
+ */
+ ulonglong prev_range_last_block= UNUSED_PAGE_NO;
+ /* The counter of records the staring from prev_range_last_block */
+ ulonglong prev_range_last_block_records= 0;
+ /*
+ The counter of single point ranges.
+ (For single point ranges we do not call records_in_range())
+ */
+ ulonglong single_point_ranges= 0;
+ /*
+ The counter of of single point ranges that we succeded to assign
+ to some blocks
+ */
+ ulonglong assigned_single_point_ranges= 0;
+ /*
+ Counter of single point ranges for which records_in_range in not
+ called and that are encountered between two ranges without such property
+ For example, let's have a subsequence of ranges
+ R1,r1,....rk,R2
+ where r1,...,rk are single point ranges for which records_in_range is
+ called while R1 and R2 are not such ranges.
+ Then single_point_ranges_delta will count ranges r1,...,rk.
+ */
+ ulonglong unassigned_single_point_ranges= 0;
+
+ uint len= table->key_info[keyno].key_length + table->file->ref_length;
+ if (table->file->is_clustering_key(keyno))
+ len= table->s->stored_rec_length;
+ /* Assume block is 75 % full */
+ uint avg_block_records= ((uint) (stats.block_size*3/4))/len + 1;
uint limit= thd->variables.eq_range_index_dive_limit;
bool use_statistics_for_eq_range= eq_ranges_exceeds_limit(seq,
seq_init_param,
limit);
- uint len= table->key_info[keyno].key_length + table->file->ref_length;
- if (keyno == table->s->primary_key && table->file->primary_key_is_clustered())
- len= table->s->stored_rec_length;
- /* Assume block is 75 % full */
- uint avg_block_records= ((uint) (table->file->stats.block_size*3/4))/len + 1;
DBUG_ENTER("multi_range_read_info_const");
/* Default MRR implementation doesn't need buffer */
@@ -90,10 +129,8 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
if (unlikely(thd->killed != 0))
DBUG_RETURN(HA_POS_ERROR);
-
+
n_ranges++;
- if (range.range_flag & EQ_RANGE)
- n_eq_ranges++;
key_range *min_endp, *max_endp;
if (range.range_flag & GEOM_FLAG)
{
@@ -108,67 +145,192 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
max_endp= range.end_key.length? &range.end_key : NULL;
}
int keyparts_used= my_count_bits(range.start_key.keypart_map);
- if (use_statistics_for_eq_range &&
- !(range.range_flag & NULL_RANGE) &&
- (range.range_flag & EQ_RANGE) &&
- table->key_info[keyno].actual_rec_per_key(keyparts_used - 1) > 0.5)
+
+ if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
{
- if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
- rows= 1; /* there can be at most one row */
- else
- rows=
- (ha_rows) table->key_info[keyno].actual_rec_per_key(keyparts_used-1);
+ rows= 1;
+ /*
+ In this case we do not call records_in_range() and as a result
+ do not get any info on the edge blocks for this range. However if it
+ happens that the range for which we have such info uses the same block
+ for its first record as the last range for which such info is
+ provided uses for its last record then this range can be assigned
+ later to one of the blocks used by other ranges.
+
+ Note that we don't have to increment edge_blocks_cnt or
+ range_blocks_cnt here.
+ */
+ single_point_ranges++;
+ }
+ else if (use_statistics_for_eq_range &&
+ !(range.range_flag & NULL_RANGE) &&
+ (range.range_flag & EQ_RANGE) &&
+ table->key_info[keyno].actual_rec_per_key(keyparts_used - 1) > 0.5)
+ {
+ rows= ((ha_rows) table->key_info[keyno].
+ actual_rec_per_key(keyparts_used-1));
+ range_blocks_cnt+= ((MY_MAX(rows, 1) - 1) / avg_block_records + 1);
}
else
{
- if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
- rows= 1; /* there can be at most one row */
- else if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp,
- max_endp)))
+ page_range pages= unused_page_range;
+ if ((rows= this->records_in_range(keyno, min_endp, max_endp, &pages)) ==
+ HA_POS_ERROR)
{
/* Can't scan one range => can't do MRR scan at all */
total_rows= HA_POS_ERROR;
break;
}
+ if (pages.first_page == UNUSED_PAGE_NO)
+ {
+ /*
+ The engine does not provide info on the range position.
+ Place the range in a new block. Note that in this case
+ any new range will be placed in a new block.
+ */
+ ulonglong additional_blocks= ((MY_MAX(rows,1) - 1) / avg_block_records +
+ 1);
+ edge_blocks_cnt+= additional_blocks == 1 ? 1 : 2;
+ range_blocks_cnt+= additional_blocks;
+ }
+ else
+ {
+ /* The info on the range position is provided */
+ if (pages.first_page == prev_range_last_block)
+ {
+ /*
+ The new range starts in the same block that the last range
+ for which the position of the range was provided.
+ */
+ /*
+ First add records of single point ranges that can be placed
+ between these two ranges.
+ */
+ prev_range_last_block_records+= (single_point_ranges -
+ assigned_single_point_ranges);
+ assigned_single_point_ranges= single_point_ranges;
+ if (pages.first_page == pages.last_page)
+ {
+ /*
+ All records of the current range are in the same block
+ Note that the prev_range_last_block_records can be much larger
+ than max_records_in_block as the rows can be compressed!
+ */
+ prev_range_last_block_records+= rows;
+ DBUG_ASSERT(prev_range_last_block_records <
+ stats.block_size);
+ }
+ else
+ {
+ /*
+ The current range spans more than one block
+
+ Place part of the range records in 'prev_range_last_block'
+ and the remaining records in additional blocks.
+
+ We don't know where the first key was positioned in the
+ block, so we assume the range started in the middle of the
+ block.
+
+ Note that prev_range_last_block_records > avg_block_records
+ can be true in case of compressed rows.
+ */
+ ha_rows rem_rows= rows;
+
+ if (avg_block_records > prev_range_last_block_records)
+ {
+ ha_rows space_left_in_prev_block=
+ (avg_block_records - prev_range_last_block_records)/2;
+ rem_rows= 0;
+ if (rows > space_left_in_prev_block)
+ rem_rows= rows - space_left_in_prev_block;
+ }
+ /* Calculate how many additional blocks we need for rem_rows */
+ ulonglong additional_blocks= ((MY_MAX(rem_rows, 1) - 1) /
+ avg_block_records + 1);
+ edge_blocks_cnt++;
+ range_blocks_cnt+= additional_blocks;
+ prev_range_last_block= pages.last_page;
+ /* There is at least one row on last page */
+ prev_range_last_block_records= 1;
+ }
+ }
+ else
+ {
+ /*
+ The new range does not start in the same block that the last range
+ for which the position of the range was provided.
+ Note that rows may be 0!
+ */
+ ulonglong additional_blocks= ((MY_MAX(rows, 1) - 1) /
+ avg_block_records + 1);
+ edge_blocks_cnt+= additional_blocks == 1 ? 1 : 2;
+ range_blocks_cnt+= additional_blocks;
+ unassigned_single_point_ranges+= (single_point_ranges -
+ assigned_single_point_ranges);
+ assigned_single_point_ranges= single_point_ranges;
+ prev_range_last_block= pages.last_page;
+ /* There is at least one row on last page */
+ prev_range_last_block_records= 1;
+ }
+ }
}
- total_rows += rows;
- total_touched_blocks+= (rows / avg_block_records +1);
+ total_rows+= rows;
}
-
+ /*
+ Count the number of io_blocks that where not yet read and thus not cached.
+ The number of equal read blocks that where not read are:
+
+ (single_point_ranges - assigned_single_point_ranges).
+
+ We don't add these to io_blocks as we don't want to penalize equal
+ readss (if we did, a range that would read 5 rows would be
+ regarded as better than one equal read).
+
+ Better to assume we have done a records_in_range() for the equal
+ range and it's also cached.
+ */
+ io_blocks= (range_blocks_cnt - edge_blocks_cnt);
+ unassigned_single_point_ranges+= (single_point_ranges -
+ assigned_single_point_ranges);
+
if (total_rows != HA_POS_ERROR)
{
set_if_smaller(total_rows, max_rows);
+
/* The following calculation is the same as in multi_range_read_info(): */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
cost->reset();
- cost->avg_io_cost= 1; /* assume random seeks */
- cost->idx_avg_io_cost= 1;
- if (!((keyno == table->s->primary_key && primary_key_is_clustered()) ||
- is_clustering_key(keyno)))
+ cost->avg_io_cost= cost->idx_avg_io_cost= avg_io_cost();
+
+ if (!is_clustering_key(keyno))
{
- cost->idx_io_count= total_touched_blocks +
- keyread_time(keyno, 0, total_rows);
- cost->cpu_cost= cost->idx_cpu_cost=
- (double) total_rows / TIME_FOR_COMPARE_IDX +
- (2 * n_ranges - n_eq_ranges) * IDX_LOOKUP_COST;
+ cost->idx_io_count= (double) io_blocks;
+ cost->idx_cpu_cost= (keyread_time(keyno, 0, total_rows) +
+ n_ranges * IDX_LOOKUP_COST);
if (!(*flags & HA_MRR_INDEX_ONLY))
- {
- cost->io_count= read_time(keyno, 0, total_rows);
- cost->cpu_cost+= (double) total_rows / TIME_FOR_COMPARE;
- }
+ cost->cpu_cost= read_time(keyno, 0, total_rows);
}
else
{
- cost->io_count= read_time(keyno, n_ranges, (uint) total_rows);
- cost->cpu_cost= (double) total_rows / TIME_FOR_COMPARE + 0.01;
+ /*
+ Clustered index
+ If all index dives are to a few blocks, then limit the
+ ranges used by read_time to the number of dives.
+ */
+ io_blocks+= unassigned_single_point_ranges;
+ cost->idx_cpu_cost= n_ranges * IDX_LOOKUP_COST;
+ uint limited_ranges= (uint) MY_MIN((ulonglong) n_ranges, io_blocks);
+ cost->cpu_cost= read_time(keyno, limited_ranges, total_rows);
}
+ cost->cpu_cost+= (rows2double(total_rows) / TIME_FOR_COMPARE +
+ MULTI_RANGE_READ_SETUP_COST);
}
DBUG_PRINT("statistics",
("key: %s rows: %llu total_cost: %.3f io_blocks: %llu "
"idx_io_count: %.3f cpu_cost: %.3f io_count: %.3f",
table->s->keynames.type_names[keyno],
- (ulonglong) total_rows, cost->total_cost(),
- (ulonglong) total_touched_blocks,
+ (ulonglong) total_rows, cost->total_cost(), (ulonglong) io_blocks,
cost->idx_io_count, cost->cpu_cost, cost->io_count));
DBUG_RETURN(total_rows);
}
@@ -209,7 +371,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 key_parts, uint *bufsz,
uint *flags, Cost_estimate *cost)
{
/*
@@ -222,25 +384,26 @@ ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
*flags |= HA_MRR_USE_DEFAULT_IMPL;
cost->reset();
- cost->avg_io_cost= 1; /* assume random seeks */
-
/* Produce the same cost as non-MRR code does */
- if (!(keyno == table->s->primary_key && primary_key_is_clustered()))
+ if (!is_clustering_key(keyno))
{
- cost->idx_io_count= n_ranges + keyread_time(keyno, 0, n_rows);
- cost->cpu_cost= cost->idx_cpu_cost=
- (double) n_rows / TIME_FOR_COMPARE_IDX + n_ranges * IDX_LOOKUP_COST;
+ /*
+ idx_io_count could potentially be increased with the number of
+ index leaf blocks we have to read for finding n_rows.
+ */
+ cost->idx_io_count= n_ranges;
+ cost->idx_cpu_cost= (keyread_time(keyno, 0, n_rows) +
+ n_ranges * IDX_LOOKUP_COST);
if (!(*flags & HA_MRR_INDEX_ONLY))
{
- cost->io_count= read_time(keyno, 0, n_rows);
- cost->cpu_cost+= (double) n_rows / TIME_FOR_COMPARE;
+ cost->cpu_cost= read_time(keyno, 0, n_rows);
}
}
else
{
- cost->io_count= read_time(keyno, n_ranges, (uint)n_rows);
- cost->cpu_cost= (double) n_rows / TIME_FOR_COMPARE + 0.01;
+ cost->cpu_cost= read_time(keyno, n_ranges, (uint)n_rows);
}
+ cost->cpu_cost+= rows2double(n_rows) / TIME_FOR_COMPARE;
return 0;
}
@@ -939,7 +1102,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
h_idx= (primary_file->inited == handler::INDEX)? primary_file: secondary_file;
keyno= h_idx->active_index;
- if (!(keyno == table->s->primary_key && h_idx->primary_key_is_clustered()))
+ if (! h_idx->is_clustering_key(keyno))
{
strategy= disk_strategy= &reader_factory.ordered_rndpos_reader;
if (h_arg->pushed_rowid_filter)
@@ -976,11 +1139,10 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
if (strategy != index_strategy)
{
uint saved_pk_length=0;
- if (h_idx->primary_key_is_clustered())
+ uint pk= h_idx->get_table()->s->primary_key;
+ if (h_idx->pk_is_clustering_key(pk))
{
- uint pk= h_idx->get_table()->s->primary_key;
- if (pk != MAX_KEY)
- saved_pk_length= h_idx->get_table()->key_info[pk].key_length;
+ saved_pk_length= h_idx->get_table()->key_info[pk].key_length;
}
KEY *used_index= &h_idx->get_table()->key_info[h_idx->active_index];
@@ -1185,9 +1347,9 @@ int DsMrr_impl::setup_two_handlers()
We get here when the access alternates betwen MRR scan(s) and non-MRR
scans.
- Calling primary_file->index_end() will invoke dsmrr_close() for this object,
- which will delete secondary_file. We need to keep it, so put it away and dont
- let it be deleted:
+ Calling primary_file->index_end() will invoke dsmrr_close() for this
+ object, which will delete secondary_file. We need to keep it, so put it
+ away and don't let it be deleted:
*/
if (primary_file->inited == handler::INDEX)
{
@@ -1218,7 +1380,7 @@ void DsMrr_impl::close_second_handler()
{
secondary_file->extra(HA_EXTRA_NO_KEYREAD);
secondary_file->ha_index_or_rnd_end();
- secondary_file->ha_external_lock(current_thd, F_UNLCK);
+ secondary_file->ha_external_unlock(current_thd);
secondary_file->ha_close();
delete secondary_file;
secondary_file= NULL;
@@ -1621,8 +1783,7 @@ bool DsMrr_impl::check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno,
uint mrr_flags)
{
return MY_TEST((mrr_flags & HA_MRR_SINGLE_POINT) &&
- keyno == share->primary_key &&
- primary_file->primary_key_is_clustered() &&
+ primary_file->is_clustering_key(keyno) &&
optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS));
}
@@ -1660,8 +1821,7 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
TABLE_SHARE *share= primary_file->get_table_share();
bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags);
- bool using_cpk= MY_TEST(keyno == share->primary_key &&
- primary_file->primary_key_is_clustered());
+ bool using_cpk= primary_file->is_clustering_key(keyno);
*flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) ||
*flags & HA_MRR_INDEX_ONLY ||
@@ -1915,6 +2075,9 @@ void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, Cost_estimate *cost)
We define half_rotation_cost as DISK_SEEK_BASE_COST=0.9.
+ If handler::avg_io_cost() < 1.0, then we will trust the handler
+ when it comes to the average cost (this is for example true for HEAP).
+
@param table Table to be accessed
@param nrows Number of rows to retrieve
@param interrupted TRUE <=> Assume that the disk sweep will be
@@ -1928,12 +2091,12 @@ void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
DBUG_ENTER("get_sweep_read_cost");
cost->reset();
- if (table->file->primary_key_is_clustered())
+ if (table->file->pk_is_clustering_key(table->s->primary_key))
{
- cost->io_count= table->file->read_time(table->s->primary_key,
+ cost->cpu_cost= table->file->read_time(table->s->primary_key,
(uint) nrows, nrows);
}
- else
+ else if ((cost->avg_io_cost= table->file->avg_io_cost()) >= 0.999)
{
double n_blocks=
ceil(ulonglong2double(table->file->stats.data_file_length) / IO_SIZE);
@@ -1961,5 +2124,3 @@ void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
/* **************************************************************************
* DS-MRR implementation ends
***************************************************************************/
-
-
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index a7f6fc9e88d..2db08bf01e3 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -195,6 +195,16 @@ public:
return res;
}
longlong to_longlong(bool unsigned_flag) const;
+ /*
+ Return the value as a signed or unsigned longlong, depending on the sign.
+ - Positive values are returned as unsigned.
+ - Negative values are returned as signed.
+ This is used by bit SQL operators: | & ^ ~
+ as well as by the SQL function BIT_COUNT().
+ */
+ longlong to_xlonglong() const
+ { return to_longlong(!sign()); }
+
// Convert to string returning decimal2string() error code
int to_string_native(String *to, uint prec, uint dec, char filler,
uint mask= E_DEC_FATAL_ERROR) const;
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc
index f4cf8204d61..3234b8f9995 100644
--- a/sql/my_json_writer.cc
+++ b/sql/my_json_writer.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014 SkySQL Ab, MariaDB Corporation Ab
+/* Copyright (C) 2014, 2020, MariaDB Corporation.
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
@@ -180,7 +180,7 @@ void Json_writer::add_size(longlong val)
void Json_writer::add_double(double val)
{
char buf[64];
- size_t len= my_snprintf(buf, sizeof(buf), "%lg", val);
+ size_t len= my_snprintf(buf, sizeof(buf), "%-.11lg", val);
add_unquoted_str(buf, len);
}
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 47ce6171357..651eea33304 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -96,10 +96,10 @@ static struct my_option my_long_options[]=
static my_bool
-get_one_option(int optid, const struct my_option *, char *)
+get_one_option(const struct my_option *opt, char *, const char *)
{
DBUG_ENTER("get_one_option");
- switch (optid) {
+ switch (opt->id) {
case '?':
printf("%s\n", USAGETEXT);
my_print_help(my_long_options);
diff --git a/sql/mysql_upgrade_service.cc b/sql/mysql_upgrade_service.cc
index a10b5bb5162..60fd8fc7efa 100644
--- a/sql/mysql_upgrade_service.cc
+++ b/sql/mysql_upgrade_service.cc
@@ -78,12 +78,10 @@ static struct my_option my_long_options[]=
static my_bool
-get_one_option(int optid,
- const struct my_option *opt __attribute__ ((unused)),
- char *argument __attribute__ ((unused)))
+get_one_option(const struct my_option *opt, char *, const char *)
{
DBUG_ENTER("get_one_option");
- switch (optid) {
+ switch (opt->id) {
case '?':
printf("%s\n", USAGETEXT);
my_print_help(my_long_options);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index a74fb4326e4..9834d331474 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -33,6 +33,7 @@
#include "sql_table.h" // release_ddl_log, execute_ddl_log_recovery
#include "sql_connect.h" // free_max_user_conn, init_max_user_conn,
// handle_one_connection
+#include "thread_cache.h"
#include "sql_time.h" // known_date_time_formats,
// get_date_time_format_str,
// date_time_format_make
@@ -114,7 +115,7 @@
#include "sp_rcontext.h"
#include "sp_cache.h"
#include "sql_reload.h" // reload_acl_and_cache
-#include "pcre.h"
+#include "sp_head.h" // init_sp_psi_keys
#ifdef HAVE_POLL_H
#include <poll.h>
@@ -168,15 +169,6 @@ extern "C" { // Because of SCO 3.2V4.2
#include <crtdbg.h>
#endif
-#ifdef HAVE_SOLARIS_LARGE_PAGES
-#if defined(__sun__) && defined(__GNUC__) && defined(__cplusplus) \
- && defined(_XOPEN_SOURCE)
-extern int getpagesizes(size_t *, int);
-extern int getpagesizes2(size_t *, int);
-extern int memcntl(caddr_t, size_t, int, caddr_t, int, int);
-#endif /* __sun__ ... */
-#endif /* HAVE_SOLARIS_LARGE_PAGES */
-
#ifdef _AIX41
int initgroups(const char *,unsigned int);
#endif
@@ -323,10 +315,6 @@ static PSI_rwlock_key key_rwlock_openssl;
#endif
#endif /* HAVE_PSI_INTERFACE */
-#ifdef HAVE_NPTL
-volatile sig_atomic_t ld_assume_kernel_is_set= 0;
-#endif
-
/**
Statement instrumentation key for replication.
*/
@@ -336,15 +324,11 @@ PSI_statement_info stmt_info_rpl;
/* the default log output is log tables */
static bool lower_case_table_names_used= 0;
-static bool max_long_data_size_used= false;
static bool volatile select_thread_in_use, signal_thread_in_use;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0, opt_silent_startup= 0;
-uint kill_cached_threads;
-static uint wake_thread;
ulong max_used_connections;
-volatile ulong cached_thread_count= 0;
static char *mysqld_user, *mysqld_chroot;
static char *default_character_set_name;
static char *character_set_filesystem_name;
@@ -358,11 +342,9 @@ char *enforced_storage_engine=NULL;
char *gtid_pos_auto_engines;
plugin_ref *opt_gtid_pos_auto_plugins;
static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
-static I_List<CONNECT> thread_cache;
+Thread_cache thread_cache;
static bool binlog_format_used= false;
LEX_STRING opt_init_connect, opt_init_slave;
-mysql_cond_t COND_thread_cache;
-static mysql_cond_t COND_flush_thread_cache;
mysql_cond_t COND_slave_background;
static DYNAMIC_ARRAY all_options;
static longlong start_memory_used;
@@ -424,6 +406,7 @@ my_bool use_temp_pool, relay_log_purge;
my_bool relay_log_recovery;
my_bool opt_sync_frm, opt_allow_suspicious_udfs;
my_bool opt_secure_auth= 0;
+my_bool opt_require_secure_transport= 0;
char* opt_secure_file_priv;
my_bool lower_case_file_system= 0;
my_bool opt_large_pages= 0;
@@ -446,6 +429,7 @@ my_bool opt_noacl;
my_bool sp_automatic_privileges= 1;
ulong opt_binlog_rows_event_max_size;
+ulong binlog_row_metadata;
my_bool opt_master_verify_checksum= 0;
my_bool opt_slave_sql_verify_checksum= 1;
const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS};
@@ -461,7 +445,7 @@ Atomic_counter<uint32_t> thread_count;
bool shutdown_wait_for_slaves;
Atomic_counter<uint32_t> slave_open_temp_tables;
ulong thread_created;
-ulong back_log, connect_timeout, concurrency, server_id;
+ulong back_log, connect_timeout, server_id;
ulong what_to_log;
ulong slow_launch_time;
ulong open_files_limit, max_binlog_size;
@@ -507,12 +491,6 @@ long opt_secure_timestamp;
uint default_password_lifetime;
my_bool disconnect_on_expired_password;
-/*
- Maximum length of parameter value which can be set through
- mysql_send_long_data() call.
-*/
-ulong max_long_data_size;
-
bool max_user_connections_checking=0;
/**
Limit of the total number of prepared statements in the server.
@@ -544,7 +522,7 @@ ulong stored_program_cache_size= 0;
ulong opt_slave_parallel_threads= 0;
ulong opt_slave_domain_parallel_threads= 0;
-ulong opt_slave_parallel_mode= SLAVE_PARALLEL_CONSERVATIVE;
+ulong opt_slave_parallel_mode;
ulong opt_binlog_commit_wait_count= 0;
ulong opt_binlog_commit_wait_usec= 0;
ulong opt_slave_parallel_max_queued= 131072;
@@ -606,7 +584,10 @@ DATE_TIME_FORMAT global_date_format, global_datetime_format, global_time_format;
Time_zone *default_tz;
const char *mysql_real_data_home_ptr= mysql_real_data_home;
-char server_version[SERVER_VERSION_LENGTH], *server_version_ptr;
+extern "C" {
+char server_version[SERVER_VERSION_LENGTH];
+}
+char *server_version_ptr;
bool using_custom_server_version= false;
char *mysqld_unix_port, *opt_mysql_tmpdir;
ulong thread_handling;
@@ -681,7 +662,10 @@ static std::atomic<char*> shutdown_user;
/* Thread specific variables */
-pthread_key(THD*, THR_THD);
+static thread_local THD *THR_THD;
+
+MYSQL_THD _current_thd() { return THR_THD; }
+void set_current_thd(THD *thd) { THR_THD= thd; }
/*
LOCK_start_thread is used to syncronize thread start and stop with
@@ -694,14 +678,13 @@ pthread_key(THD*, THR_THD);
*/
mysql_mutex_t LOCK_start_thread;
-mysql_mutex_t LOCK_thread_cache;
mysql_mutex_t
LOCK_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_connection_count, LOCK_error_messages, LOCK_slave_background;
+ LOCK_error_messages, LOCK_slave_background;
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
LOCK_global_table_stats, LOCK_global_index_stats;
@@ -796,8 +779,8 @@ static struct my_option pfs_early_options[]=
{"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},
+ &pfs_param.m_consumer_events_statements_current_enabled, 0, GET_BOOL,
+ OPT_ARG, FALSE, 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,
@@ -808,6 +791,21 @@ static struct my_option pfs_early_options[]=
&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_transactions_current", 0,
+ "Default startup value for the events_transactions_current consumer.",
+ &pfs_param.m_consumer_events_transactions_current_enabled,
+ &pfs_param.m_consumer_events_transactions_current_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_transactions_history", 0,
+ "Default startup value for the events_transactions_history consumer.",
+ &pfs_param.m_consumer_events_transactions_history_enabled,
+ &pfs_param.m_consumer_events_transactions_history_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_transactions_history_long", 0,
+ "Default startup value for the events_transactions_history_long consumer.",
+ &pfs_param.m_consumer_events_transactions_history_long_enabled,
+ &pfs_param.m_consumer_events_transactions_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,
@@ -845,6 +843,19 @@ static struct my_option pfs_early_options[]=
NO_ARG, 1, 0, 1, 0, 0, 0}
};
+PSI_file_key key_file_binlog, key_file_binlog_cache, key_file_binlog_index,
+ key_file_binlog_index_cache, 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,
+ key_file_loadfile, key_file_log_event_data, key_file_log_event_info,
+ key_file_master_info, key_file_misc, key_file_partition_ddl_log,
+ key_file_pid, key_file_relay_log_info, key_file_send_file, key_file_tclog,
+ 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,
+ key_file_relaylog_cache, key_file_relaylog_index_cache;
+PSI_file_key key_file_binlog_state;
+
#ifdef HAVE_PSI_INTERFACE
#ifdef HAVE_MMAP
PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active, key_LOCK_pool,
@@ -859,7 +870,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_BINLOG_LOCK_binlog_background_thread,
key_LOCK_binlog_end_pos,
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_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,
@@ -876,7 +887,6 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
key_LOCK_error_messages,
key_LOCK_start_thread,
- key_LOCK_thread_cache,
key_PARTITION_LOCK_auto_inc;
PSI_mutex_key key_RELAYLOG_LOCK_index;
PSI_mutex_key key_LOCK_relaylog_end_pos;
@@ -924,7 +934,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_delayed_insert_mutex, "Delayed_insert::mutex", 0},
{ &key_hash_filo_lock, "hash_filo::lock", 0},
{ &key_LOCK_active_mi, "LOCK_active_mi", PSI_FLAG_GLOBAL},
- { &key_LOCK_connection_count, "LOCK_connection_count", PSI_FLAG_GLOBAL},
{ &key_LOCK_thread_id, "LOCK_thread_id", PSI_FLAG_GLOBAL},
{ &key_LOCK_crypt, "LOCK_crypt", PSI_FLAG_GLOBAL},
{ &key_LOCK_delayed_create, "LOCK_delayed_create", PSI_FLAG_GLOBAL},
@@ -969,7 +978,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_after_binlog_sync, "LOCK_after_binlog_sync", PSI_FLAG_GLOBAL},
{ &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
{ &key_LOCK_slave_background, "LOCK_slave_background", 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_start_thread, "LOCK_start_thread", PSI_FLAG_GLOBAL},
@@ -1028,7 +1036,6 @@ PSI_cond_key key_BINLOG_COND_xid_list,
key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
- key_COND_thread_cache, key_COND_flush_thread_cache,
key_COND_start_thread, key_COND_binlog_send,
key_BINLOG_COND_queue_busy;
PSI_cond_key key_RELAYLOG_COND_relay_log_updated,
@@ -1078,8 +1085,6 @@ static PSI_cond_info all_server_conds[]=
{ &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_cache, "COND_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},
@@ -1118,17 +1123,6 @@ static PSI_thread_info all_server_threads[]=
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,
- key_file_loadfile, key_file_log_event_data, key_file_log_event_info,
- key_file_master_info, key_file_misc, key_file_partition,
- key_file_pid, key_file_relay_log_info, key_file_send_file, key_file_tclog,
- 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;
-
#endif /* HAVE_PSI_INTERFACE */
#ifdef HAVE_PSI_STATEMENT_INTERFACE
@@ -1184,9 +1178,9 @@ void net_after_header_psi(struct st_net *net, void *user_data,
thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
stmt_info_new_packet.m_key,
thd->get_db(), thd->db.length,
- thd->charset());
+ thd->charset(), NULL);
- THD_STAGE_INFO(thd, stage_init);
+ THD_STAGE_INFO(thd, stage_starting);
}
/*
@@ -1312,7 +1306,7 @@ private:
void Buffered_logs::init()
{
- init_alloc_root(&m_root, "Buffered_logs", 1024, 0, MYF(0));
+ init_alloc_root(PSI_NOT_INSTRUMENTED, &m_root, 1024, 0, MYF(0));
}
void Buffered_logs::cleanup()
@@ -1475,10 +1469,10 @@ struct st_VioSSLFd *ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */
/**
- Number of currently active user connections. The variable is protected by
- LOCK_connection_count.
+ Number of currently active user connections.
*/
-uint connection_count= 0, extra_connection_count= 0;
+Atomic_counter<uint> connection_count;
+static Atomic_counter<uint> extra_connection_count;
my_bool opt_gtid_strict_mode= FALSE;
@@ -1490,7 +1484,7 @@ 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 *);
+extern "C" my_bool mysqld_get_one_option(const struct my_option *, char *, const char *);
static int init_thread_environment();
static char *get_relative_path(const char *path);
static int fix_paths(void);
@@ -1692,7 +1686,7 @@ void kill_mysql(THD *thd)
make_user_name(thd, user_host_buff);
- if ((user= my_strdup(user_host_buff, MYF(0))) &&
+ if ((user= my_strdup(PSI_NOT_INSTRUMENTED, user_host_buff, MYF(0))) &&
!shutdown_user.compare_exchange_strong(expected_shutdown_user,
user,
std::memory_order_relaxed,
@@ -1724,8 +1718,7 @@ static void close_connections(void)
DBUG_ENTER("close_connections");
/* Clear thread cache */
- kill_cached_threads++;
- flush_thread_cache();
+ thread_cache.final_flush();
/* Abort listening to new connections */
DBUG_PRINT("quit",("Closing sockets"));
@@ -1875,7 +1868,7 @@ extern "C" void unireg_abort(int exit_code)
if (opt_help)
usage();
- if (exit_code)
+ else if (exit_code)
sql_print_error("Aborting");
/* Don't write more notes to the log to not hide error message */
disable_log_notes= 1;
@@ -1913,13 +1906,6 @@ extern "C" void unireg_abort(int exit_code)
}
-static void cleanup_tls()
-{
- if (THR_THD)
- (void)pthread_key_delete(THR_THD);
-}
-
-
static void mysqld_exit(int exit_code)
{
DBUG_ENTER("mysqld_exit");
@@ -1948,7 +1934,6 @@ static void mysqld_exit(int exit_code)
if (exit_code == 0)
SAFEMALLOC_REPORT_MEMORY(0);
}
- cleanup_tls();
DBUG_LEAVE;
sd_notify(0, "STATUS=MariaDB server is down");
exit(exit_code); /* purecov: inspected */
@@ -2008,9 +1993,7 @@ static void clean_up(bool print_message)
sp_cache_end();
free_status_vars();
end_thr_alarm(1); /* Free allocated memory */
-#ifndef EMBEDDED_LIBRARY
end_thr_timer();
-#endif
my_free_open_file_info();
if (defaults_argv)
free_defaults(defaults_argv);
@@ -2095,8 +2078,8 @@ static void clean_up_mutexes()
{
DBUG_ENTER("clean_up_mutexes");
server_threads.destroy();
+ thread_cache.destroy();
mysql_rwlock_destroy(&LOCK_grant);
- mysql_mutex_destroy(&LOCK_thread_cache);
mysql_mutex_destroy(&LOCK_start_thread);
mysql_mutex_destroy(&LOCK_status);
mysql_rwlock_destroy(&LOCK_all_status_vars);
@@ -2105,7 +2088,6 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_delayed_create);
mysql_mutex_destroy(&LOCK_crypt);
mysql_mutex_destroy(&LOCK_user_conn);
- mysql_mutex_destroy(&LOCK_connection_count);
mysql_mutex_destroy(&LOCK_thread_id);
mysql_mutex_destroy(&LOCK_stats);
mysql_mutex_destroy(&LOCK_global_user_client_stats);
@@ -2131,9 +2113,7 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_short_uuid_generator);
mysql_mutex_destroy(&LOCK_prepared_stmt_count);
mysql_mutex_destroy(&LOCK_error_messages);
- mysql_cond_destroy(&COND_thread_cache);
mysql_cond_destroy(&COND_start_thread);
- mysql_cond_destroy(&COND_flush_thread_cache);
mysql_mutex_destroy(&LOCK_server_started);
mysql_cond_destroy(&COND_server_started);
mysql_mutex_destroy(&LOCK_prepare_ordered);
@@ -2543,6 +2523,11 @@ static void network_init(void)
#endif
}
#endif
+
+#ifdef _WIN32
+ network_init_win();
+#endif
+
DBUG_PRINT("info",("server started"));
DBUG_VOID_RETURN;
}
@@ -2565,7 +2550,7 @@ void close_connection(THD *thd, uint sql_errno)
if (sql_errno)
{
- net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL);
+ thd->protocol->net_send_error(thd, sql_errno, ER_DEFAULT(sql_errno), NULL);
thd->print_aborted_warning(lvl, ER_DEFAULT(sql_errno));
}
else
@@ -2599,20 +2584,6 @@ extern "C" sig_handler end_mysqld_signal(int sig __attribute__((unused)))
}
#endif /* EMBEDDED_LIBRARY */
-/*
- Decrease number of connections
-
- SYNOPSIS
- dec_connection_count()
-*/
-
-void dec_connection_count(scheduler_functions *scheduler)
-{
- mysql_mutex_lock(&LOCK_connection_count);
- (*scheduler->connection_count)--;
- mysql_mutex_unlock(&LOCK_connection_count);
-}
-
/*
Unlink thd from global list of available connections
@@ -2638,7 +2609,7 @@ void unlink_thd(THD *thd)
*/
if (!thd->wsrep_applier)
#endif /* WITH_WSREP */
- dec_connection_count(thd->scheduler);
+ --*thd->scheduler->connection_count;
thd->free_connection();
@@ -2646,170 +2617,6 @@ void unlink_thd(THD *thd)
}
-/*
- Store thread in cache for reuse by new connections
-
- SYNOPSIS
- cache_thread()
- thd Thread handler
-
- NOTES
- LOCK_thread_cache is used to protect the cache variables
-
- RETURN
- 0 Thread was not put in cache
- 1 Thread is to be reused by new connection.
- (ie, caller should return, not abort with pthread_exit())
-*/
-
-
-static bool cache_thread(THD *thd)
-{
- struct timespec abstime;
- DBUG_ENTER("cache_thread");
- DBUG_ASSERT(thd);
-
- mysql_mutex_lock(&LOCK_thread_cache);
- if (cached_thread_count < thread_cache_size &&
- ! abort_loop && !kill_cached_threads)
- {
- /* Don't kill the thread, just put it in cache for reuse */
- DBUG_PRINT("info", ("Adding thread to cache"));
- cached_thread_count++;
-
- /*
- Delete the instrumentation for the job that just completed,
- before parking this pthread in the cache (blocked on COND_thread_cache).
- */
- PSI_CALL_delete_current_thread();
-
-#ifndef DBUG_OFF
- while (_db_is_pushed_())
- _db_pop_();
-#endif
-
- set_timespec(abstime, THREAD_CACHE_TIMEOUT);
- while (!abort_loop && ! wake_thread && ! kill_cached_threads)
- {
- int error= mysql_cond_timedwait(&COND_thread_cache, &LOCK_thread_cache,
- &abstime);
- if (error == ETIMEDOUT || error == ETIME)
- {
- /*
- If timeout, end thread.
- If a new thread is requested (wake_thread is set), we will handle
- the call, even if we got a timeout (as we are already awake and free)
- */
- break;
- }
- }
- cached_thread_count--;
- if (kill_cached_threads)
- mysql_cond_signal(&COND_flush_thread_cache);
- if (wake_thread)
- {
- CONNECT *connect;
-
- wake_thread--;
- connect= thread_cache.get();
- mysql_mutex_unlock(&LOCK_thread_cache);
-
- if (!(connect->create_thd(thd)))
- {
- /* Out of resources. Free thread to get more resources */
- connect->close_and_delete();
- DBUG_RETURN(0);
- }
- delete connect;
-
- /*
- We have to call store_globals to update mysys_var->id and lock_info
- with the new thread_id
- */
- thd->store_globals();
-
- /*
- Create new instrumentation for the new THD job,
- and attach it to this running pthread.
- */
- PSI_CALL_set_thread(PSI_CALL_new_thread(key_thread_one_connection,
- thd, thd->thread_id));
-
- /* reset abort flag for the thread */
- thd->mysys_var->abort= 0;
- thd->thr_create_utime= microsecond_interval_timer();
- thd->start_utime= thd->thr_create_utime;
-
- server_threads.insert(thd);
- DBUG_RETURN(1);
- }
- }
- mysql_mutex_unlock(&LOCK_thread_cache);
- DBUG_RETURN(0);
-}
-
-
-/*
- End thread for the current connection
-
- SYNOPSIS
- one_thread_per_connection_end()
- thd Thread handler. This may be null if we run out of resources.
- put_in_cache Store thread in cache, if there is room in it
- Normally this is true in all cases except when we got
- out of resources initializing the current thread
-
- NOTES
- If thread is cached, we will wait until thread is scheduled to be
- reused and then we will return.
- If thread is not cached, we end the thread.
-
- RETURN
- 0 Signal to handle_one_connection to reuse connection
-*/
-
-bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
-{
- DBUG_ENTER("one_thread_per_connection_end");
-
- if (thd)
- {
- const bool wsrep_applier= IF_WSREP(thd->wsrep_applier, false);
-
- unlink_thd(thd);
- if (!wsrep_applier && put_in_cache && cache_thread(thd))
- DBUG_RETURN(0); // Thread is reused
- delete thd;
- }
-
- DBUG_PRINT("info", ("killing thread"));
- DBUG_LEAVE; // Must match DBUG_ENTER()
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- ERR_remove_state(0);
-#endif
- my_thread_end();
-
- pthread_exit(0);
- return 0; // Avoid compiler warnings
-}
-
-
-void flush_thread_cache()
-{
- 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_cache);
- }
- kill_cached_threads--;
- mysql_mutex_unlock(&LOCK_thread_cache);
- DBUG_VOID_RETURN;
-}
-
-
/******************************************************************************
Setup a signal thread with handles all signals.
Because Linux doesn't support schemas use a mutex to check that
@@ -2981,75 +2788,6 @@ extern "C" char *my_demangle(const char *mangled_name, int *status)
#endif
-/*
- pthread_attr_setstacksize() without so much platform-dependency
-
- Return: The actual stack size if possible.
-*/
-
-#ifndef EMBEDDED_LIBRARY
-static size_t my_setstacksize(pthread_attr_t *attr, size_t stacksize)
-{
- size_t guard_size __attribute__((unused))= 0;
-
-#if defined(__ia64__) || defined(__ia64)
- /*
- On IA64, half of the requested stack size is used for "normal stack"
- and half for "register stack". The space measured by check_stack_overrun
- is the "normal stack", so double the request to make sure we have the
- caller-expected amount of normal stack.
-
- NOTE: there is no guarantee that the register stack can't grow faster
- than normal stack, so it's very unclear that we won't dump core due to
- stack overrun despite check_stack_overrun's efforts. Experimentation
- shows that in the execution_constants test, the register stack grows
- less than half as fast as normal stack, but perhaps other scenarios are
- less forgiving. If it turns out that more space is needed for the
- register stack, that could be forced (rather inefficiently) by using a
- multiplier higher than 2 here.
- */
- stacksize *= 2;
-#endif
-
- /*
- On many machines, the "guard space" is subtracted from the requested
- stack size, and that space is quite large on some platforms. So add
- it to our request, if we can find out what it is.
- */
-#ifdef HAVE_PTHREAD_ATTR_GETGUARDSIZE
- if (pthread_attr_getguardsize(attr, &guard_size))
- guard_size = 0; /* if can't find it out, treat as 0 */
-#endif
-
- pthread_attr_setstacksize(attr, stacksize + guard_size);
-
- /* Retrieve actual stack size if possible */
-#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE
- {
- size_t real_stack_size= 0;
- /* We must ignore real_stack_size = 0 as Solaris 2.9 can return 0 here */
- if (pthread_attr_getstacksize(attr, &real_stack_size) == 0 &&
- real_stack_size > guard_size)
- {
- real_stack_size -= guard_size;
- if (real_stack_size < stacksize)
- {
- if (global_system_variables.log_warnings)
- sql_print_warning("Asked for %zu thread stack, but got %zu",
- stacksize, real_stack_size);
- stacksize= real_stack_size;
- }
- }
- }
-#endif /* !EMBEDDED_LIBRARY */
-
-#if defined(__ia64__) || defined(__ia64)
- stacksize /= 2;
-#endif
- return stacksize;
-}
-#endif
-
#ifdef DBUG_ASSERT_AS_PRINTF
extern "C" void
mariadb_dbug_assert_failed(const char *assert_expr, const char *file,
@@ -3130,8 +2868,7 @@ void init_signals(void)
sa.sa_flags = 0;
sa.sa_handler = print_signal_warning;
sigaction(SIGHUP, &sa, (struct sigaction*) 0);
- if (thd_lib_detected != THD_LIB_LT)
- sigaddset(&set,THR_SERVER_ALARM);
+ sigaddset(&set,THR_SERVER_ALARM);
if (test_flags & TEST_SIGINT)
{
/* Allow SIGINT to break mysqld. This is for debugging with --gdb */
@@ -3388,10 +3125,18 @@ extern "C" void *my_str_malloc_mysqld(size_t size);
void *my_str_malloc_mysqld(size_t size)
{
- return my_malloc(size, MYF(MY_FAE));
+ return my_malloc(key_memory_my_str_malloc, size, MYF(MY_FAE));
}
+#if 0
+extern "C" void *my_str_realloc_mysqld(void *ptr, size_t size);
+void *my_str_realloc_mysqld(void *ptr, size_t size)
+{
+ return my_realloc(key_memory_my_str_malloc, ptr, size, MYF(MY_FAE));
+}
+#endif
+
#include <mysqld_default_groups.h>
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
@@ -3437,20 +3182,6 @@ static void init_libstrings()
#endif
}
-ulonglong my_pcre_frame_size;
-
-static void init_pcre()
-{
- pcre_malloc= pcre_stack_malloc= my_str_malloc_mysqld;
- pcre_free= pcre_stack_free= my_free;
- pcre_stack_guard= check_enough_stack_size_slow;
- /* See http://pcre.org/original/doc/html/pcrestack.html */
- my_pcre_frame_size= -pcre_exec(NULL, NULL, NULL, -999, -999, 0, NULL, 0);
- // pcre can underestimate its stack usage. Use a safe value, as in the manual
- set_if_bigger(my_pcre_frame_size, 500);
- my_pcre_frame_size += 16; // Again, safety margin, see the manual
-}
-
/**
Initialize one of the global date/time format variables.
@@ -3614,7 +3345,7 @@ SHOW_VAR com_status_vars[]= {
{"show_generic", STMT_STATUS(SQLCOM_SHOW_GENERIC)},
{"show_grants", STMT_STATUS(SQLCOM_SHOW_GRANTS)},
{"show_keys", STMT_STATUS(SQLCOM_SHOW_KEYS)},
- {"show_master_status", STMT_STATUS(SQLCOM_SHOW_MASTER_STAT)},
+ {"show_binlog_status", STMT_STATUS(SQLCOM_SHOW_BINLOG_STAT)},
{"show_open_tables", STMT_STATUS(SQLCOM_SHOW_OPEN_TABLES)},
{"show_package_status", STMT_STATUS(SQLCOM_SHOW_STATUS_PACKAGE)},
#ifndef DBUG_OFF
@@ -3828,10 +3559,8 @@ int json_unescape_json(const char *json_str, const char *json_end,
@returns Pointer to string containing the full file path, or NULL if
it was not possible to create the path.
*/
-static inline const char *
-rpl_make_log_name(const char *opt,
- const char *def,
- const char *ext)
+static const char *rpl_make_log_name(PSI_memory_key key, const char *opt,
+ const char *def, const char *ext)
{
DBUG_ENTER("rpl_make_log_name");
DBUG_PRINT("enter", ("opt: %s, def: %s, ext: %s", opt ? opt : "(null)",
@@ -3850,7 +3579,7 @@ rpl_make_log_name(const char *opt,
mysql_real_data_home_ptr= mysql_real_data_home;
if (fn_format(buff, base, mysql_real_data_home_ptr, ext, options))
- DBUG_RETURN(my_strdup(buff, MYF(MY_WME)));
+ DBUG_RETURN(my_strdup(key, buff, MYF(MY_WME)));
else
DBUG_RETURN(NULL);
}
@@ -3859,11 +3588,6 @@ rpl_make_log_name(const char *opt,
static int init_early_variables()
{
- if (pthread_key_create(&THR_THD, NULL))
- {
- fprintf(stderr, "Fatal error: Can't create thread-keys\n");
- return 1;
- }
set_current_thd(0);
set_malloc_size_cb(my_malloc_size_cb_func);
global_status_var.global_memory_used= 0;
@@ -3986,7 +3710,9 @@ static int init_common_variables()
key_BINLOG_COND_relay_log_updated,
key_BINLOG_COND_bin_log_updated,
key_file_binlog,
+ key_file_binlog_cache,
key_file_binlog_index,
+ key_file_binlog_index_cache,
key_BINLOG_COND_queue_busy,
key_LOCK_binlog_end_pos);
#endif
@@ -4115,73 +3841,15 @@ static int init_common_variables()
DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname,
server_version, SYSTEM_TYPE,MACHINE_TYPE));
-#ifdef HAVE_LINUX_LARGE_PAGES
/* Initialize large page size */
if (opt_large_pages)
{
- SYSVAR_AUTOSIZE(opt_large_page_size, my_get_large_page_size());
- if (opt_large_page_size)
+ DBUG_PRINT("info", ("Large page set"));
+ if (my_init_large_pages(opt_super_large_pages))
{
- DBUG_PRINT("info", ("Large page set, large_page_size = %d",
- opt_large_page_size));
- my_use_large_pages= 1;
- my_large_page_size= opt_large_page_size;
+ return 1;
}
- else
- SYSVAR_AUTOSIZE(opt_large_pages, 0);
}
-#endif /* HAVE_LINUX_LARGE_PAGES */
-#ifdef HAVE_SOLARIS_LARGE_PAGES
-#define LARGE_PAGESIZE (4*1024*1024) /* 4MB */
-#define SUPER_LARGE_PAGESIZE (256*1024*1024) /* 256MB */
- if (opt_large_pages)
- {
- /*
- tell the kernel that we want to use 4/256MB page for heap storage
- and also for the stack. We use 4 MByte as default and if the
- super-large-page is set we increase it to 256 MByte. 256 MByte
- is for server installations with GBytes of RAM memory where
- the MySQL Server will have page caches and other memory regions
- measured in a number of GBytes.
- We use as big pages as possible which isn't bigger than the above
- desired page sizes.
- */
- int nelem;
- size_t max_desired_page_size;
- if (opt_super_large_pages)
- max_desired_page_size= SUPER_LARGE_PAGESIZE;
- else
- max_desired_page_size= LARGE_PAGESIZE;
- nelem = getpagesizes(NULL, 0);
- if (nelem > 0)
- {
- size_t *pagesize = (size_t *) malloc(sizeof(size_t) * nelem);
- if (pagesize != NULL && getpagesizes(pagesize, nelem) > 0)
- {
- size_t max_page_size= 0;
- for (int i= 0; i < nelem; i++)
- {
- if (pagesize[i] > max_page_size &&
- pagesize[i] <= max_desired_page_size)
- max_page_size= pagesize[i];
- }
- free(pagesize);
- if (max_page_size > 0)
- {
- struct memcntl_mha mpss;
-
- mpss.mha_cmd= MHA_MAPSIZE_BSSBRK;
- mpss.mha_pagesize= max_page_size;
- mpss.mha_flags= 0;
- memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mpss, 0, 0);
- mpss.mha_cmd= MHA_MAPSIZE_STACK;
- memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mpss, 0, 0);
- }
- }
- }
- }
-#endif /* HAVE_SOLARIS_LARGE_PAGES */
-
#if defined(HAVE_POOL_OF_THREADS)
if (IS_SYSVAR_AUTOSIZE(&threadpool_size))
@@ -4316,7 +3984,6 @@ static int init_common_variables()
if (item_create_init())
return 1;
item_init();
- init_pcre();
/*
Process a comma-separated character set list and choose
the first available character set. This is mostly for
@@ -4514,7 +4181,6 @@ static int init_thread_environment()
{
DBUG_ENTER("init_thread_environment");
server_threads.init();
- mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_start_thread, &LOCK_start_thread, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_delayed_insert,
@@ -4537,8 +4203,6 @@ static int init_thread_environment()
&LOCK_error_messages, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_uuid_short_generator,
&LOCK_short_uuid_generator, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_LOCK_connection_count,
- &LOCK_connection_count, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_thread_id,
&LOCK_thread_id, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_stats, &LOCK_stats, MY_MUTEX_INIT_FAST);
@@ -4578,9 +4242,7 @@ static int init_thread_environment()
mysql_rwlock_init(key_rwlock_LOCK_ssl_refresh, &LOCK_ssl_refresh);
mysql_rwlock_init(key_rwlock_LOCK_grant, &LOCK_grant);
mysql_rwlock_init(key_rwlock_LOCK_all_status_vars, &LOCK_all_status_vars);
- mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
mysql_cond_init(key_COND_start_thread, &COND_start_thread, NULL);
- 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);
#endif
@@ -4725,6 +4387,21 @@ void ssl_acceptor_stats_update(int sslaccept_ret)
static void init_ssl()
{
+/*
+ Not need to check require_secure_transport on the Linux,
+ because it always has Unix domain sockets that are secure:
+*/
+#ifdef _WIN32
+ if (opt_require_secure_transport &&
+ !opt_use_ssl &&
+ !opt_enable_named_pipe &&
+ !opt_bootstrap)
+ {
+ sql_print_error("Server is started with --require-secure-transport=ON "
+ "but no secure transport (SSL or PIPE) are configured.");
+ unireg_abort(1);
+ }
+#endif
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
if (opt_use_ssl)
{
@@ -4898,6 +4575,22 @@ init_gtid_pos_auto_engines(void)
return 0;
}
+#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 }
+
+#define MARIADB_REMOVED_OPTION(option) \
+ { option, OPT_REMOVED_OPTION, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
static int init_server_components()
{
DBUG_ENTER("init_server_components");
@@ -4927,13 +4620,11 @@ static int init_server_components()
init_thr_lock();
backup_init();
-#ifndef EMBEDDED_LIBRARY
if (init_thr_timer(thread_scheduler->max_threads + extra_max_connections))
{
fprintf(stderr, "Can't initialize timers\n");
unireg_abort(1);
}
-#endif
my_uuid_init((ulong) (my_rnd(&sql_rand))*12345,12345);
wt_init();
@@ -5167,10 +4858,12 @@ static int init_server_components()
}
log_bin_basename=
- rpl_make_log_name(opt_bin_logname, pidfile_name,
+ rpl_make_log_name(key_memory_MYSQL_BIN_LOG_basename,
+ opt_bin_logname, pidfile_name,
opt_bin_logname ? "" : "-bin");
log_bin_index=
- rpl_make_log_name(opt_binlog_index_name, log_bin_basename, ".index");
+ rpl_make_log_name(key_memory_MYSQL_BIN_LOG_index,
+ opt_binlog_index_name, log_bin_basename, ".index");
if (log_bin_basename == NULL || log_bin_index == NULL)
{
sql_print_error("Unable to create replication path names:"
@@ -5188,10 +4881,12 @@ static int init_server_components()
if (opt_relay_logname)
{
relay_log_basename=
- rpl_make_log_name(opt_relay_logname, pidfile_name,
+ rpl_make_log_name(key_memory_MYSQL_RELAY_LOG_basename,
+ opt_relay_logname, pidfile_name,
opt_relay_logname ? "" : "-relay-bin");
relay_log_index=
- rpl_make_log_name(opt_relaylog_index_name, relay_log_basename, ".index");
+ rpl_make_log_name(key_memory_MYSQL_RELAY_LOG_index,
+ opt_relaylog_index_name, relay_log_basename, ".index");
if (relay_log_basename == NULL || relay_log_index == NULL)
{
sql_print_error("Unable to create replication path names:"
@@ -5246,8 +4941,56 @@ static int init_server_components()
if (remaining_argc > 1)
{
int ho_error;
- struct my_option no_opts[]=
+ struct my_option removed_opts[]=
{
+ /* The following options exist in 5.6 but not in 10.0 */
+ 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_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-features"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-offset"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-limit"), // OPTIMIZER_TRACE
+ MYSQL_COMPATIBILITY_OPTION("server-id-bits"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-rows-search-algorithms"), // HAVE_REPLICATION
+ 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-pending-jobs-size-max", "--slave-parallel-max-queued"), // HAVE_REPLICATION
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-public-key-path"), // HAVE_OPENSSL
+
+ /* 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"),
+ MYSQL_COMPATIBILITY_OPTION("show_compatibility_56"),
+
+ /* The following options were removed in 10.5 */
+#if defined(__linux__)
+ MARIADB_REMOVED_OPTION("super-large-pages"),
+#endif
+ MARIADB_REMOVED_OPTION("innodb-locks-unsafe-for-binlog"),
+ MARIADB_REMOVED_OPTION("innodb-rollback-segments"),
+ MARIADB_REMOVED_OPTION("innodb-stats-sample-pages"),
+ MARIADB_REMOVED_OPTION("max-long-data-size"),
+ MARIADB_REMOVED_OPTION("multi-range-count"),
+ MARIADB_REMOVED_OPTION("skip-bdb"),
+ MARIADB_REMOVED_OPTION("thread-concurrency"),
+ MARIADB_REMOVED_OPTION("timed-mutexes"),
+
+ /* 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
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
/*
@@ -5256,7 +4999,7 @@ static int init_server_components()
*/
my_getopt_skip_unknown= 0;
- if ((ho_error= handle_options(&remaining_argc, &remaining_argv, no_opts,
+ if ((ho_error= handle_options(&remaining_argc, &remaining_argv, removed_opts,
mysqld_get_one_option)))
unireg_abort(ho_error);
/* Add back the program name handle_options removes */
@@ -5389,7 +5132,7 @@ static int init_server_components()
int error;
mysql_mutex_t *log_lock= mysql_bin_log.get_log_lock();
mysql_mutex_lock(log_lock);
- error= mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, 0,
+ error= mysql_bin_log.open(opt_bin_logname, 0, 0,
WRITE_CACHE, max_binlog_size, 0, TRUE);
mysql_mutex_unlock(log_lock);
if (unlikely(error))
@@ -5481,13 +5224,13 @@ static void test_lc_time_sz()
for (const char **month= (*loc)->month_names->type_names; *month; month++)
{
set_if_bigger(max_month_len,
- my_numchars_mb(&my_charset_utf8_general_ci,
+ my_numchars_mb(&my_charset_utf8mb3_general_ci,
*month, *month + strlen(*month)));
}
for (const char **day= (*loc)->day_names->type_names; *day; day++)
{
set_if_bigger(max_day_len,
- my_numchars_mb(&my_charset_utf8_general_ci,
+ my_numchars_mb(&my_charset_utf8mb3_general_ci,
*day, *day + strlen(*day)));
}
if ((*loc)->max_month_name_length != max_month_len ||
@@ -5525,10 +5268,10 @@ int mysqld_main(int argc, char **argv)
if (init_early_variables())
exit(1);
-#ifdef HAVE_NPTL
- ld_assume_kernel_is_set= (getenv("LD_ASSUME_KERNEL") != 0);
-#endif
#ifndef _WIN32
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ pre_initialize_performance_schema();
+#endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */
// For windows, my_init() is called from the win specific mysqld_main
if (my_init()) // init my_sys library & pthreads
{
@@ -5539,16 +5282,15 @@ int mysqld_main(int argc, char **argv)
orig_argc= argc;
orig_argv= argv;
- my_getopt_use_args_separator= TRUE;
+ my_defaults_mark_files= TRUE;
load_defaults_or_exit(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv);
- my_getopt_use_args_separator= FALSE;
defaults_argc= argc;
defaults_argv= argv;
remaining_argc= argc;
remaining_argv= argv;
/* Must be initialized early for comparison of options name */
- system_charset_info= &my_charset_utf8_general_ci;
+ system_charset_info= &my_charset_utf8mb3_general_ci;
sys_var_init();
@@ -5629,6 +5371,7 @@ int mysqld_main(int argc, char **argv)
init_server_psi_keys();
/* Instrument the main thread */
PSI_thread *psi= PSI_CALL_new_thread(key_thread_main, NULL, 0);
+ PSI_CALL_set_thread_os_id(psi);
PSI_CALL_set_thread(psi);
/*
@@ -5691,7 +5434,13 @@ int mysqld_main(int argc, char **argv)
new_thread_stack_size= my_setstacksize(&connection_attrib,
(size_t)my_thread_stack_size);
if (new_thread_stack_size != my_thread_stack_size)
+ {
+ if ((new_thread_stack_size < my_thread_stack_size) &&
+ global_system_variables.log_warnings)
+ sql_print_warning("Asked for %llu thread stack, but got %llu",
+ my_thread_stack_size, new_thread_stack_size);
SYSVAR_AUTOSIZE(my_thread_stack_size, new_thread_stack_size);
+ }
(void) thr_setconcurrency(concurrency); // 10 by default
@@ -5809,6 +5558,7 @@ int mysqld_main(int argc, char **argv)
if (Events::init((THD*) 0, opt_noacl || opt_bootstrap))
unireg_abort(1);
+#ifdef WITH_WSREP
if (WSREP_ON)
{
if (opt_bootstrap)
@@ -5820,14 +5570,15 @@ int mysqld_main(int argc, char **argv)
wsrep_init_globals();
if (!wsrep_before_SE())
{
- wsrep_init_startup (false);
+ wsrep_init_startup(false);
}
-
+ wsrep_new_cluster= false;
WSREP_DEBUG("Startup creating %ld applier threads running %lu",
wsrep_slave_threads - 1, wsrep_running_applier_threads);
wsrep_create_appliers(wsrep_slave_threads - 1);
}
}
+#endif /* WITH_WSREP */
if (opt_bootstrap)
{
@@ -5906,7 +5657,7 @@ int mysqld_main(int argc, char **argv)
mysql_cond_broadcast(&COND_server_started);
mysql_mutex_unlock(&LOCK_server_started);
- MYSQL_SET_STAGE(0 ,__FILE__, __LINE__);
+ (void)MYSQL_SET_STAGE(0 ,__FILE__, __LINE__);
/* Memory used when everything is setup */
start_memory_used= global_status_var.global_memory_used;
@@ -5935,10 +5686,12 @@ int mysqld_main(int argc, char **argv)
{
wsrep_shutdown_replication();
}
+ /* Release threads if they are waiting in WSREP_SYNC_WAIT_UPTO_GTID */
+ wsrep_gtid_server.signal_waiters(0, true);
#endif
close_connections();
-
+ ha_pre_shutdown();
clean_up(1);
sd_notify(0, "STATUS=MariaDB server is down");
@@ -6073,6 +5826,31 @@ default_service_handling(char **argv,
return 1;
}
+/* Remove service name from the command line arguments, and pass
+resulting command line to the service via opt_args.*/
+#include <vector>
+static void service_init_cmdline_args(int argc, char **argv)
+{
+ start_mode= 1;
+ use_opt_args= 1;
+
+ if(argc == 1)
+ {
+ opt_argc= argc;
+ opt_argv= argv;
+ }
+ else
+ {
+ static std::vector<char *> argv_no_service;
+ for (int i= 0; argv[i]; i++)
+ argv_no_service.push_back(argv[i]);
+ // Remove the last argument, service name
+ argv_no_service[argv_no_service.size() - 1]= 0;
+ opt_argc= (int)argv_no_service.size() - 1;
+ opt_argv= &argv_no_service[0];
+ }
+ DBUG_ASSERT(!opt_argv[opt_argc]);
+}
int mysqld_main(int argc, char **argv)
{
@@ -6087,7 +5865,11 @@ int mysqld_main(int argc, char **argv)
"MySQLShutdown"), 10);
/* Must be initialized early for comparison of service name */
- system_charset_info= &my_charset_utf8_general_ci;
+ system_charset_info= &my_charset_utf8mb3_general_ci;
+
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ pre_initialize_performance_schema();
+#endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */
if (my_init())
{
@@ -6100,6 +5882,7 @@ int mysqld_main(int argc, char **argv)
my_path(file_path, argv[0], ""); /* Find name in path */
fn_format(file_path,argv[0],file_path,"", MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS);
+
if (argc == 2)
{
if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
@@ -6116,7 +5899,7 @@ int mysqld_main(int argc, char **argv)
*/
if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
load_default_groups[load_default_groups_sz-2]= argv[1];
- start_mode= 1;
+ service_init_cmdline_args(argc, argv);
Service.Init(argv[1], mysql_service);
return 0;
}
@@ -6132,12 +5915,9 @@ int mysqld_main(int argc, char **argv)
mysqld was started as
mysqld --defaults-file=my_path\my.ini service-name
*/
- use_opt_args=1;
- opt_argc= 2; // Skip service-name
- opt_argv=argv;
- start_mode= 1;
if (my_strcasecmp(system_charset_info, argv[2],"mysql"))
load_default_groups[load_default_groups_sz-2]= argv[2];
+ service_init_cmdline_args(argc, argv);
Service.Init(argv[2], mysql_service);
return 0;
}
@@ -6170,7 +5950,7 @@ int mysqld_main(int argc, char **argv)
else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
{
/* start the default service */
- start_mode= 1;
+ service_init_cmdline_args(argc, argv);
Service.Init(MYSQL_SERVICENAME, mysql_service);
return 0;
}
@@ -6218,8 +5998,7 @@ void inc_thread_created(void)
void handle_connection_in_main_thread(CONNECT *connect)
{
- thread_cache_size= 0; // Safety
- do_handle_one_connection(connect);
+ do_handle_one_connection(connect, false);
}
@@ -6229,37 +6008,22 @@ void handle_connection_in_main_thread(CONNECT *connect)
void create_thread_to_handle_connection(CONNECT *connect)
{
- char error_message_buff[MYSQL_ERRMSG_SIZE];
- int error;
DBUG_ENTER("create_thread_to_handle_connection");
- /* Check if we can get thread from the cache */
- if (cached_thread_count > wake_thread)
- {
- mysql_mutex_lock(&LOCK_thread_cache);
- /* Recheck condition when we have the lock */
- if (cached_thread_count > wake_thread)
- {
- /* Get thread from cache */
- thread_cache.push_back(connect);
- 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);
- }
+ if (thread_cache.enqueue(connect))
+ DBUG_VOID_RETURN;
/* Create new thread to handle connection */
inc_thread_created();
DBUG_PRINT("info",(("creating thread %lu"), (ulong) connect->thread_id));
connect->prior_thr_create_utime= microsecond_interval_timer();
- if ((error= mysql_thread_create(key_thread_one_connection,
- &connect->real_id, &connection_attrib,
- handle_one_connection, (void*) connect)))
+ pthread_t tmp;
+ if (auto error= mysql_thread_create(key_thread_one_connection,
+ &tmp, &connection_attrib,
+ handle_one_connection, (void*) connect))
{
+ char error_message_buff[MYSQL_ERRMSG_SIZE];
/* purecov: begin inspected */
DBUG_PRINT("error", ("Can't create thread to handle request (error %d)",
error));
@@ -6296,29 +6060,17 @@ void create_new_thread(CONNECT *connect)
Don't allow too many connections. We roughly check here that we allow
only (max_connections + 1) connections.
*/
-
- mysql_mutex_lock(&LOCK_connection_count);
-
- if (*connect->scheduler->connection_count >=
- *connect->scheduler->max_connections + 1|| abort_loop)
+ if ((*connect->scheduler->connection_count)++ >=
+ *connect->scheduler->max_connections + 1)
{
DBUG_PRINT("error",("Too many connections"));
-
- mysql_mutex_unlock(&LOCK_connection_count);
- statistic_increment(denied_connections, &LOCK_status);
- statistic_increment(connection_errors_max_connection, &LOCK_status);
- connect->close_with_error(0, NullS, abort_loop ? ER_SERVER_SHUTDOWN : ER_CON_COUNT_ERROR);
+ connect->close_with_error(0, NullS, ER_CON_COUNT_ERROR);
DBUG_VOID_RETURN;
}
- ++*connect->scheduler->connection_count;
-
- if (connection_count + extra_connection_count > max_used_connections)
- max_used_connections= connection_count + extra_connection_count;
-
- mysql_mutex_unlock(&LOCK_connection_count);
-
- connect->thread_count_incremented= 1;
+ uint sum= connection_count + extra_connection_count;
+ if (sum > max_used_connections)
+ max_used_connections= sum;
/*
The initialization of thread_id is done in create_embedded_thd() for
@@ -6339,13 +6091,6 @@ void create_new_thread(CONNECT *connect)
void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock)
{
- CONNECT *connect;
- bool is_unix_sock;
-
-#ifdef FD_CLOEXEC
- (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC);
-#endif
-
#ifdef HAVE_LIBWRAP
{
if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) ||
@@ -6391,53 +6136,46 @@ void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock)
DBUG_PRINT("info", ("Creating CONNECT for new connection"));
- if ((connect= new CONNECT()))
- {
- is_unix_sock= (mysql_socket_getfd(sock) ==
- mysql_socket_getfd(unix_sock));
-
- if (!(connect->vio=
- mysql_socket_vio_new(new_sock,
- is_unix_sock ? VIO_TYPE_SOCKET :
- VIO_TYPE_TCPIP,
- is_unix_sock ? VIO_LOCALHOST : 0)))
- {
- delete connect;
- connect= 0; // Error handling below
- }
- }
-
- if (!connect)
+ if (auto connect= new CONNECT(new_sock,
+ mysql_socket_getfd(sock) ==
+ mysql_socket_getfd(unix_sock) ?
+ VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
+ mysql_socket_getfd(sock) ==
+ mysql_socket_getfd(extra_ip_sock) ?
+ extra_thread_scheduler : thread_scheduler))
+ create_new_thread(connect);
+ else
{
/* Connect failure */
(void)mysql_socket_close(new_sock);
statistic_increment(aborted_connects, &LOCK_status);
statistic_increment(connection_errors_internal, &LOCK_status);
- return;
}
+}
- if (is_unix_sock)
- connect->host= my_localhost;
-
- if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
+#ifndef _WIN32
+static void set_non_blocking_if_supported(MYSQL_SOCKET sock)
+{
+#if !defined(NO_FCNTL_NONBLOCK)
+ if (!(test_flags & TEST_BLOCKING))
{
- connect->extra_port= 1;
- connect->scheduler= extra_thread_scheduler;
+ int flags= fcntl(mysql_socket_getfd(sock), F_GETFL, 0);
+#if defined(O_NONBLOCK)
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NONBLOCK);
+#elif defined(O_NDELAY)
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NDELAY);
+#endif
}
- create_new_thread(connect);
+#endif
}
-#ifndef _WIN32
+
void handle_connections_sockets()
{
MYSQL_SOCKET sock= mysql_socket_invalid();
- MYSQL_SOCKET new_sock= mysql_socket_invalid();
uint error_count=0;
struct sockaddr_storage cAddr;
- int ip_flags __attribute__((unused))=0;
- int socket_flags __attribute__((unused))= 0;
- int extra_ip_flags __attribute__((unused))=0;
- int flags=0,retval;
+ int retval;
#ifdef HAVE_POLL
int socket_count= 0;
struct pollfd fds[3]; // for ip_sock, unix_sock and extra_ip_sock
@@ -6459,16 +6197,16 @@ void handle_connections_sockets()
if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET)
{
setup_fds(base_ip_sock);
- ip_flags = fcntl(mysql_socket_getfd(base_ip_sock), F_GETFL, 0);
+ set_non_blocking_if_supported(base_ip_sock);
}
if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET)
{
setup_fds(extra_ip_sock);
- extra_ip_flags = fcntl(mysql_socket_getfd(extra_ip_sock), F_GETFL, 0);
+ set_non_blocking_if_supported(extra_ip_sock);
}
#ifdef HAVE_SYS_UN_H
setup_fds(unix_sock);
- socket_flags=fcntl(mysql_socket_getfd(unix_sock), F_GETFL, 0);
+ set_non_blocking_if_supported(unix_sock);
#endif
sd_notify(0, "READY=1\n"
@@ -6510,79 +6248,44 @@ void handle_connections_sockets()
if (fds[i].revents & POLLIN)
{
sock= pfs_fds[i];
- flags= fcntl(mysql_socket_getfd(sock), F_GETFL, 0);
break;
}
}
#else // HAVE_POLL
if (FD_ISSET(mysql_socket_getfd(base_ip_sock),&readFDs))
- {
sock= base_ip_sock;
- flags= ip_flags;
- }
else
if (FD_ISSET(mysql_socket_getfd(extra_ip_sock),&readFDs))
- {
sock= extra_ip_sock;
- flags= extra_ip_flags;
- }
else
- {
sock = unix_sock;
- flags= socket_flags;
- }
#endif // HAVE_POLL
-#if !defined(NO_FCNTL_NONBLOCK)
- if (!(test_flags & TEST_BLOCKING))
- {
-#if defined(O_NONBLOCK)
- fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NONBLOCK);
-#elif defined(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);
+ MYSQL_SOCKET new_sock;
+
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;
-#if !defined(NO_FCNTL_NONBLOCK)
- if (!(test_flags & TEST_BLOCKING))
+ if (mysql_socket_getfd(new_sock) != INVALID_SOCKET)
+ handle_accepted_socket(new_sock, sock);
+ else if (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN)
{
- if (retry == MAX_ACCEPT_RETRY - 1)
- {
- // Try without O_NONBLOCK
- fcntl(mysql_socket_getfd(sock), F_SETFL, flags);
- }
+ /*
+ accept(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 ((error_count++ & 255) == 0) // This can happen often
+ sql_perror("Error in accept");
+ if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
+ sleep(1); // Give other threads some time
+ break;
}
-#endif
}
-
- 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");
- if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
- sleep(1); // Give other threads some time
- continue;
- }
-#if !defined(NO_FCNTL_NONBLOCK)
- if (!(test_flags & TEST_BLOCKING))
- fcntl(mysql_socket_getfd(sock), F_SETFL, flags);
-#endif
- handle_accepted_socket(new_sock, sock);
}
sd_notify(0, "STOPPING=1\n"
"STATUS=Shutdown in progress\n");
@@ -6615,12 +6318,12 @@ 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));
+ my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &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);
@@ -6641,20 +6344,6 @@ int handle_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
@@ -6845,7 +6534,7 @@ struct my_option my_long_options[]=
"mysql.gtid_slave_pos",
&gtid_pos_auto_engines, 0, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0 },
-#ifdef HAVE_LARGE_PAGE_OPTION
+#ifdef HAVE_SOLARIS_LARGE_PAGES
{"super-large-pages", 0, "Enable support for super large pages.",
&opt_super_large_pages, &opt_super_large_pages, 0,
GET_BOOL, OPT_ARG, 0, 0, 1, 0, 1, 0},
@@ -6993,9 +6682,6 @@ struct my_option my_long_options[]=
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"silent-startup", OPT_SILENT, "Don't print [Note] to the error log during startup.",
&opt_silent_startup, &opt_silent_startup, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"skip-bdb", OPT_DEPRECATED_OPTION,
- "Deprecated option; Exist only for compatibility with old my.cnf files",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DISABLE_GRANT_OPTIONS
{"skip-grant-tables", 0,
"Start without grant tables. This gives all users FULL ACCESS to all tables.",
@@ -7062,11 +6748,11 @@ struct my_option my_long_options[]=
{"temp-pool", 0,
#if (ENABLE_TEMP_POOL)
"Using this option will cause most temporary files created to use a small "
- "set of names, rather than a unique name for each new file.",
+ "set of names, rather than a unique name for each new file. Deprecated.",
#else
"This option is ignored on this OS.",
#endif
- &use_temp_pool, &use_temp_pool, 0, GET_BOOL, NO_ARG, 1,
+ &use_temp_pool, &use_temp_pool, 0, GET_BOOL, NO_ARG, 0,
0, 0, 0, 0, 0},
{"transaction-isolation", 0,
"Default transaction isolation level",
@@ -7107,42 +6793,6 @@ struct my_option my_long_options[]=
"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_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_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-features"), // OPTIMIZER_TRACE
- MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-offset"), // OPTIMIZER_TRACE
- MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-limit"), // OPTIMIZER_TRACE
- MYSQL_COMPATIBILITY_OPTION("server-id-bits"),
- MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-rows-search-algorithms"), // HAVE_REPLICATION
- 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-pending-jobs-size-max", "--slave-parallel-max-queued"), // HAVE_REPLICATION
- MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL
- MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-public-key-path"), // HAVE_OPENSSL
-
- /* 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"),
- MYSQL_COMPATIBILITY_OPTION("show_compatibility_56"),
-
- /* 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,
@@ -7301,16 +6951,6 @@ static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff,
}
-static int show_flush_commands(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope)
-{
- var->type= SHOW_LONGLONG;
- var->value= buff;
- *((longlong *) buff)= (longlong)tdc_refresh_version();
- return 0;
-}
-
-
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
/*
@@ -7620,7 +7260,7 @@ static int debug_status_func(THD *thd, SHOW_VAR *var, void *buff,
#endif
#ifdef HAVE_POOL_OF_THREADS
-int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff,
+static int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope)
{
var->type= SHOW_INT;
@@ -7640,6 +7280,17 @@ static int show_threadpool_threads(THD *thd, SHOW_VAR *var, char *buff,
}
#endif
+
+static int show_cached_thread_count(THD *thd, SHOW_VAR *var, char *buff,
+ enum enum_var_type scope)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *(reinterpret_cast<ulong*>(buff))= thread_cache.size();
+ return 0;
+}
+
+
/*
Variables shown by SHOW STATUS in alphabetical order
*/
@@ -7689,6 +7340,7 @@ SHOW_VAR status_vars[]= {
{"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_insert_returning", (char*)offsetof(STATUS_VAR, feature_insert_returning), SHOW_LONG_STATUS},
{"Feature_invisible_columns", (char*) offsetof(STATUS_VAR, feature_invisible_columns), SHOW_LONG_STATUS},
{"Feature_json", (char*) offsetof(STATUS_VAR, feature_json), SHOW_LONG_STATUS},
{"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS},
@@ -7699,7 +7351,6 @@ SHOW_VAR status_vars[]= {
{"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
{"Feature_window_functions", (char*) offsetof(STATUS_VAR, feature_window_functions), 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},
@@ -7859,7 +7510,7 @@ SHOW_VAR status_vars[]= {
{"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_SIMPLE_FUNC},
{"Threadpool_threads", (char *) &show_threadpool_threads, SHOW_SIMPLE_FUNC},
#endif
- {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
+ {"Threads_cached", (char*) &show_cached_thread_count, SHOW_SIMPLE_FUNC},
{"Threads_connected", (char*) &connection_count, SHOW_INT},
{"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH},
{"Threads_running", (char*) offsetof(STATUS_VAR, threads_running), SHOW_UINT32_STATUS},
@@ -7946,7 +7597,7 @@ static int option_cmp(my_option *a, my_option *b)
static void print_help()
{
MEM_ROOT mem_root;
- init_alloc_root(&mem_root, "help", 4096, 4096, MYF(0));
+ init_alloc_root(PSI_NOT_INSTRUMENTED, &mem_root, 4096, 4096, MYF(0));
pop_dynamic(&all_options);
add_many_options(&all_options, pfs_early_options,
@@ -8053,10 +7704,9 @@ static int mysql_init_variables(void)
disable_log_notes= 0;
mqh_used= 0;
cleanup_done= 0;
- select_errors= dropping_tables= ha_open_options=0;
- thread_count= kill_cached_threads= wake_thread= 0;
+ test_flags= select_errors= dropping_tables= ha_open_options=0;
+ thread_count= 0;
slave_open_temp_tables= 0;
- cached_thread_count= 0;
opt_endinfo= using_udf_functions= 0;
opt_using_transactions= 0;
abort_loop= select_thread_in_use= signal_thread_in_use= 0;
@@ -8083,9 +7733,9 @@ static int mysql_init_variables(void)
key_map_full.set_all();
/* Character sets */
- system_charset_info= &my_charset_utf8_general_ci;
- files_charset_info= &my_charset_utf8_general_ci;
- national_charset_info= &my_charset_utf8_general_ci;
+ system_charset_info= &my_charset_utf8mb3_general_ci;
+ files_charset_info= &my_charset_utf8mb3_general_ci;
+ national_charset_info= &my_charset_utf8mb3_general_ci;
table_alias_charset= &my_charset_bin;
character_set_filesystem= &my_charset_bin;
@@ -8102,7 +7752,7 @@ static int mysql_init_variables(void)
global_query_id= 1;
global_thread_id= 0;
strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1);
- thread_cache.empty();
+ thread_cache.init();
key_caches.empty();
if (!(dflt_key_cache= get_or_create_key_cache(default_key_cache_base.str,
default_key_cache_base.length)))
@@ -8140,8 +7790,8 @@ static int mysql_init_variables(void)
/* Variables that depends on compile options */
#ifndef DBUG_OFF
- default_dbug_option=IF_WIN("d:t:i:O,\\mysqld.trace",
- "d:t:i:o,/tmp/mysqld.trace");
+ default_dbug_option=IF_WIN("d:t:i:O,\\mariadbd.trace",
+ "d:t:i:o,/tmp/mariadbd.trace");
current_dbug_option= default_dbug_option;
#endif
opt_error_log= IF_WIN(1,0);
@@ -8239,7 +7889,8 @@ static int mysql_init_variables(void)
}
my_bool
-mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
+mysqld_get_one_option(const struct my_option *opt, char *argument,
+ const char *filename)
{
if (opt->app_type)
{
@@ -8249,10 +7900,16 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
var->value_origin= sys_var::AUTO;
return 0;
}
- var->value_origin= sys_var::CONFIG;
+ if (*filename)
+ {
+ var->origin_filename= filename;
+ var->value_origin= sys_var::CONFIG;
+ }
+ else
+ var->value_origin= sys_var::COMMAND_LINE;
}
- switch(optid) {
+ switch(opt->id) {
case '#':
#ifndef DBUG_OFF
if (!argument)
@@ -8272,10 +7929,9 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
sql_print_warning("'%s' is disabled in this build", opt->name);
#endif
break;
- case OPT_DEPRECATED_OPTION:
- sql_print_warning("'%s' is deprecated. It does nothing and exists only "
- "for compatibility with old my.cnf files.",
- opt->name);
+ case OPT_REMOVED_OPTION:
+ sql_print_warning("'%s' was removed. It does nothing now and exists only "
+ "for compatibility with old my.cnf files.", opt->name);
break;
case OPT_MYSQL_COMPATIBILITY:
sql_print_warning("'%s' is MySQL 5.6 / 5.7 compatible option. Not used or "
@@ -8577,9 +8233,6 @@ mysqld_get_one_option(int optid, const struct my_option *opt, char *argument)
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
@@ -8766,9 +8419,15 @@ static void option_error_reporter(enum loglevel level, const char *format, ...)
va_list args;
va_start(args, format);
- /* Don't print warnings for --loose options during bootstrap */
- if (level == ERROR_LEVEL || !opt_bootstrap ||
- global_system_variables.log_warnings)
+ /*
+ Don't print warnings for --loose options during bootstrap if
+ log_warnings <= 2 (2 is default) as warnings during bootstrap
+ can confuse people when running mysql_install_db and other scripts.
+ Don't print loose warnings at all if log_warnings <= 1
+ */
+ if (level == ERROR_LEVEL ||
+ (global_system_variables.log_warnings >
+ (ulong) (1 + MY_TEST(opt_bootstrap))))
{
vprint_msg_to_log(level, format, args);
}
@@ -8791,13 +8450,12 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
{
int ho_error;
- my_getopt_register_get_addr(mysql_getopt_value);
+ my_getopt_get_addr= mysql_getopt_value;
my_getopt_error_reporter= option_error_reporter;
/* prepare all_options array */
- my_init_dynamic_array(&all_options, sizeof(my_option),
- array_elements(my_long_options) +
- sys_var_elements(),
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &all_options, sizeof(my_option),
+ array_elements(my_long_options) + sys_var_elements(),
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);
@@ -8995,14 +8653,6 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
opt_readonly= read_only;
- /*
- If max_long_data_size is not specified explicitly use
- value of max_allowed_packet.
- */
- if (!max_long_data_size_used)
- SYSVAR_AUTOSIZE(max_long_data_size,
- global_system_variables.max_allowed_packet);
-
/* Remember if max_user_connections was 0 at startup */
max_user_connections_checking= global_system_variables.max_user_connections != 0;
@@ -9142,11 +8792,10 @@ bool is_secure_file_path(char *path)
}
else
{
- if (files_charset_info->coll->strnncoll(files_charset_info,
- (uchar *) buff2, strlen(buff2),
- (uchar *) opt_secure_file_priv,
- opt_secure_file_priv_len,
- TRUE))
+ if (files_charset_info->strnncoll(buff2, strlen(buff2),
+ opt_secure_file_priv,
+ opt_secure_file_priv_len,
+ TRUE))
return FALSE;
}
return TRUE;
@@ -9237,7 +8886,7 @@ static int fix_paths(void)
sql_print_warning("Failed to normalize the argument for --secure-file-priv.");
DBUG_RETURN(1);
}
- char *secure_file_real_path= (char *)my_malloc(FN_REFLEN, MYF(MY_FAE));
+ char *secure_file_real_path= (char *)my_malloc(PSI_INSTRUMENT_ME, FN_REFLEN, MYF(MY_FAE));
convert_dirname(secure_file_real_path, buff, NullS);
my_free(opt_secure_file_priv);
opt_secure_file_priv= secure_file_real_path;
@@ -9338,6 +8987,11 @@ void refresh_status(THD *thd)
{
mysql_mutex_lock(&LOCK_status);
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ /* Reset aggregated status counters. */
+ reset_pfs_status_stats();
+#endif
+
/* Add thread's status variabes to global status */
add_to_status(&global_status_var, &thd->status_var);
@@ -9375,9 +9029,14 @@ static PSI_file_info all_server_files[]=
{ &key_file_map, "map", 0},
#endif /* HAVE_MMAP */
{ &key_file_binlog, "binlog", 0},
+ { &key_file_binlog_cache, "binlog_cache", 0},
{ &key_file_binlog_index, "binlog_index", 0},
+ { &key_file_binlog_index_cache, "binlog_index_cache", 0},
{ &key_file_relaylog, "relaylog", 0},
+ { &key_file_relaylog_cache, "relaylog_cache", 0},
{ &key_file_relaylog_index, "relaylog_index", 0},
+ { &key_file_relaylog_index_cache, "relaylog_index_cache", 0},
+ { &key_file_io_cache, "io_cache", 0},
{ &key_file_casetest, "casetest", 0},
{ &key_file_dbopt, "dbopt", 0},
{ &key_file_des_key_file, "des_key_file", 0},
@@ -9392,7 +9051,7 @@ static PSI_file_info all_server_files[]=
{ &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_partition_ddl_log, "partition_ddl_log", 0},
{ &key_file_pid, "pid", 0},
{ &key_file_query_log, "query_log", 0},
{ &key_file_relay_log_info, "relay_log_info", 0},
@@ -9411,25 +9070,25 @@ 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_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_apply_event= { 0, "Apply log event", 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_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, "Reset for next command", 0};
-PSI_stage_info stage_closing_tables= { 0, "Closing tables", 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_copy_to_tmp_table= { 0, "copy to tmp table", PSI_FLAG_STAGE_PROGRESS};
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_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};
@@ -9447,17 +9106,17 @@ 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_init_update= { 0, "Init for update", 0};
+PSI_stage_info stage_init= { 0, "init", 0};
+PSI_stage_info stage_init_update= { 0, "init for update", 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_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_master_has_sent_all_binlog_to_slave= { 0, "Master has sent all binlog to slave; waiting for more updates", 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};
@@ -9467,7 +9126,7 @@ PSI_stage_info stage_starting_cleanup= { 0, "Starting cleanup", 0};
PSI_stage_info stage_rollback= { 0, "Rollback", 0};
PSI_stage_info stage_rollback_implicit= { 0, "Rollback_implicit", 0};
PSI_stage_info stage_commit= { 0, "Commit", 0};
-PSI_stage_info stage_commit_implicit= { 0, "Commit_implicit", 0};
+PSI_stage_info stage_commit_implicit= { 0, "Commit implicit", 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};
@@ -9482,9 +9141,9 @@ PSI_stage_info stage_searching_rows_for_update= { 0, "Searching rows for update"
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_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_slave_has_read_all_relay_log= { 0, "Slave has read all relay log; waiting for more updates", 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};
@@ -9495,7 +9154,7 @@ PSI_stage_info stage_storing_result_in_query_cache= { 0, "Storing result in quer
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_unlocking_tables= { 0, "Unlocking tables", 0};
-PSI_stage_info stage_table_lock= { 0, "Table 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};
@@ -9525,14 +9184,13 @@ PSI_stage_info stage_waiting_for_query_cache_lock= { 0, "Waiting for query cache
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_binlog_waiting_background_tasks= { 0, "Waiting for background binlog tasks", 0};
PSI_stage_info stage_binlog_write= { 0, "Writing to binlog", 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_prior_transaction_to_start_commit= { 0, "Waiting for prior transaction to start commit", 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};
@@ -9540,10 +9198,92 @@ PSI_stage_info stage_waiting_for_ftwrl_threads_to_pause= { 0, "Waiting for worke
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};
+PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master connection to process the same GTID", 0};
PSI_stage_info stage_slave_background_process_request= { 0, "Processing requests", 0};
PSI_stage_info stage_slave_background_wait_request= { 0, "Waiting for requests", 0};
PSI_stage_info stage_waiting_for_deadlock_kill= { 0, "Waiting for parallel replication deadlock handling to complete", 0};
+PSI_stage_info stage_starting= { 0, "starting", 0};
+
+PSI_memory_key key_memory_DATE_TIME_FORMAT;
+PSI_memory_key key_memory_DDL_LOG_MEMORY_ENTRY;
+PSI_memory_key key_memory_Event_queue_element_for_exec_names;
+PSI_memory_key key_memory_Event_scheduler_scheduler_param;
+PSI_memory_key key_memory_Filesort_info_merge;
+PSI_memory_key key_memory_Filesort_info_record_pointers;
+PSI_memory_key key_memory_Gis_read_stream_err_msg;
+PSI_memory_key key_memory_JOIN_CACHE;
+PSI_memory_key key_memory_MPVIO_EXT_auth_info;
+PSI_memory_key key_memory_MYSQL_BIN_LOG_basename;
+PSI_memory_key key_memory_MYSQL_BIN_LOG_index;
+PSI_memory_key key_memory_MYSQL_LOCK;
+PSI_memory_key key_memory_MYSQL_LOG_name;
+PSI_memory_key key_memory_MYSQL_RELAY_LOG_basename;
+PSI_memory_key key_memory_MYSQL_RELAY_LOG_index;
+PSI_memory_key key_memory_NAMED_ILINK_name;
+PSI_memory_key key_memory_PROFILE;
+PSI_memory_key key_memory_QUICK_RANGE_SELECT_mrr_buf_desc;
+PSI_memory_key key_memory_Query_cache;
+PSI_memory_key key_memory_Relay_log_info_group_relay_log_name;
+PSI_memory_key key_memory_Row_data_memory_memory;
+PSI_memory_key key_memory_Rpl_info_file_buffer;
+PSI_memory_key key_memory_SLAVE_INFO;
+PSI_memory_key key_memory_ST_SCHEMA_TABLE;
+PSI_memory_key key_memory_Sort_param_tmp_buffer;
+PSI_memory_key key_memory_Sys_var_charptr_value;
+PSI_memory_key key_memory_TABLE;
+PSI_memory_key key_memory_TABLE_RULE_ENT;
+PSI_memory_key key_memory_TC_LOG_MMAP_pages;
+PSI_memory_key key_memory_THD_db;
+PSI_memory_key key_memory_THD_handler_tables_hash;
+PSI_memory_key key_memory_THD_variables;
+PSI_memory_key key_memory_Table_trigger_dispatcher;
+PSI_memory_key key_memory_Unique_merge_buffer;
+PSI_memory_key key_memory_Unique_sort_buffer;
+PSI_memory_key key_memory_User_level_lock;
+PSI_memory_key key_memory_XID;
+PSI_memory_key key_memory_acl_cache;
+PSI_memory_key key_memory_acl_mem;
+PSI_memory_key key_memory_acl_memex;
+PSI_memory_key key_memory_binlog_cache_mngr;
+PSI_memory_key key_memory_binlog_pos;
+PSI_memory_key key_memory_binlog_recover_exec;
+PSI_memory_key key_memory_binlog_statement_buffer;
+PSI_memory_key key_memory_binlog_ver_1_event;
+PSI_memory_key key_memory_bison_stack;
+PSI_memory_key key_memory_blob_mem_storage;
+PSI_memory_key key_memory_dboptions_hash;
+PSI_memory_key key_memory_errmsgs;
+PSI_memory_key key_memory_frm_string;
+PSI_memory_key key_memory_gdl;
+PSI_memory_key key_memory_global_system_variables;
+PSI_memory_key key_memory_handler_errmsgs;
+PSI_memory_key key_memory_handlerton;
+PSI_memory_key key_memory_hash_index_key_buffer;
+PSI_memory_key key_memory_host_cache_hostname;
+PSI_memory_key key_memory_ignored_db;
+PSI_memory_key key_memory_locked_table_list;
+PSI_memory_key key_memory_locked_thread_list;
+PSI_memory_key key_memory_my_str_malloc;
+PSI_memory_key key_memory_native_functions;
+PSI_memory_key key_memory_prepared_statement_main_mem_root;
+PSI_memory_key key_memory_prepared_statement_map;
+PSI_memory_key key_memory_queue_item;
+PSI_memory_key key_memory_quick_range_select_root;
+PSI_memory_key key_memory_rpl_filter;
+PSI_memory_key key_memory_sp_cache;
+PSI_memory_key key_memory_sp_head_call_root;
+PSI_memory_key key_memory_sp_head_execute_root;
+PSI_memory_key key_memory_sp_head_main_root;
+PSI_memory_key key_memory_table_mapping_root;
+PSI_memory_key key_memory_table_share;
+PSI_memory_key key_memory_table_triggers_list;
+PSI_memory_key key_memory_thd_main_mem_root;
+PSI_memory_key key_memory_thd_transactions;
+PSI_memory_key key_memory_user_conn;
+PSI_memory_key key_memory_user_var_entry;
+PSI_memory_key key_memory_user_var_entry_value;
+
+PSI_memory_key key_memory_String_value;
#ifdef HAVE_PSI_INTERFACE
@@ -9672,7 +9412,6 @@ PSI_stage_info *all_server_stages[]=
& 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,
@@ -9681,7 +9420,8 @@ PSI_stage_info *all_server_stages[]=
& stage_waiting_for_semi_sync_ack_from_slave,
& stage_waiting_for_semi_sync_slave,
& stage_reading_semi_sync_ack,
- & stage_waiting_for_deadlock_kill
+ & stage_waiting_for_deadlock_kill,
+ & stage_starting
};
PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection;
@@ -9693,6 +9433,145 @@ static PSI_socket_info all_server_sockets[]=
{ &key_socket_client_connection, "client_connection", 0}
};
+static PSI_memory_info all_server_memory[]=
+{
+ { &key_memory_locked_table_list, "Locked_tables_list::m_locked_tables_root", 0},
+ { &key_memory_locked_thread_list, "display_table_locks", PSI_FLAG_THREAD},
+ { &key_memory_thd_transactions, "THD::transactions::mem_root", PSI_FLAG_THREAD},
+// { &key_memory_delegate, "Delegate::memroot", 0},
+ { &key_memory_acl_mem, "sql_acl_mem", PSI_FLAG_GLOBAL},
+ { &key_memory_acl_memex, "sql_acl_memex", PSI_FLAG_GLOBAL},
+ { &key_memory_acl_cache, "acl_cache", PSI_FLAG_GLOBAL},
+ { &key_memory_thd_main_mem_root, "thd::main_mem_root", PSI_FLAG_THREAD},
+// { &key_memory_help, "help", 0},
+// { &key_memory_new_frm_mem, "new_frm_mem", 0},
+ { &key_memory_table_share, "TABLE_SHARE::mem_root", PSI_FLAG_GLOBAL}, /* table definition cache */
+ { &key_memory_gdl, "gdl", 0},
+ { &key_memory_table_triggers_list, "Table_triggers_list", 0},
+// { &key_memory_servers, "servers", 0},
+ { &key_memory_prepared_statement_map, "Prepared_statement_map", PSI_FLAG_THREAD},
+ { &key_memory_prepared_statement_main_mem_root, "Prepared_statement::main_mem_root", PSI_FLAG_THREAD},
+// { &key_memory_protocol_rset_root, "Protocol_local::m_rset_root", PSI_FLAG_THREAD},
+// { &key_memory_warning_info_warn_root, "Warning_info::m_warn_root", PSI_FLAG_THREAD},
+ { &key_memory_sp_cache, "THD::sp_cache", 0},
+ { &key_memory_sp_head_main_root, "sp_head::main_mem_root", 0},
+ { &key_memory_sp_head_execute_root, "sp_head::execute_mem_root", PSI_FLAG_THREAD},
+ { &key_memory_sp_head_call_root, "sp_head::call_mem_root", PSI_FLAG_THREAD},
+ { &key_memory_table_mapping_root, "table_mapping::m_mem_root", 0},
+ { &key_memory_quick_range_select_root, "QUICK_RANGE_SELECT::alloc", PSI_FLAG_THREAD},
+// { &key_memory_quick_index_merge_root, "QUICK_INDEX_MERGE_SELECT::alloc", PSI_FLAG_THREAD},
+// { &key_memory_quick_ror_intersect_select_root, "QUICK_ROR_INTERSECT_SELECT::alloc", PSI_FLAG_THREAD},
+// { &key_memory_quick_ror_union_select_root, "QUICK_ROR_UNION_SELECT::alloc", PSI_FLAG_THREAD},
+// { &key_memory_quick_group_min_max_select_root, "QUICK_GROUP_MIN_MAX_SELECT::alloc", PSI_FLAG_THREAD},
+// { &key_memory_test_quick_select_exec, "test_quick_select", PSI_FLAG_THREAD},
+// { &key_memory_prune_partitions_exec, "prune_partitions::exec", 0},
+ { &key_memory_binlog_recover_exec, "MYSQL_BIN_LOG::recover", 0},
+ { &key_memory_blob_mem_storage, "Blob_mem_storage::storage", 0},
+ { &key_memory_NAMED_ILINK_name, "NAMED_ILINK::name", 0},
+ { &key_memory_String_value, "String::value", 0},
+ { &key_memory_Sys_var_charptr_value, "Sys_var_charptr::value", 0},
+ { &key_memory_queue_item, "Queue::queue_item", 0},
+ { &key_memory_THD_db, "THD::db", 0},
+ { &key_memory_user_var_entry, "user_var_entry", 0},
+// { &key_memory_Slave_job_group_group_relay_log_name, "Slave_job_group::group_relay_log_name", 0},
+ { &key_memory_Relay_log_info_group_relay_log_name, "Relay_log_info::group_relay_log_name", 0},
+ { &key_memory_binlog_cache_mngr, "binlog_cache_mngr", 0},
+ { &key_memory_Row_data_memory_memory, "Row_data_memory::memory", 0},
+// { &key_memory_Gtid_set_to_string, "Gtid_set::to_string", 0},
+// { &key_memory_Gtid_state_to_string, "Gtid_state::to_string", 0},
+// { &key_memory_Owned_gtids_to_string, "Owned_gtids::to_string", 0},
+// { &key_memory_log_event, "Log_event", 0},
+// { &key_memory_Incident_log_event_message, "Incident_log_event::message", 0},
+// { &key_memory_Rows_query_log_event_rows_query, "Rows_query_log_event::rows_query", 0},
+ { &key_memory_Sort_param_tmp_buffer, "Sort_param::tmp_buffer", 0},
+ { &key_memory_Filesort_info_merge, "Filesort_info::merge", 0},
+ { &key_memory_Filesort_info_record_pointers, "Filesort_info::record_pointers", 0},
+// { &key_memory_Filesort_buffer_sort_keys, "Filesort_buffer::sort_keys", 0},
+ { &key_memory_handler_errmsgs, "handler::errmsgs", 0},
+ { &key_memory_handlerton, "handlerton", 0},
+ { &key_memory_XID, "XID", 0},
+ { &key_memory_host_cache_hostname, "host_cache::hostname", 0},
+ { &key_memory_user_var_entry_value, "user_var_entry::value", 0},
+ { &key_memory_User_level_lock, "User_level_lock", 0},
+ { &key_memory_MYSQL_LOG_name, "MYSQL_LOG::name", 0},
+ { &key_memory_TC_LOG_MMAP_pages, "TC_LOG_MMAP::pages", 0},
+// { &key_memory_my_bitmap_map, "my_bitmap_map", 0},
+ { &key_memory_QUICK_RANGE_SELECT_mrr_buf_desc, "QUICK_RANGE_SELECT::mrr_buf_desc", 0},
+ { &key_memory_Event_queue_element_for_exec_names, "Event_queue_element_for_exec::names", 0},
+ { &key_memory_my_str_malloc, "my_str_malloc", 0},
+ { &key_memory_MYSQL_BIN_LOG_basename, "MYSQL_BIN_LOG::basename", 0},
+ { &key_memory_MYSQL_BIN_LOG_index, "MYSQL_BIN_LOG::index", 0},
+ { &key_memory_MYSQL_RELAY_LOG_basename, "MYSQL_RELAY_LOG::basename", 0},
+ { &key_memory_MYSQL_RELAY_LOG_index, "MYSQL_RELAY_LOG::index", 0},
+ { &key_memory_rpl_filter, "rpl_filter memory", 0},
+ { &key_memory_errmsgs, "errmsgs", 0},
+ { &key_memory_Gis_read_stream_err_msg, "Gis_read_stream::err_msg", 0},
+// { &key_memory_Geometry_objects_data, "Geometry::ptr_and_wkb_data", 0},
+ { &key_memory_MYSQL_LOCK, "MYSQL_LOCK", 0},
+// { &key_memory_NET_buff, "NET::buff", 0},
+// { &key_memory_NET_compress_packet, "NET::compress_packet", 0},
+ { &key_memory_Event_scheduler_scheduler_param, "Event_scheduler::scheduler_param", 0},
+// { &key_memory_Gtid_set_Interval_chunk, "Gtid_set::Interval_chunk", 0},
+// { &key_memory_Owned_gtids_sidno_to_hash, "Owned_gtids::sidno_to_hash", 0},
+// { &key_memory_Sid_map_Node, "Sid_map::Node", 0},
+// { &key_memory_Gtid_state_group_commit_sidno, "Gtid_state::group_commit_sidno_locks", 0},
+// { &key_memory_Mutex_cond_array_Mutex_cond, "Mutex_cond_array::Mutex_cond", 0},
+ { &key_memory_TABLE_RULE_ENT, "TABLE_RULE_ENT", 0},
+// { &key_memory_Rpl_info_table, "Rpl_info_table", 0},
+ { &key_memory_Rpl_info_file_buffer, "Rpl_info_file::buffer", 0},
+// { &key_memory_db_worker_hash_entry, "db_worker_hash_entry", 0},
+// { &key_memory_rpl_slave_check_temp_dir, "rpl_slave::check_temp_dir", 0},
+// { &key_memory_rpl_slave_command_buffer, "rpl_slave::command_buffer", 0},
+ { &key_memory_binlog_ver_1_event, "binlog_ver_1_event", 0},
+ { &key_memory_SLAVE_INFO, "SLAVE_INFO", 0},
+ { &key_memory_binlog_pos, "binlog_pos", 0},
+// { &key_memory_HASH_ROW_ENTRY, "HASH_ROW_ENTRY", 0},
+ { &key_memory_binlog_statement_buffer, "binlog_statement_buffer", 0},
+// { &key_memory_partition_syntax_buffer, "partition_syntax_buffer", 0},
+// { &key_memory_READ_INFO, "READ_INFO", 0},
+ { &key_memory_JOIN_CACHE, "JOIN_CACHE", 0},
+// { &key_memory_TABLE_sort_io_cache, "TABLE::sort_io_cache", 0},
+// { &key_memory_frm, "frm", 0},
+ { &key_memory_Unique_sort_buffer, "Unique::sort_buffer", 0},
+ { &key_memory_Unique_merge_buffer, "Unique::merge_buffer", 0},
+ { &key_memory_TABLE, "TABLE", PSI_FLAG_GLOBAL}, /* Table cache */
+// { &key_memory_frm_extra_segment_buff, "frm::extra_segment_buff", 0},
+// { &key_memory_frm_form_pos, "frm::form_pos", 0},
+ { &key_memory_frm_string, "frm::string", 0},
+// { &key_memory_LOG_name, "LOG_name", 0},
+ { &key_memory_DATE_TIME_FORMAT, "DATE_TIME_FORMAT", 0},
+ { &key_memory_DDL_LOG_MEMORY_ENTRY, "DDL_LOG_MEMORY_ENTRY", 0},
+ { &key_memory_ST_SCHEMA_TABLE, "ST_SCHEMA_TABLE", 0},
+ { &key_memory_ignored_db, "ignored_db", 0},
+ { &key_memory_PROFILE, "PROFILE", 0},
+ { &key_memory_global_system_variables, "global_system_variables", 0},
+ { &key_memory_THD_variables, "THD::variables", 0},
+// { &key_memory_Security_context, "Security_context", 0},
+// { &key_memory_shared_memory_name, "Shared_memory_name", 0},
+ { &key_memory_bison_stack, "bison_stack", 0},
+ { &key_memory_THD_handler_tables_hash, "THD::handler_tables_hash", 0},
+ { &key_memory_hash_index_key_buffer, "hash_index_key_buffer", 0},
+ { &key_memory_dboptions_hash, "dboptions_hash", 0},
+ { &key_memory_user_conn, "user_conn", 0},
+// { &key_memory_LOG_POS_COORD, "LOG_POS_COORD", 0},
+// { &key_memory_XID_STATE, "XID_STATE", 0},
+ { &key_memory_MPVIO_EXT_auth_info, "MPVIO_EXT::auth_info", 0},
+// { &key_memory_opt_bin_logname, "opt_bin_logname", 0},
+ { &key_memory_Query_cache, "Query_cache", PSI_FLAG_GLOBAL},
+// { &key_memory_READ_RECORD_cache, "READ_RECORD_cache", 0},
+// { &key_memory_Quick_ranges, "Quick_ranges", 0},
+// { &key_memory_File_query_log_name, "File_query_log::name", 0},
+ { &key_memory_Table_trigger_dispatcher, "Table_trigger_dispatcher::m_mem_root", 0},
+// { &key_memory_thd_timer, "thd_timer", 0},
+// { &key_memory_THD_Session_tracker, "THD::Session_tracker", 0},
+// { &key_memory_THD_Session_sysvar_resource_manager, "THD::Session_sysvar_resource_manager", 0},
+// { &key_memory_show_slave_status_io_gtid_set, "show_slave_status_io_gtid_set", 0},
+// { &key_memory_write_set_extraction, "write_set_extraction", 0},
+// { &key_memory_get_all_tables, "get_all_tables", 0},
+// { &key_memory_fill_schema_schemata, "fill_schema_schemata", 0},
+ { &key_memory_native_functions, "native_functions", PSI_FLAG_GLOBAL},
+};
+
/**
Initialise all the performance schema instrumentation points
used by the server.
@@ -9723,11 +9602,16 @@ void init_server_psi_keys(void)
count= array_elements(all_server_sockets);
mysql_socket_register(category, all_server_sockets, count);
+ count= array_elements(all_server_memory);
+ mysql_memory_register(category, all_server_memory, count);
+
#ifdef HAVE_PSI_STATEMENT_INTERFACE
init_sql_statement_info();
count= array_elements(sql_statement_info);
mysql_statement_register(category, sql_statement_info, count);
+ init_sp_psi_keys();
+
category= "com";
init_com_statement_info();
diff --git a/sql/mysqld.h b/sql/mysqld.h
index bd45ff7b798..e8dd5706e80 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2006, 2016, Oracle and/or its affiliates.
- Copyright (c) 2010, 2019, MariaDB Corporation.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
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
@@ -79,11 +79,8 @@ void close_connection(THD *thd, uint sql_errno= 0);
void handle_connection_in_main_thread(CONNECT *thd);
void create_thread_to_handle_connection(CONNECT *connect);
void unlink_thd(THD *thd);
-bool one_thread_per_connection_end(THD *thd, bool put_in_cache);
-void flush_thread_cache();
void refresh_status(THD *thd);
bool is_secure_file_path(char *path);
-void dec_connection_count(scheduler_functions *scheduler);
extern void init_net_server_extension(THD *thd);
extern void handle_accepted_socket(MYSQL_SOCKET new_sock, MYSQL_SOCKET sock);
extern void create_new_thread(CONNECT *connect);
@@ -118,8 +115,8 @@ extern bool opt_skip_name_resolve;
extern bool opt_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
extern my_bool debug_assert_on_not_freed_memory;
-extern bool volatile abort_loop;
-extern uint connection_count;
+extern MYSQL_PLUGIN_IMPORT bool volatile abort_loop;
+extern Atomic_counter<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;
@@ -134,6 +131,7 @@ extern my_bool read_only, opt_readonly;
extern MYSQL_PLUGIN_IMPORT my_bool lower_case_file_system;
extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
extern my_bool opt_secure_auth;
+extern my_bool opt_require_secure_transport;
extern const char *current_dbug_option;
extern char* opt_secure_file_priv;
extern char* opt_secure_backup_file_priv;
@@ -143,7 +141,7 @@ extern ulong use_stat_tables;
extern my_bool opt_old_style_user_limits, trust_function_creators;
extern uint opt_crash_binlog_innodb;
extern const char *shared_memory_base_name;
-extern char *mysqld_unix_port;
+extern MYSQL_PLUGIN_IMPORT char *mysqld_unix_port;
extern my_bool opt_enable_shared_memory;
extern ulong opt_replicate_events_marked_for_skip;
extern char *default_tz_name;
@@ -157,7 +155,6 @@ extern plugin_ref *opt_gtid_pos_auto_plugins;
extern bool opt_endinfo, using_udf_functions;
extern my_bool locked_in_memory;
extern bool opt_using_transactions;
-extern ulong max_long_data_size;
extern ulong current_pid;
extern ulong expire_logs_days;
extern my_bool relay_log_recovery;
@@ -169,7 +166,8 @@ extern my_bool relay_log_purge, opt_innodb_safe_binlog, opt_innodb;
extern my_bool relay_log_recovery;
extern uint select_errors,ha_open_options;
extern ulonglong test_flags;
-extern uint protocol_version, mysqld_port, dropping_tables;
+extern uint protocol_version, dropping_tables;
+extern MYSQL_PLUGIN_IMPORT uint mysqld_port;
extern ulong delay_key_write_options;
extern char *opt_logname, *opt_slow_logname, *opt_bin_logname,
*opt_relay_logname;
@@ -237,7 +235,6 @@ extern ulong slave_trans_retries;
extern ulong slave_trans_retry_interval;
extern uint slave_net_timeout;
extern int max_user_connections;
-extern volatile ulong cached_thread_count;
extern ulong what_to_log,flush_time;
extern uint max_prepared_stmt_count, prepared_stmt_count;
extern MYSQL_PLUGIN_IMPORT ulong open_files_limit;
@@ -246,6 +243,7 @@ extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
extern ulong max_binlog_size;
extern ulong slave_max_allowed_packet;
extern ulong opt_binlog_rows_event_max_size;
+extern ulong binlog_row_metadata;
extern ulong thread_cache_size;
extern ulong stored_program_cache_size;
extern ulong opt_slave_parallel_threads;
@@ -281,7 +279,8 @@ extern handlerton *heap_hton;
extern const char *load_default_groups[];
extern struct my_option my_long_options[];
int handle_early_options();
-extern int mysqld_server_started, mysqld_server_initialized;
+extern int MYSQL_PLUGIN_IMPORT mysqld_server_started;
+extern int 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;
@@ -305,7 +304,6 @@ extern my_bool disconnect_on_expired_password;
enum secure_timestamp { SECTIME_NO, SECTIME_SUPER, SECTIME_REPL, SECTIME_YES };
-#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_pending_checkpoint;
@@ -319,7 +317,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_BINLOG_LOCK_binlog_background_thread,
key_LOCK_binlog_end_pos,
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_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,
@@ -372,8 +370,7 @@ extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
- key_COND_start_thread,
- key_COND_thread_cache, key_COND_flush_thread_cache;
+ key_COND_start_thread;
extern PSI_cond_key key_RELAYLOG_COND_relay_log_updated,
key_RELAYLOG_COND_bin_log_updated, key_COND_wakeup_ready,
key_COND_wait_commit;
@@ -390,22 +387,153 @@ extern PSI_thread_key key_thread_delayed_insert,
key_thread_one_connection, key_thread_signal_hand,
key_thread_slave_background, key_rpl_parallel_thread;
-extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
+extern PSI_file_key key_file_binlog, key_file_binlog_cache,
+ key_file_binlog_index, key_file_binlog_index_cache, 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,
key_file_loadfile, key_file_log_event_data, key_file_log_event_info,
- key_file_master_info, key_file_misc, key_file_partition,
+ key_file_master_info, key_file_misc, key_file_partition_ddl_log,
key_file_pid, key_file_relay_log_info, key_file_send_file, key_file_tclog,
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_file_key key_file_relaylog, key_file_relaylog_index,
+ key_file_relaylog_cache, key_file_relaylog_index_cache;
extern PSI_socket_key key_socket_tcpip, key_socket_unix,
key_socket_client_connection;
extern PSI_file_key key_file_binlog_state;
+#ifdef HAVE_PSI_INTERFACE
void init_server_psi_keys();
#endif /* HAVE_PSI_INTERFACE */
+extern PSI_memory_key key_memory_locked_table_list;
+extern PSI_memory_key key_memory_locked_thread_list;
+extern PSI_memory_key key_memory_thd_transactions;
+extern PSI_memory_key key_memory_delegate;
+extern PSI_memory_key key_memory_acl_mem;
+extern PSI_memory_key key_memory_acl_memex;
+extern PSI_memory_key key_memory_acl_cache;
+extern PSI_memory_key key_memory_thd_main_mem_root;
+extern PSI_memory_key key_memory_help;
+extern PSI_memory_key key_memory_frm;
+extern PSI_memory_key key_memory_table_share;
+extern PSI_memory_key key_memory_gdl;
+extern PSI_memory_key key_memory_table_triggers_list;
+extern PSI_memory_key key_memory_prepared_statement_map;
+extern PSI_memory_key key_memory_prepared_statement_main_mem_root;
+extern PSI_memory_key key_memory_protocol_rset_root;
+extern PSI_memory_key key_memory_warning_info_warn_root;
+extern PSI_memory_key key_memory_sp_cache;
+extern PSI_memory_key key_memory_sp_head_main_root;
+extern PSI_memory_key key_memory_sp_head_execute_root;
+extern PSI_memory_key key_memory_sp_head_call_root;
+extern PSI_memory_key key_memory_table_mapping_root;
+extern PSI_memory_key key_memory_quick_range_select_root;
+extern PSI_memory_key key_memory_quick_index_merge_root;
+extern PSI_memory_key key_memory_quick_ror_intersect_select_root;
+extern PSI_memory_key key_memory_quick_ror_union_select_root;
+extern PSI_memory_key key_memory_quick_group_min_max_select_root;
+extern PSI_memory_key key_memory_test_quick_select_exec;
+extern PSI_memory_key key_memory_prune_partitions_exec;
+extern PSI_memory_key key_memory_binlog_recover_exec;
+extern PSI_memory_key key_memory_blob_mem_storage;
+
+extern PSI_memory_key key_memory_Sys_var_charptr_value;
+extern PSI_memory_key key_memory_THD_db;
+extern PSI_memory_key key_memory_user_var_entry;
+extern PSI_memory_key key_memory_user_var_entry_value;
+extern PSI_memory_key key_memory_Slave_job_group_group_relay_log_name;
+extern PSI_memory_key key_memory_Relay_log_info_group_relay_log_name;
+extern PSI_memory_key key_memory_binlog_cache_mngr;
+extern PSI_memory_key key_memory_Row_data_memory_memory;
+extern PSI_memory_key key_memory_errmsgs;
+extern PSI_memory_key key_memory_Event_queue_element_for_exec_names;
+extern PSI_memory_key key_memory_Event_scheduler_scheduler_param;
+extern PSI_memory_key key_memory_Gis_read_stream_err_msg;
+extern PSI_memory_key key_memory_Geometry_objects_data;
+extern PSI_memory_key key_memory_host_cache_hostname;
+extern PSI_memory_key key_memory_User_level_lock;
+extern PSI_memory_key key_memory_Filesort_info_record_pointers;
+extern PSI_memory_key key_memory_Sort_param_tmp_buffer;
+extern PSI_memory_key key_memory_Filesort_info_merge;
+extern PSI_memory_key key_memory_Filesort_buffer_sort_keys;
+extern PSI_memory_key key_memory_handler_errmsgs;
+extern PSI_memory_key key_memory_handlerton;
+extern PSI_memory_key key_memory_XID;
+extern PSI_memory_key key_memory_MYSQL_LOCK;
+extern PSI_memory_key key_memory_MYSQL_LOG_name;
+extern PSI_memory_key key_memory_TC_LOG_MMAP_pages;
+extern PSI_memory_key key_memory_my_str_malloc;
+extern PSI_memory_key key_memory_MYSQL_BIN_LOG_basename;
+extern PSI_memory_key key_memory_MYSQL_BIN_LOG_index;
+extern PSI_memory_key key_memory_MYSQL_RELAY_LOG_basename;
+extern PSI_memory_key key_memory_MYSQL_RELAY_LOG_index;
+extern PSI_memory_key key_memory_rpl_filter;
+extern PSI_memory_key key_memory_Security_context;
+extern PSI_memory_key key_memory_NET_buff;
+extern PSI_memory_key key_memory_NET_compress_packet;
+extern PSI_memory_key key_memory_my_bitmap_map;
+extern PSI_memory_key key_memory_QUICK_RANGE_SELECT_mrr_buf_desc;
+extern PSI_memory_key key_memory_TABLE_RULE_ENT;
+extern PSI_memory_key key_memory_Mutex_cond_array_Mutex_cond;
+extern PSI_memory_key key_memory_Owned_gtids_sidno_to_hash;
+extern PSI_memory_key key_memory_Sid_map_Node;
+extern PSI_memory_key key_memory_bison_stack;
+extern PSI_memory_key key_memory_TABLE_sort_io_cache;
+extern PSI_memory_key key_memory_DATE_TIME_FORMAT;
+extern PSI_memory_key key_memory_DDL_LOG_MEMORY_ENTRY;
+extern PSI_memory_key key_memory_ST_SCHEMA_TABLE;
+extern PSI_memory_key key_memory_ignored_db;
+extern PSI_memory_key key_memory_SLAVE_INFO;
+extern PSI_memory_key key_memory_log_event_old;
+extern PSI_memory_key key_memory_HASH_ROW_ENTRY;
+extern PSI_memory_key key_memory_table_def_memory;
+extern PSI_memory_key key_memory_MPVIO_EXT_auth_info;
+extern PSI_memory_key key_memory_LOG_POS_COORD;
+extern PSI_memory_key key_memory_XID_STATE;
+extern PSI_memory_key key_memory_Rpl_info_file_buffer;
+extern PSI_memory_key key_memory_Rpl_info_table;
+extern PSI_memory_key key_memory_binlog_pos;
+extern PSI_memory_key key_memory_db_worker_hash_entry;
+extern PSI_memory_key key_memory_rpl_slave_command_buffer;
+extern PSI_memory_key key_memory_binlog_ver_1_event;
+extern PSI_memory_key key_memory_rpl_slave_check_temp_dir;
+extern PSI_memory_key key_memory_TABLE;
+extern PSI_memory_key key_memory_binlog_statement_buffer;
+extern PSI_memory_key key_memory_user_conn;
+extern PSI_memory_key key_memory_dboptions_hash;
+extern PSI_memory_key key_memory_hash_index_key_buffer;
+extern PSI_memory_key key_memory_THD_handler_tables_hash;
+extern PSI_memory_key key_memory_JOIN_CACHE;
+extern PSI_memory_key key_memory_READ_INFO;
+extern PSI_memory_key key_memory_partition_syntax_buffer;
+extern PSI_memory_key key_memory_global_system_variables;
+extern PSI_memory_key key_memory_THD_variables;
+extern PSI_memory_key key_memory_PROFILE;
+extern PSI_memory_key key_memory_LOG_name;
+extern PSI_memory_key key_memory_string_iterator;
+extern PSI_memory_key key_memory_frm_extra_segment_buff;
+extern PSI_memory_key key_memory_frm_form_pos;
+extern PSI_memory_key key_memory_frm_string;
+extern PSI_memory_key key_memory_Unique_sort_buffer;
+extern PSI_memory_key key_memory_Unique_merge_buffer;
+extern PSI_memory_key key_memory_shared_memory_name;
+extern PSI_memory_key key_memory_opt_bin_logname;
+extern PSI_memory_key key_memory_Query_cache;
+extern PSI_memory_key key_memory_READ_RECORD_cache;
+extern PSI_memory_key key_memory_Quick_ranges;
+extern PSI_memory_key key_memory_File_query_log_name;
+extern PSI_memory_key key_memory_Table_trigger_dispatcher;
+extern PSI_memory_key key_memory_show_slave_status_io_gtid_set;
+extern PSI_memory_key key_memory_write_set_extraction;
+extern PSI_memory_key key_memory_thd_timer;
+extern PSI_memory_key key_memory_THD_Session_tracker;
+extern PSI_memory_key key_memory_THD_Session_sysvar_resource_manager;
+extern PSI_memory_key key_memory_get_all_tables;
+extern PSI_memory_key key_memory_fill_schema_schemata;
+extern PSI_memory_key key_memory_native_functions;
+extern PSI_memory_key key_memory_JSON;
+
/*
MAINTAINER: Please keep this list in order, to limit merge collisions.
Hint: grep PSI_stage_info | sort -u
@@ -525,7 +653,6 @@ 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_binlog_waiting_background_tasks;
extern PSI_stage_info stage_binlog_write;
extern PSI_stage_info stage_binlog_processing_checkpoint_notify;
@@ -544,6 +671,7 @@ extern PSI_stage_info stage_gtid_wait_other_connection;
extern PSI_stage_info stage_slave_background_process_request;
extern PSI_stage_info stage_slave_background_wait_request;
extern PSI_stage_info stage_waiting_for_deadlock_kill;
+extern PSI_stage_info stage_starting;
#ifdef HAVE_PSI_STATEMENT_INTERFACE
/**
@@ -575,8 +703,6 @@ extern pthread_t signal_thread;
extern struct st_VioSSLFd * ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */
-extern ulonglong my_pcre_frame_size;
-
/*
The following variables were under INNODB_COMPABILITY_HOOKS
*/
@@ -615,7 +741,7 @@ extern mysql_mutex_t
LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
LOCK_active_mi, LOCK_manager, LOCK_user_conn,
- LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
+ LOCK_prepared_stmt_count, LOCK_error_messages,
LOCK_slave_background;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_global_system_variables;
extern mysql_rwlock_t LOCK_all_status_vars;
@@ -624,8 +750,8 @@ extern mysql_mutex_t LOCK_start_thread;
extern char* des_key_file;
extern mysql_mutex_t LOCK_des_key_file;
#endif
-extern mysql_mutex_t LOCK_server_started;
-extern mysql_cond_t COND_server_started;
+extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_server_started;
+extern MYSQL_PLUGIN_IMPORT mysql_cond_t COND_server_started;
extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern mysql_rwlock_t LOCK_ssl_refresh;
extern mysql_prlock_t LOCK_system_variables_hash;
@@ -634,12 +760,11 @@ extern mysql_cond_t COND_manager;
extern mysql_cond_t COND_slave_background;
extern Atomic_counter<uint32_t> thread_count;
+extern my_bool opt_use_ssl;
extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
*opt_ssl_key, *opt_ssl_crl, *opt_ssl_crlpath;
extern ulonglong tls_version;
-extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD);
-
#ifdef MYSQL_SERVER
/**
@@ -656,7 +781,7 @@ enum options_mysqld
OPT_BOOTSTRAP,
OPT_CONSOLE,
OPT_DEBUG_SYNC_TIMEOUT,
- OPT_DEPRECATED_OPTION,
+ OPT_REMOVED_OPTION,
OPT_IGNORE_DB_DIRECTORY,
OPT_ISAM_LOG,
OPT_KEY_BUFFER_SIZE,
@@ -668,7 +793,6 @@ enum options_mysqld
OPT_LOG_BASENAME,
OPT_LOG_ERROR,
OPT_LOWER_CASE_TABLE_NAMES,
- OPT_MAX_LONG_DATA_SIZE,
OPT_PLUGIN_LOAD,
OPT_PLUGIN_LOAD_ADD,
OPT_PFS_INSTRUMENT,
@@ -747,6 +871,12 @@ enum enum_query_type
QT_ITEM_SUBSELECT_ID_ONLY,
QT_SHOW_SELECT_NUMBER= (1<<10),
+
+ /// Do not print database name or table name in the identifiers (even if
+ /// this means the printout will be ambigous). It is assumed that the caller
+ /// passing this flag knows what they are doing.
+ QT_ITEM_IDENT_DISABLE_DB_TABLE_NAMES= (1 <<11),
+
/// This is used for EXPLAIN EXTENDED extra warnings / Be more detailed
/// Be more detailed than QT_EXPLAIN.
/// Perhaps we should eventually include QT_ITEM_IDENT_SKIP_CURRENT_DATABASE
@@ -791,18 +921,13 @@ extern "C" void unireg_clear(int exit_code);
inline void table_case_convert(char * name, uint length)
{
if (lower_case_table_names)
- files_charset_info->cset->casedn(files_charset_info,
- name, length, name, length);
+ files_charset_info->casedn(name, length, name, length);
}
extern void set_server_version(char *buf, size_t size);
#define current_thd _current_thd()
-inline int set_current_thd(THD *thd)
-{
- return my_pthread_setspecific_ptr(THR_THD, thd);
-}
-
+void set_current_thd(THD *thd);
/*
@todo remove, make it static in ha_maria.cc
@@ -810,7 +935,6 @@ inline int set_current_thd(THD *thd)
*/
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;
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index b324f4a362f..a96c43a94fe 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -48,6 +48,9 @@
#include <debug_sync.h>
#include "proxy_protocol.h"
+PSI_memory_key key_memory_NET_buff;
+PSI_memory_key key_memory_NET_compress_packet;
+
#ifdef EMBEDDED_LIBRARY
#undef MYSQL_SERVER
#undef MYSQL_CLIENT
@@ -139,6 +142,8 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
DBUG_ENTER("my_net_init");
DBUG_PRINT("enter", ("my_flags: %u", my_flags));
net->vio = vio;
+ net->read_timeout= 0;
+ net->write_timeout= 0;
my_net_local_init(net); /* Set some limits */
if (net_allocate_new_packet(net, thd, my_flags))
@@ -177,8 +182,9 @@ my_bool my_net_init(NET *net, Vio *vio, void *thd, uint my_flags)
my_bool net_allocate_new_packet(NET *net, void *thd, uint my_flags)
{
DBUG_ENTER("net_allocate_new_packet");
- if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+
- NET_HEADER_SIZE + COMP_HEADER_SIZE +1,
+ if (!(net->buff=(uchar*) my_malloc(key_memory_NET_buff,
+ (size_t) net->max_packet +
+ NET_HEADER_SIZE + COMP_HEADER_SIZE + 1,
MYF(MY_WME | my_flags))))
DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet;
@@ -222,11 +228,11 @@ my_bool net_realloc(NET *net, size_t length)
my_real_read() may actually read 4 bytes depending on build flags and
platform.
*/
- if (!(buff= (uchar*) my_realloc((char*) net->buff, pkt_length +
+ if (!(buff= (uchar*) my_realloc(key_memory_NET_buff,
+ (char*) net->buff, pkt_length +
NET_HEADER_SIZE + COMP_HEADER_SIZE + 1,
- MYF(MY_WME |
- (net->thread_specific_malloc ?
- MY_THREAD_SPECIFIC : 0)))))
+ MYF(MY_WME | (net->thread_specific_malloc
+ ? MY_THREAD_SPECIFIC : 0)))))
{
/* @todo: 1 and 2 codes are identical. */
net->error= 1;
@@ -408,7 +414,6 @@ my_bool net_flush(NET *net)
my_bool my_net_write(NET *net, const uchar *packet, size_t len)
{
uchar buff[NET_HEADER_SIZE];
- int rc;
if (unlikely(!net->vio)) /* nowhere to write */
return 0;
@@ -445,7 +450,7 @@ my_bool 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= MY_TEST(net_write_buff(net, packet, len));
+ my_bool rc= MY_TEST(net_write_buff(net, packet, len));
MYSQL_NET_WRITE_DONE(rc);
return rc;
}
@@ -486,7 +491,7 @@ net_write_command(NET *net,uchar command,
size_t length=len+1+head_len; /* 1 extra byte for command */
uchar buff[NET_HEADER_SIZE+1];
uint header_size=NET_HEADER_SIZE+1;
- int rc;
+ my_bool rc;
DBUG_ENTER("net_write_command");
DBUG_PRINT("enter",("length: %lu", (ulong) len));
@@ -649,11 +654,10 @@ net_real_write(NET *net,const uchar *packet, size_t len)
size_t complen;
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 |
- (net->thread_specific_malloc ?
- MY_THREAD_SPECIFIC : 0)))))
+ if (!(b= (uchar*) my_malloc(key_memory_NET_compress_packet,
+ len + NET_HEADER_SIZE + 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;
diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc
index f8d11da1d5e..15bc2074e1f 100644
--- a/sql/opt_index_cond_pushdown.cc
+++ b/sql/opt_index_cond_pushdown.cc
@@ -206,7 +206,7 @@ static Item *make_cond_for_index(THD *thd, Item *cond, TABLE *table, uint keyno,
new_cond->argument_list()->push_back(fix, thd->mem_root);
used_tables|= fix->used_tables();
}
- if (MY_TEST(item->marker == ICP_COND_USES_INDEX_ONLY))
+ if (item->marker == ICP_COND_USES_INDEX_ONLY)
{
n_marked++;
item->marker= 0;
@@ -239,7 +239,7 @@ static Item *make_cond_for_index(THD *thd, Item *cond, TABLE *table, uint keyno,
if (!fix)
return (COND*) 0;
new_cond->argument_list()->push_back(fix, thd->mem_root);
- if (MY_TEST(item->marker == ICP_COND_USES_INDEX_ONLY))
+ if (item->marker == ICP_COND_USES_INDEX_ONLY)
{
n_marked++;
item->marker= 0;
@@ -353,13 +353,11 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
*/
if ((tab->table->file->index_flags(keyno, 0, 1) &
HA_DO_INDEX_COND_PUSHDOWN) &&
- optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
- tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
- tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI &&
- tab->type != JT_CONST && tab->type != JT_SYSTEM &&
- !(keyno == tab->table->s->primary_key && // (6)
- tab->table->file->primary_key_is_clustered())) // (6)
-
+ optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
+ tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
+ tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI &&
+ tab->type != JT_CONST && tab->type != JT_SYSTEM &&
+ !tab->table->file->is_clustering_key(keyno)) // 6
{
DBUG_EXECUTE("where",
print_where(tab->select_cond, "full cond", QT_ORDINARY););
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 2156b877ace..c13563f9263 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -277,7 +277,6 @@ public:
pointer to such SEL_TREE instead of NULL)
*/
Mem_root_array<SEL_ARG *, true> keys;
-
key_map keys_map; /* bitmask of non-NULL elements in keys */
/*
@@ -405,7 +404,7 @@ bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
uchar *max_key,uint max_key_flag);
static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
-static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
+SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
static bool null_part_in_key(KEY_PART *key_part, const uchar *key,
uint length);
static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
@@ -426,8 +425,9 @@ static bool sel_trees_must_be_ored(RANGE_OPT_PARAM* param,
static int and_range_trees(RANGE_OPT_PARAM *param,
SEL_TREE *tree1, SEL_TREE *tree2,
SEL_TREE *result);
-static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree);
-
+static bool remove_nonrange_trees(PARAM *param, SEL_TREE *tree);
+static void restore_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree,
+ SEL_ARG **backup);
static void print_key_value(String *out, const KEY_PART_INFO *key_part,
const uchar* key, uint length);
static void print_keyparts_name(String *out, const KEY_PART_INFO *key_part,
@@ -1274,9 +1274,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, "QUICK_RANGE_SELECT",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &alloc,
+ thd->variables.range_alloc_block_size, 0, MYF(MY_THREAD_SPECIFIC));
thd->mem_root= &alloc;
}
else
@@ -1284,7 +1283,7 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
file= head->file;
record= head->record[0];
- my_init_dynamic_array2(&ranges, sizeof(QUICK_RANGE*),
+ my_init_dynamic_array2(PSI_INSTRUMENT_ME, &ranges, sizeof(QUICK_RANGE*),
thd->alloc(sizeof(QUICK_RANGE*) * 16), 16, 16,
MYF(MY_THREAD_SPECIFIC));
@@ -1345,7 +1344,7 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
{
DBUG_PRINT("info", ("Freeing separate handler %p (free: %d)", file,
free_file));
- file->ha_external_lock(current_thd, F_UNLCK);
+ file->ha_external_unlock(current_thd);
file->ha_close();
delete file;
}
@@ -1370,9 +1369,8 @@ QUICK_INDEX_SORT_SELECT::QUICK_INDEX_SORT_SELECT(THD *thd_param, TABLE *table)
DBUG_ENTER("QUICK_INDEX_SORT_SELECT::QUICK_INDEX_SORT_SELECT");
index= MAX_KEY;
head= table;
- init_sql_alloc(&alloc, "QUICK_INDEX_SORT_SELECT",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &alloc,
+ thd->variables.range_alloc_block_size, 0, MYF(MY_THREAD_SPECIFIC));
DBUG_VOID_RETURN;
}
@@ -1393,8 +1391,7 @@ bool
QUICK_INDEX_SORT_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range)
{
DBUG_ENTER("QUICK_INDEX_SORT_SELECT::push_quick_back");
- if (head->file->primary_key_is_clustered() &&
- quick_sel_range->index == head->s->primary_key)
+ if (head->file->is_clustering_key(quick_sel_range->index))
{
/*
A quick_select over a clustered primary key is handled specifically
@@ -1442,9 +1439,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, "QUICK_ROR_INTERSECT_SELECT",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &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,
@@ -1538,7 +1534,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler,
if (init())
{
- file->ha_external_lock(thd, F_UNLCK);
+ file->ha_external_unlock(thd);
file->ha_close();
goto failure;
}
@@ -1567,7 +1563,7 @@ end:
{
if (!reuse_handler)
{
- file->ha_external_lock(thd, F_UNLCK);
+ file->ha_external_unlock(thd);
file->ha_close();
goto failure;
}
@@ -1720,9 +1716,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, "QUICK_ROR_UNION_SELECT",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &alloc,
+ thd->variables.range_alloc_block_size, 0, MYF(MY_THREAD_SPECIFIC));
thd_param->mem_root= &alloc;
}
@@ -2570,7 +2565,7 @@ static int fill_used_fields_bitmap(PARAM *param)
bitmap_union(&param->needed_fields, table->write_set);
pk= param->table->s->primary_key;
- if (pk != MAX_KEY && param->table->file->primary_key_is_clustered())
+ if (param->table->file->pk_is_clustering_key(pk))
{
/* The table uses clustered PK and it is not internally generated */
KEY_PART_INFO *key_part= param->table->key_info[pk].key_part;
@@ -2607,10 +2602,10 @@ static int fill_used_fields_bitmap(PARAM *param)
In the table struct the following information is updated:
quick_keys - Which keys can be used
quick_rows - How many rows the key matches
- quick_condition_rows - E(# rows that will satisfy the table condition)
+ opt_range_condition_rows - E(# rows that will satisfy the table condition)
IMPLEMENTATION
- quick_condition_rows value is obtained as follows:
+ opt_range_condition_rows value is obtained as follows:
It is a minimum of E(#output rows) for all considered table access
methods (range and index_merge accesses over various indexes).
@@ -2634,10 +2629,10 @@ static int fill_used_fields_bitmap(PARAM *param)
which is currently produced.
TODO
- * Change the value returned in quick_condition_rows from a pessimistic
+ * Change the value returned in opt_range_condition_rows from a pessimistic
estimate to true E(#rows that satisfy table condition).
- (we can re-use some of E(#rows) calcuation code from index_merge/intersection
- for this)
+ (we can re-use some of E(#rows) calcuation code from
+ index_merge/intersection for this)
* Check if this function really needs to modify keys_to_use, and change the
code to pass it by reference if it doesn't.
@@ -2661,6 +2656,9 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
uint idx;
double scan_time;
+ Item *notnull_cond= NULL;
+ TABLE_READ_PLAN *best_trp= NULL;
+ SEL_ARG **backup_keys= 0;
DBUG_ENTER("SQL_SELECT::test_quick_select");
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
(ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables,
@@ -2675,6 +2673,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (keys_to_use.is_clear_all() || head->is_filled_at_execution())
DBUG_RETURN(0);
records= head->stat_records();
+ notnull_cond= head->notnull_cond;
if (!records)
records++; /* purecov: inspected */
@@ -2682,12 +2681,21 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
scan_time= read_time= DBL_MAX;
else
{
- scan_time= (double) records / TIME_FOR_COMPARE + 1;
- read_time= (double) head->file->scan_time() + scan_time + 1.1;
- if (limit < records)
+ scan_time= rows2double(records) / TIME_FOR_COMPARE;
+ /*
+ The 2 is there to prefer range scans to full table scans.
+ This is mainly to make the test suite happy as many tests has
+ very few rows. In real life tables has more than a few rows and the
+ +2 has no practical effect.
+ */
+ read_time= (double) head->file->scan_time() + scan_time + 2;
+ if (limit < records && read_time < (double) records + scan_time + 1 )
+ {
read_time= (double) records + scan_time + 1; // Force to use index
+ notnull_cond= NULL;
+ }
}
-
+
possible_keys.clear_all();
DBUG_PRINT("info",("Time to scan table: %g", read_time));
@@ -2707,6 +2715,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uchar buff[STACK_BUFF_ALLOC];
MEM_ROOT alloc;
SEL_TREE *tree= NULL;
+ SEL_TREE *notnull_cond_tree= NULL;
KEY_PART *key_parts;
KEY *key_info;
PARAM param;
@@ -2735,9 +2744,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.possible_keys.clear_all();
thd->no_errors=1; // Don't warn about NULL
- init_sql_alloc(&alloc, "test_quick_select",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &alloc,
+ thd->variables.range_alloc_block_size, 0, MYF(MY_THREAD_SPECIFIC));
if (!(param.key_parts=
(KEY_PART*) alloc_root(&alloc,
sizeof(KEY_PART) *
@@ -2785,7 +2793,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
Json_writer_array trace_keypart(thd, "key_parts");
for (uint part= 0 ; part < n_key_parts ;
part++, key_parts++, key_part_info++)
- {
+ {
key_parts->key= param.keys;
key_parts->part= part;
key_parts->length= key_part_info->length;
@@ -2822,8 +2830,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (!force_quick_range && !head->covering_keys.is_clear_all())
{
int key_for_use= find_shortest_key(head, &head->covering_keys);
- double key_read_time= head->file->key_scan_time(key_for_use) +
- (double) records / TIME_FOR_COMPARE_IDX;
+ double key_read_time= (head->file->key_scan_time(key_for_use) +
+ rows2double(records) / TIME_FOR_COMPARE);
DBUG_PRINT("info", ("'all'+'using index' scan will be using key %d, "
"read time %g", key_for_use, key_read_time));
@@ -2840,16 +2848,20 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
trace_cov.add("cause", "cost");
}
- TABLE_READ_PLAN *best_trp= NULL;
- TRP_GROUP_MIN_MAX *group_trp= NULL;
double best_read_time= read_time;
- if (cond)
+ if (notnull_cond)
+ notnull_cond_tree= notnull_cond->get_mm_tree(&param, &notnull_cond);
+
+ if (cond || notnull_cond_tree)
{
{
Json_writer_array trace_range_summary(thd,
"setup_range_conditions");
- tree= cond->get_mm_tree(&param, &cond);
+ if (cond)
+ tree= cond->get_mm_tree(&param, &cond);
+ if (notnull_cond_tree)
+ tree= tree_and(&param, tree, notnull_cond_tree);
}
if (tree)
{
@@ -2879,36 +2891,6 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
}
- /*
- Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
- Notice that it can be constructed no matter if there is a range tree.
- */
- DBUG_EXECUTE_IF("force_group_by", force_group_by = true; );
- if (!only_single_index_range_scan)
- group_trp= get_best_group_min_max(&param, tree, best_read_time);
- if (group_trp)
- {
- param.table->quick_condition_rows= MY_MIN(group_trp->records,
- head->stat_records());
- Json_writer_object grp_summary(thd, "best_group_range_summary");
-
- if (unlikely(thd->trace_started()))
- group_trp->trace_basic_info(&param, &grp_summary);
-
- if (group_trp->read_cost < best_read_time || force_group_by)
- {
- grp_summary.add("chosen", true);
- best_trp= group_trp;
- best_read_time= best_trp->read_cost;
- if (force_group_by)
- {
- goto force_plan;
- }
- }
- else
- grp_summary.add("chosen", false).add("cause", "cost");
- }
-
if (tree)
{
/*
@@ -2920,6 +2902,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
bool can_build_covering= FALSE;
Json_writer_object trace_range(thd, "analyzing_range_alternatives");
+ backup_keys= (SEL_ARG**) alloca(sizeof(backup_keys[0])*param.keys);
+ memcpy(&backup_keys[0], &tree->keys[0],
+ sizeof(backup_keys[0])*param.keys);
+
remove_nonrange_trees(&param, tree);
/* Get best 'range' plan and prepare data for making other plans */
@@ -2974,7 +2960,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
best_trp= intersect_trp;
best_read_time= best_trp->read_cost;
- set_if_smaller(param.table->quick_condition_rows,
+ set_if_smaller(param.table->opt_range_condition_rows,
intersect_trp->records);
}
}
@@ -2994,7 +2980,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
new_conj_trp= get_best_disjunct_quick(&param, imerge, best_read_time);
if (new_conj_trp)
- set_if_smaller(param.table->quick_condition_rows,
+ set_if_smaller(param.table->opt_range_condition_rows,
new_conj_trp->records);
if (new_conj_trp &&
(!best_conj_trp ||
@@ -3009,7 +2995,38 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
}
-force_plan:
+ /*
+ Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
+ Notice that it can be constructed no matter if there is a range tree.
+ */
+ DBUG_EXECUTE_IF("force_group_by", force_group_by = true; );
+ if (!only_single_index_range_scan)
+ {
+ TRP_GROUP_MIN_MAX *group_trp;
+ if (tree)
+ restore_nonrange_trees(&param, tree, backup_keys);
+ if ((group_trp= get_best_group_min_max(&param, tree, read_time)))
+ {
+ param.table->opt_range_condition_rows= MY_MIN(group_trp->records,
+ head->stat_records());
+ Json_writer_object grp_summary(thd, "best_group_range_summary");
+
+ if (unlikely(thd->trace_started()))
+ group_trp->trace_basic_info(&param, &grp_summary);
+
+ if (group_trp->read_cost < best_read_time || force_group_by)
+ {
+ grp_summary.add("chosen", true);
+ best_trp= group_trp;
+ best_read_time= best_trp->read_cost;
+ }
+ else
+ grp_summary.add("chosen", false).add("cause", "cost");
+ }
+ if (tree)
+ remove_nonrange_trees(&param, tree);
+ }
+
thd->mem_root= param.old_root;
/* If we got a read plan, create a quick select from it. */
@@ -3326,8 +3343,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
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]);
+ if (table->opt_range_keys.is_set(keynr))
+ set_if_bigger(max_quick_key_parts, table->opt_range[keynr].key_parts);
}
/*
@@ -3339,13 +3356,13 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
{
for (keynr= 0; keynr < table->s->keys; keynr++)
{
- if (table->quick_keys.is_set(keynr) &&
- table->quick_key_parts[keynr] == quick_key_parts)
+ if (table->opt_range_keys.is_set(keynr) &&
+ table->opt_range[keynr].key_parts == quick_key_parts)
{
uint i;
- uint used_key_parts= table->quick_key_parts[keynr];
- double quick_cond_selectivity= table->quick_rows[keynr] /
- table_records;
+ uint used_key_parts= table->opt_range[keynr].key_parts;
+ double quick_cond_selectivity= (table->opt_range[keynr].rows /
+ table_records);
KEY *key_info= table->key_info + keynr;
KEY_PART_INFO* key_part= key_info->key_part;
/*
@@ -3437,9 +3454,8 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
SEL_TREE *tree;
double rows;
- init_sql_alloc(&alloc, "calculate_cond_selectivity_for_table",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &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;
@@ -3870,9 +3886,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, "prune_partitions",
- thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_quick_range_select_root, &alloc,
+ thd->variables.range_alloc_block_size, 0, MYF(MY_THREAD_SPECIFIC));
range_par->mem_root= &alloc;
range_par->old_root= thd->mem_root;
@@ -4932,16 +4947,16 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num)
double get_sweep_read_cost(const PARAM *param, ha_rows records)
{
double result;
+ uint pk= param->table->s->primary_key;
DBUG_ENTER("get_sweep_read_cost");
- if (param->table->file->primary_key_is_clustered() ||
+ if (param->table->file->pk_is_clustering_key(pk) ||
param->table->file->stats.block_size == 0 /* HEAP */)
{
/*
We are using the primary key to find the rows.
Calculate the cost for this.
*/
- result= param->table->file->read_time(param->table->s->primary_key,
- (uint)records, records);
+ result= param->table->file->read_time(pk, (uint)records, records);
}
else
{
@@ -5063,7 +5078,6 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
double imerge_cost= 0.0;
ha_rows cpk_scan_records= 0;
ha_rows non_cpk_scan_records= 0;
- bool pk_is_clustered= param->table->file->primary_key_is_clustered();
bool all_scans_ror_able= TRUE;
bool all_scans_rors= TRUE;
uint unique_calc_buff_size;
@@ -5135,9 +5149,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
imerge_cost += (*cur_child)->read_cost;
all_scans_ror_able &= ((*ptree)->n_ror_scans > 0);
all_scans_rors &= (*cur_child)->is_ror;
- if (pk_is_clustered &&
- param->real_keynr[(*cur_child)->key_idx] ==
- param->table->s->primary_key)
+ if (param->table->file->is_clustering_key(param->real_keynr[(*cur_child)->key_idx]))
{
cpk_scan= cur_child;
cpk_scan_records= (*cur_child)->records;
@@ -5190,8 +5202,8 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
Add one ROWID comparison for each row retrieved on non-CPK scan. (it
is done in QUICK_RANGE_SELECT::row_in_ranges)
*/
- double rid_comp_cost= static_cast<double>(non_cpk_scan_records) /
- TIME_FOR_COMPARE_ROWID;
+ double rid_comp_cost= (rows2double(non_cpk_scan_records) /
+ TIME_FOR_COMPARE_ROWID);
imerge_cost+= rid_comp_cost;
trace_best_disjunct.add("cost_of_mapping_rowid_in_non_clustered_pk_scan",
rid_comp_cost);
@@ -5500,7 +5512,7 @@ typedef struct st_common_index_intersect_info
{
PARAM *param; /* context info for range optimizations */
uint key_size; /* size of a ROWID element stored in Unique object */
- uint compare_factor; /* 1/compare - cost to compare two ROWIDs */
+ double compare_factor; /* 1/compare - cost to compare two ROWIDs */
size_t max_memory_size; /* maximum space allowed for Unique objects */
ha_rows table_cardinality; /* estimate of the number of records in table */
double cutoff_cost; /* discard index intersects with greater costs */
@@ -5722,14 +5734,14 @@ bool prepare_search_best_index_intersect(PARAM *param,
common->table_cardinality=
get_table_cardinality_for_index_intersect(table);
- if (table->file->primary_key_is_clustered())
+ if (table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX)
{
INDEX_SCAN_INFO **index_scan_end;
index_scan= tree->index_scans;
index_scan_end= index_scan+n_index_scans;
for ( ; index_scan < index_scan_end; index_scan++)
{
- if ((*index_scan)->keynr == table->s->primary_key)
+ if (table->file->is_clustering_key((*index_scan)->keynr))
{
common->cpk_scan= cpk_scan= *index_scan;
break;
@@ -5768,7 +5780,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
continue;
}
- cost= table->quick_index_only_costs[(*index_scan)->keynr];
+ cost= table->opt_range[(*index_scan)->keynr].index_only_cost;
idx_scan.add("cost", cost);
@@ -5796,7 +5808,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
{
idx_scan.add("chosen", true);
if (!*scan_ptr)
- idx_scan.add("cause", "first occurence of index prefix");
+ idx_scan.add("cause", "first occurrence of index prefix");
else
idx_scan.add("cause", "better cost for same idx prefix");
*scan_ptr= *index_scan;
@@ -6195,7 +6207,7 @@ bool check_index_intersect_extension(PARTIAL_INDEX_INTERSECT_INFO *curr,
{
uint *buff_elems= common_info->buff_elems;
uint key_size= common_info->key_size;
- uint compare_factor= common_info->compare_factor;
+ double compare_factor= common_info->compare_factor;
size_t max_memory_size= common_info->max_memory_size;
records_sent_to_unique+= ext_index_scan_records;
@@ -6764,6 +6776,7 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
{
/* create (part1val, ..., part{n-1}val) tuple. */
ha_rows records;
+ page_range pages;
if (!tuple_arg)
{
tuple_arg= scan->sel_arg;
@@ -6781,7 +6794,7 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
min_range.length= max_range.length= (uint) (key_ptr - key_val);
min_range.keypart_map= max_range.keypart_map= keypart_map;
records= (info->param->table->file->
- records_in_range(scan->keynr, &min_range, &max_range));
+ records_in_range(scan->keynr, &min_range, &max_range, &pages));
if (cur_covered)
{
/* uncovered -> covered */
@@ -7011,7 +7024,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
sizeof(ROR_SCAN_INFO*)*
param->keys)))
return NULL;
- cpk_no= ((param->table->file->primary_key_is_clustered()) ?
+ cpk_no= (param->table->file->
+ pk_is_clustering_key(param->table->s->primary_key) ?
param->table->s->primary_key : MAX_KEY);
for (idx= 0, cur_ror_scan= tree->ror_scans; idx < param->keys; idx++)
@@ -7177,7 +7191,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
ha_rows best_rows = double2rows(intersect_best->out_rows);
if (!best_rows)
best_rows= 1;
- set_if_smaller(param->table->quick_condition_rows, best_rows);
+ set_if_smaller(param->table->opt_range_condition_rows, best_rows);
trp->records= best_rows;
trp->index_scan_costs= intersect_best->index_scan_costs;
trp->cpk_scan= cpk_scan;
@@ -7346,7 +7360,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
trp->read_cost= total_cost;
trp->records= records;
trp->cpk_scan= NULL;
- set_if_smaller(param->table->quick_condition_rows, records);
+ set_if_smaller(param->table->opt_range_condition_rows, records);
DBUG_PRINT("info",
("Returning covering ROR-intersect plan: cost %g, records %lu",
@@ -8197,18 +8211,6 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param,
table_map ref_tables= 0;
table_map param_comp= ~(param->prev_tables | param->read_tables |
param->current_table);
-#ifdef HAVE_SPATIAL
- Field::geometry_type sav_geom_type= Field::GEOM_GEOMETRY, *geom_type=
- field_item->field->type() == MYSQL_TYPE_GEOMETRY
- ? &(static_cast<Field_geom*>(field_item->field))->geom_type
- : NULL;
- if (geom_type)
- {
- sav_geom_type= *geom_type;
- /* We have to be able to store all sorts of spatial features here */
- *geom_type= Field::GEOM_GEOMETRY;
- }
-#endif /*HAVE_SPATIAL*/
for (uint i= 0; i < arg_count; i++)
{
@@ -8236,12 +8238,6 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param,
}
}
-#ifdef HAVE_SPATIAL
- if (geom_type)
- {
- *geom_type= sav_geom_type;
- }
-#endif /*HAVE_SPATIAL*/
DBUG_RETURN(ftree);
}
@@ -8677,13 +8673,12 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param,
size_t min_length, max_length;
field_length-= maybe_null;
- if (my_like_range(field->charset(),
- res->ptr(), res->length(),
- escape, wild_one, wild_many,
- field_length,
- (char*) min_str + offset,
- (char*) max_str + offset,
- &min_length, &max_length))
+ if (field->charset()->like_range(res->ptr(), res->length(),
+ escape, wild_one, wild_many,
+ field_length,
+ (char*) min_str + offset,
+ (char*) max_str + offset,
+ &min_length, &max_length))
DBUG_RETURN(0); // Can't optimize with LIKE
if (offset != maybe_null) // BLOB or VARCHAR
@@ -9481,7 +9476,7 @@ bool sel_trees_must_be_ored(RANGE_OPT_PARAM* param,
1 No tree->keys[] left.
*/
-static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree)
+static bool remove_nonrange_trees(PARAM *param, SEL_TREE *tree)
{
bool res= FALSE;
for (uint i=0; i < param->keys; i++)
@@ -9491,6 +9486,8 @@ static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree)
if (tree->keys[i]->part)
{
tree->keys[i]= NULL;
+ /* Mark that records_in_range has not been called */
+ param->quick_rows[param->real_keynr[i]]= HA_POS_ERROR;
tree->keys_map.clear_bit(i);
}
else
@@ -9502,6 +9499,23 @@ static bool remove_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree)
/*
+ Restore nonrange trees to their previous state
+*/
+
+static void restore_nonrange_trees(RANGE_OPT_PARAM *param, SEL_TREE *tree,
+ SEL_ARG **backup_keys)
+{
+ for (uint i=0; i < param->keys; i++)
+ {
+ if (backup_keys[i])
+ {
+ tree->keys[i]= backup_keys[i];
+ tree->keys_map.set_bit(i);
+ }
+ }
+}
+
+/*
Build a SEL_TREE for a disjunction out of such trees for the disjuncts
SYNOPSIS
@@ -11073,11 +11087,11 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
about range scan we've evaluated.
mrr_flags INOUT MRR access flags
cost OUT Scan cost
+ is_ror_scan is set to reflect if the key scan is a ROR (see
+ is_key_scan_ror function for more info)
NOTES
- param->is_ror_scan is set to reflect if the key scan is a ROR (see
- is_key_scan_ror function for more info)
- param->table->quick_*, param->range_count (and maybe others) are
+ param->table->opt_range*, param->range_count (and maybe others) are
updated with data of given key scan, see quick_range_seq_next for details.
RETURN
@@ -11097,7 +11111,10 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
ha_rows rows= HA_POS_ERROR;
uint keynr= param->real_keynr[idx];
DBUG_ENTER("check_quick_select");
-
+
+ /* Range not calculated yet */
+ param->quick_rows[keynr]= HA_POS_ERROR;
+
/* Handle cases when we don't have a valid non-empty list of range */
if (!tree)
DBUG_RETURN(HA_POS_ERROR);
@@ -11124,7 +11141,6 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
*/
*mrr_flags|= HA_MRR_NO_ASSOCIATION | HA_MRR_SORTED;
- bool pk_is_clustered= file->primary_key_is_clustered();
// TODO: param->max_key_parts holds 0 now, and not the #keyparts used.
// Passing wrong second argument to index_flags() makes no difference for
// most storage engines but might be an issue for MyRocks with certain
@@ -11145,6 +11161,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
if (param->table->pos_in_table_list->is_non_derived())
rows= file->multi_range_read_info_const(keynr, &seq_if, (void*)&seq, 0,
bufsize, mrr_flags, cost);
+ param->quick_rows[keynr]= rows;
if (rows != HA_POS_ERROR)
{
ha_rows table_records= param->table->stat_records();
@@ -11152,28 +11169,31 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
{
/*
For any index the total number of records within all ranges
- cannot be be bigger than the number of records in the table
+ cannot be be bigger than the number of records in the table.
+ This check is needed as sometimes that table statistics or range
+ estimates may be slightly out of sync.
*/
rows= table_records;
set_if_bigger(rows, 1);
+ param->quick_rows[keynr]= rows;
}
- 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_parts;
- param->table->quick_n_ranges[keynr]= param->range_count;
- param->table->quick_condition_rows=
- MY_MIN(param->table->quick_condition_rows, rows);
- param->table->quick_rows[keynr]= rows;
- param->table->quick_costs[keynr]= cost->total_cost();
- if (keynr == param->table->s->primary_key && pk_is_clustered)
- param->table->quick_index_only_costs[keynr]= 0;
+ param->table->opt_range_keys.set_bit(keynr);
+ param->table->opt_range[keynr].key_parts= param->max_key_parts;
+ param->table->opt_range[keynr].ranges= param->range_count;
+ param->table->opt_range_condition_rows=
+ MY_MIN(param->table->opt_range_condition_rows, rows);
+ param->table->opt_range[keynr].rows= rows;
+ param->table->opt_range[keynr].cost= cost->total_cost();
+ if (param->table->file->is_clustering_key(keynr))
+ param->table->opt_range[keynr].index_only_cost= 0;
else
- param->table->quick_index_only_costs[keynr]= cost->index_only_cost();
+ param->table->opt_range[keynr].index_only_cost= cost->index_only_cost();
}
}
+
/* Figure out if the key scan is ROR (returns rows in ROWID order) or not */
enum ha_key_alg key_alg= param->table->key_info[seq.real_keyno].algorithm;
if ((key_alg != HA_KEY_ALG_BTREE) && (key_alg!= HA_KEY_ALG_UNDEF))
@@ -11185,7 +11205,7 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
*/
seq.is_ror_scan= FALSE;
}
- else if (param->table->s->primary_key == keynr && pk_is_clustered)
+ else if (param->table->file->is_clustering_key(keynr))
{
/* Clustered PK scan is always a ROR scan (TODO: same as above) */
seq.is_ror_scan= TRUE;
@@ -11270,7 +11290,7 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
key_part= table_key->key_part + nparts;
pk_number= param->table->s->primary_key;
- if (!param->table->file->primary_key_is_clustered() || pk_number == MAX_KEY)
+ if (!param->table->file->pk_is_clustering_key(pk_number))
return FALSE;
KEY_PART_INFO *pk_part= param->table->key_info[pk_number].key_part;
@@ -12171,7 +12191,8 @@ int QUICK_RANGE_SELECT::reset()
if (mrr_buf_size && !mrr_buf_desc)
{
buf_size= mrr_buf_size;
- while (buf_size && !my_multi_malloc(MYF(MY_WME),
+ while (buf_size && !my_multi_malloc(key_memory_QUICK_RANGE_SELECT_mrr_buf_desc,
+ MYF(MY_WME),
&mrr_buf_desc, sizeof(*mrr_buf_desc),
&mrange_buff, buf_size,
NullS))
@@ -13139,7 +13160,7 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
@param param Parameter from test_quick_select
@param sel_tree Range tree generated by get_mm_tree
- @param read_time Best read time so far (=table/index scan time)
+ @param read_time Best read time so far of table or index scan time
@return table read plan
@retval NULL Loose index scan not applicable or mem_root == NULL
@retval !NULL Loose index scan table read plan
@@ -13170,7 +13191,6 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
Item_field *item_field;
bool is_agg_distinct;
List<Item_field> agg_distinct_flds;
-
DBUG_ENTER("get_best_group_min_max");
Json_writer_object trace_group(thd, "group_index_range");
@@ -13195,8 +13215,6 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
DBUG_RETURN(NULL);
}
- /* Check (SA1,SA4) and store the only MIN/MAX argument - the C attribute.*/
- List_iterator<Item> select_items_it(join->fields_list);
is_agg_distinct = is_indexed_agg_distinct(join, &agg_distinct_flds);
if ((!join->group_list) && /* Neither GROUP BY nor a DISTINCT query. */
@@ -13208,6 +13226,9 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
}
/* Analyze the query in more detail. */
+ /* Check (SA1,SA4) and store the only MIN/MAX argument - the C attribute.*/
+ List_iterator<Item> select_items_it(join->fields_list);
+
if (join->sum_funcs[0])
{
Item_sum *min_max_item;
@@ -13635,25 +13656,18 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
/* Compute the cost of using this index. */
if (tree)
{
- cur_index_tree= tree->keys[cur_param_idx];
- /* Check if this range tree can be used for prefix retrieval. */
- Cost_estimate dummy_cost;
- uint mrr_flags= HA_MRR_USE_DEFAULT_IMPL;
- uint mrr_bufsize=0;
- bool is_ror_scan= FALSE;
- cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
- FALSE /*don't care*/,
- cur_index_tree, TRUE,
- &mrr_flags, &mrr_bufsize,
- &dummy_cost, &is_ror_scan);
- if (unlikely(cur_index_tree && thd->trace_started()))
+ if ((cur_index_tree= tree->keys[cur_param_idx]))
{
- Json_writer_array trace_range(thd, "ranges");
-
- const KEY_PART_INFO *key_part= cur_index_info->key_part;
- trace_ranges(&trace_range, param, cur_param_idx,
- cur_index_tree, key_part);
+ cur_quick_prefix_records= param->quick_rows[cur_index];
+ if (unlikely(cur_index_tree && thd->trace_started()))
+ {
+ Json_writer_array trace_range(thd, "ranges");
+ trace_ranges(&trace_range, param, cur_param_idx,
+ cur_index_tree, cur_index_info->key_part);
+ }
}
+ else
+ cur_quick_prefix_records= HA_POS_ERROR;
}
cost_group_min_max(table, cur_index_info, cur_used_key_parts,
cur_group_key_parts, tree, cur_index_tree,
@@ -13712,8 +13726,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
/*
Check (SA6) if clustered key is used
*/
- if (is_agg_distinct && index == table->s->primary_key &&
- table->file->primary_key_is_clustered())
+ if (is_agg_distinct && table->file->is_clustering_key(index))
{
trace_group.add("usable", false)
.add("cause", "index is clustered");
@@ -14061,7 +14074,7 @@ get_sel_arg_for_keypart(Field *field,
last_part [in] Last keypart of the index
thd [in] Current thread
key_infix [out] Infix of constants to be used for index lookup
- key_infix_len [out] Lenghth of the infix
+ key_infix_len [out] Length of the infix
first_non_infix_part [out] The first keypart after the infix (if any)
DESCRIPTION
@@ -14089,7 +14102,6 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
uchar *key_infix, uint *key_infix_len,
KEY_PART_INFO **first_non_infix_part)
{
- SEL_ARG *cur_range;
KEY_PART_INFO *cur_part;
/* End part for the first loop below. */
KEY_PART_INFO *end_part= min_max_arg_part ? min_max_arg_part : last_part;
@@ -14098,7 +14110,7 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
uchar *key_ptr= key_infix;
for (cur_part= first_non_group_part; cur_part != end_part; cur_part++)
{
- cur_range= NULL;
+ SEL_ARG *cur_range= NULL;
/*
Check NGA3:
1. get_sel_arg_for_keypart gets the range tree for the 'field' and also
@@ -14276,7 +14288,8 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
DBUG_ENTER("cost_group_min_max");
table_records= table->stat_records();
- keys_per_block= (uint) (table->file->stats.block_size / 2 /
+ /* Assume block is 75 % full */
+ keys_per_block= (uint) (table->file->stats.block_size * 3 / 4 /
(index_info->key_length + table->file->ref_length)
+ 1);
num_blocks= (ha_rows)(table_records / keys_per_block) + 1;
@@ -14342,20 +14355,21 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
b-tree the number of comparisons will be larger.
TODO: This cost should be provided by the storage engine.
*/
- const double tree_traversal_cost=
+ 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);
+ 1/(2*TIME_FOR_COMPARE);
const double cpu_cost= num_groups *
- (tree_traversal_cost + 1/double(TIME_FOR_COMPARE_IDX));
+ (tree_traversal_cost + 1/TIME_FOR_COMPARE_IDX);
*read_cost= io_cost + cpu_cost;
*records= num_groups;
DBUG_PRINT("info",
- ("table rows: %lu keys/block: %u keys/group: %lu result rows: %lu blocks: %lu",
- (ulong)table_records, keys_per_block, (ulong) keys_per_group,
+ ("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;
}
@@ -14523,10 +14537,10 @@ 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, "QUICK_GROUP_MIN_MAX_SELECT",
- join->thd->variables.range_alloc_block_size, 0,
- MYF(MY_THREAD_SPECIFIC));
- join->thd->mem_root= &alloc;
+ THD *thd= join->thd;
+ init_sql_alloc(key_memory_quick_range_select_root, &alloc,
+ thd->variables.range_alloc_block_size, 0, MYF(MY_THREAD_SPECIFIC));
+ thd->mem_root= &alloc;
}
else
bzero(&alloc, sizeof(MEM_ROOT)); // ensure that it's not used
@@ -14586,7 +14600,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(PSI_INSTRUMENT_ME, &min_max_ranges,
+ sizeof(QUICK_RANGE*), 16, 16,
MYF(MY_THREAD_SPECIFIC)))
return 1;
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 11d9f80865a..664e821f8d1 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -598,6 +598,7 @@ public:
SEL_ARG *clone_tree(RANGE_OPT_PARAM *param);
};
+extern MYSQL_PLUGIN_IMPORT SEL_ARG null_element;
class SEL_ARG_IMPOSSIBLE: public SEL_ARG
{
diff --git a/sql/opt_split.cc b/sql/opt_split.cc
index c70fac49930..a6031f75a5b 100644
--- a/sql/opt_split.cc
+++ b/sql/opt_split.cc
@@ -341,7 +341,7 @@ bool JOIN::check_for_splittable_materialized()
return false;
ORDER *ord;
- Dynamic_array<SplM_field_ext_info> candidates;
+ Dynamic_array<SplM_field_ext_info> candidates(PSI_INSTRUMENT_MEM);
/*
Select from partition_list all candidates for splitting.
@@ -712,7 +712,7 @@ void JOIN::add_keyuses_for_splitting()
KEY_FIELD *added_key_field;
if (!spl_opt_info->added_key_fields.elements)
goto err;
- if (!(ext_keyuses_for_splitting= new Dynamic_array<KEYUSE_EXT>))
+ if (!(ext_keyuses_for_splitting= new Dynamic_array<KEYUSE_EXT>(PSI_INSTRUMENT_MEM)))
goto err;
while ((added_key_field= li++))
{
@@ -742,13 +742,11 @@ void JOIN::add_keyuses_for_splitting()
save_query_plan(save_qep);
if (!keyuse.buffer &&
- my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64,
- MYF(MY_THREAD_SPECIFIC)))
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &keyuse, sizeof(KEYUSE),
+ 20, 64, MYF(MY_THREAD_SPECIFIC)))
goto err;
- if (allocate_dynamic(&keyuse,
- save_qep->keyuse.elements +
- added_keyuse_count))
+ if (allocate_dynamic(&keyuse, save_qep->keyuse.elements + added_keyuse_count))
goto err;
memcpy(keyuse.buffer,
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 62af68262ba..41dcf6442a1 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -592,10 +592,11 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
Item_subselect::subs_type substype= subselect->substype();
switch (substype) {
case Item_subselect::IN_SUBS:
- in_subs= (Item_in_subselect *)subselect;
+ in_subs= subselect->get_IN_subquery();
break;
case Item_subselect::ALL_SUBS:
case Item_subselect::ANY_SUBS:
+ DBUG_ASSERT(subselect->get_IN_subquery());
allany_subs= (Item_allany_subselect *)subselect;
break;
default:
@@ -640,13 +641,15 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
char const *save_where= thd->where;
thd->where= "IN/ALL/ANY subquery";
- bool failure= in_subs->left_expr->fix_fields_if_needed(thd,
- &in_subs->left_expr);
+ Item **left= in_subs->left_exp_ptr();
+ bool failure= (*left)->fix_fields_if_needed(thd, left);
thd->lex->current_select= current;
thd->where= save_where;
if (failure)
DBUG_RETURN(-1); /* purecov: deadcode */
+ // fix_field above can rewrite left expression
+ uint ncols= (*left)->cols();
/*
Check if the left and right expressions have the same # of
columns, i.e. we don't have a case like
@@ -655,9 +658,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
TODO why do we have this duplicated in IN->EXISTS transformers?
psergey-todo: fix these: grep for duplicated_subselect_card_check
*/
- if (select_lex->item_list.elements != in_subs->left_expr->cols())
+ if (select_lex->item_list.elements != ncols)
{
- my_error(ER_OPERAND_COLUMNS, MYF(0), in_subs->left_expr->cols());
+ my_error(ER_OPERAND_COLUMNS, MYF(0), ncols);
DBUG_RETURN(-1);
}
}
@@ -846,9 +849,10 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
static
bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
{
+ Item *left_exp= in_subs->left_exp();
DBUG_ENTER("subquery_types_allow_materialization");
- DBUG_ASSERT(in_subs->left_expr->is_fixed());
+ DBUG_ASSERT(left_exp->is_fixed());
List_iterator<Item> it(in_subs->unit->first_select()->item_list);
uint elements= in_subs->unit->first_select()->item_list.elements;
@@ -870,7 +874,7 @@ bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
uint32 total_key_length = 0;
for (uint i= 0; i < elements; i++)
{
- Item *outer= in_subs->left_expr->element_index(i);
+ Item *outer= left_exp->element_index(i);
Item *inner= it++;
all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM &&
inner->real_item()->type() == Item::FIELD_ITEM);
@@ -1419,9 +1423,10 @@ void get_delayed_table_estimates(TABLE *table,
/* Calculate cost of scanning the temptable */
double data_size= COST_MULT(item->jtbm_record_count,
hash_sj_engine->tmp_table->s->reclength);
- /* Do like in handler::read_time */
- *scan_time= data_size/IO_SIZE + 2;
-}
+ /* Do like in handler::scan_time() */
+ *scan_time= ((data_size/table->file->stats.block_size+2) *
+ table->file->avg_io_cost());
+}
/**
@@ -1704,7 +1709,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
sj_nest->alias= sj_nest_name;
sj_nest->sj_subq_pred= subq_pred;
sj_nest->original_subq_pred_used_tables= subq_pred->used_tables() |
- subq_pred->left_expr->used_tables();
+ subq_pred->left_exp()->used_tables();
/* Nests do not participate in those 'chains', so: */
/* sj_nest->next_leaf= sj_nest->next_local= sj_nest->next_global == NULL*/
emb_join_list->push_back(sj_nest, thd->mem_root);
@@ -1792,14 +1797,17 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
*/
SELECT_LEX *save_lex= thd->lex->current_select;
thd->lex->current_select=subq_lex;
- if (subq_pred->left_expr->fix_fields_if_needed(thd, &subq_pred->left_expr))
+ Item **left= subq_pred->left_exp_ptr();
+ if ((*left)->fix_fields_if_needed(thd, left))
DBUG_RETURN(TRUE);
+ Item *left_exp= *left;
+ Item *left_exp_orig= subq_pred->left_exp_orig();
thd->lex->current_select=save_lex;
table_map subq_pred_used_tables= subq_pred->used_tables();
sj_nest->nested_join->sj_corr_tables= subq_pred_used_tables;
sj_nest->nested_join->sj_depends_on= subq_pred_used_tables |
- subq_pred->left_expr->used_tables();
+ left_exp->used_tables();
sj_nest->sj_on_expr= subq_lex->join->conds;
/*
@@ -1817,14 +1825,14 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
Item_direct_view_refs doesn't substitute itself with anything in
Item_direct_view_ref::fix_fields.
*/
- sj_nest->sj_in_exprs= subq_pred->left_expr->cols();
+ uint ncols= sj_nest->sj_in_exprs= left_exp->cols();
sj_nest->nested_join->sj_outer_expr_list.empty();
reset_equality_number_for_subq_conds(sj_nest->sj_on_expr);
- if (subq_pred->left_expr->cols() == 1)
+ if (ncols == 1)
{
/* add left = select_list_element */
- nested_join->sj_outer_expr_list.push_back(&subq_pred->left_expr,
+ nested_join->sj_outer_expr_list.push_back(left,
thd->mem_root);
/*
Create Item_func_eq. Note that
@@ -1836,36 +1844,36 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
with thd->change_item_tree
*/
Item_func_eq *item_eq=
- new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr_orig,
+ new (thd->mem_root) Item_func_eq(thd, left_exp_orig,
subq_lex->ref_pointer_array[0]);
if (!item_eq)
DBUG_RETURN(TRUE);
- if (subq_pred->left_expr_orig != subq_pred->left_expr)
- thd->change_item_tree(item_eq->arguments(), subq_pred->left_expr);
+ if (left_exp_orig != left_exp)
+ thd->change_item_tree(item_eq->arguments(), left_exp);
item_eq->in_equality_no= 0;
sj_nest->sj_on_expr= and_items(thd, sj_nest->sj_on_expr, item_eq);
}
- else if (subq_pred->left_expr->type() == Item::ROW_ITEM)
+ else if (left_exp->type() == Item::ROW_ITEM)
{
/*
disassemple left expression and add
left1 = select_list_element1 and left2 = select_list_element2 ...
*/
- for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
+ for (uint i= 0; i < ncols; i++)
{
- nested_join->sj_outer_expr_list.push_back(subq_pred->left_expr->addr(i),
+ nested_join->sj_outer_expr_list.push_back(left_exp->addr(i),
thd->mem_root);
Item_func_eq *item_eq=
new (thd->mem_root)
- Item_func_eq(thd, subq_pred->left_expr_orig->element_index(i),
+ Item_func_eq(thd, left_exp_orig->element_index(i),
subq_lex->ref_pointer_array[i]);
if (!item_eq)
DBUG_RETURN(TRUE);
- DBUG_ASSERT(subq_pred->left_expr->element_index(i)->is_fixed());
- if (subq_pred->left_expr_orig->element_index(i) !=
- subq_pred->left_expr->element_index(i))
+ DBUG_ASSERT(left_exp->element_index(i)->is_fixed());
+ if (left_exp_orig->element_index(i) !=
+ left_exp->element_index(i))
thd->change_item_tree(item_eq->arguments(),
- subq_pred->left_expr->element_index(i));
+ left_exp->element_index(i));
item_eq->in_equality_no= i;
sj_nest->sj_on_expr= and_items(thd, sj_nest->sj_on_expr, item_eq);
}
@@ -1880,10 +1888,10 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
/* fix fields on subquery was call so they should be the same */
if (!row)
DBUG_RETURN(TRUE);
- DBUG_ASSERT(subq_pred->left_expr->cols() == row->cols());
- nested_join->sj_outer_expr_list.push_back(&subq_pred->left_expr);
+ DBUG_ASSERT(ncols == row->cols());
+ nested_join->sj_outer_expr_list.push_back(left);
Item_func_eq *item_eq=
- new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr_orig, row);
+ new (thd->mem_root) Item_func_eq(thd, left_exp_orig, row);
if (!item_eq)
DBUG_RETURN(TRUE);
for (uint i= 0; i < row->cols(); i++)
@@ -2203,7 +2211,7 @@ int pull_out_semijoin_tables(JOIN *join)
/*
Don't do table pull-out for nested joins (if we get nested joins here, it
means these are outer joins. It is theoretically possible to do pull-out
- for some of the outer tables but we dont support this currently.
+ for some of the outer tables but we don't support this currently.
*/
bool have_join_nest_children= FALSE;
@@ -2498,7 +2506,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= COST_MULT(rows,
- join->map2table[tableno]->table->quick_condition_rows);
+ join->map2table[tableno]->table->opt_range_condition_rows);
sjm->rows= MY_MIN(sjm->rows, rows);
}
memcpy((uchar*) sjm->positions,
@@ -3138,7 +3146,7 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
prefix_rec_count= COST_MULT(prefix_rec_count, curpos.records_read);
prefix_cost= COST_ADD(prefix_cost, curpos.read_time);
prefix_cost= COST_ADD(prefix_cost,
- prefix_rec_count / (double) TIME_FOR_COMPARE);
+ prefix_rec_count / TIME_FOR_COMPARE);
//TODO: take into account join condition selectivity here
}
@@ -3961,6 +3969,39 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
/*
+ Return the number of tables at the top-level of the JOIN
+
+ SYNOPSIS
+ get_number_of_tables_at_top_level()
+ join The join with the picked join order
+
+ DESCRIPTION
+ The number of tables in the JOIN currently include all the inner tables of the
+ mergeable semi-joins. The function would make sure that we only count the semi-join
+ nest and not the inner tables of teh semi-join nest.
+*/
+
+uint get_number_of_tables_at_top_level(JOIN *join)
+{
+ uint j= 0, tables= 0;
+ while(j < join->table_count)
+ {
+ POSITION *cur_pos= &join->best_positions[j];
+ tables++;
+ if (cur_pos->sj_strategy == SJ_OPT_MATERIALIZE ||
+ cur_pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
+ {
+ SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info;
+ j= j + sjm->tables;
+ }
+ else
+ j++;
+ }
+ return tables;
+}
+
+
+/*
Setup semi-join materialization strategy for one semi-join nest
SYNOPSIS
@@ -4100,7 +4141,8 @@ 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);
+ tab_ref->items[i]=
+ emb_sj_nest->sj_subq_pred->left_exp()->element_index(i);
int null_count= MY_TEST(cur_key_part->field->real_maybe_null());
*ref_key= new store_key_item(thd, cur_key_part->field,
/* TODO:
@@ -4285,18 +4327,20 @@ static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm,
Item_in_subselect *subq_pred)
{
Item *res= NULL;
- if (subq_pred->left_expr->cols() == 1)
+ Item *left_exp= subq_pred->left_exp();
+ uint ncols= left_exp->cols();
+ if (ncols == 1)
{
- if (!(res= new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr,
+ if (!(res= new (thd->mem_root) Item_func_eq(thd, left_exp,
new (thd->mem_root) Item_field(thd, sjm->table->field[0]))))
return NULL; /* purecov: inspected */
}
else
{
Item *conj;
- for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
+ for (uint i= 0; i < ncols; i++)
{
- if (!(conj= new (thd->mem_root) Item_func_eq(thd, subq_pred->left_expr->element_index(i),
+ if (!(conj= new (thd->mem_root) Item_func_eq(thd, left_exp->element_index(i),
new (thd->mem_root) Item_field(thd, sjm->table->field[i]))) ||
!(res= and_items(thd, res, conj)))
return NULL; /* purecov: inspected */
@@ -4419,12 +4463,12 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
temp_pool_slot = bitmap_lock_set_next(&temp_pool);
if (temp_pool_slot != MY_BIT_NONE) // we got a slot
- sprintf(path, "%s_%lx_%i", tmp_file_prefix,
+ sprintf(path, "%s-subquery-%lx-%i", tmp_file_prefix,
current_pid, temp_pool_slot);
else
{
/* if we run out of slots or we are not using tempool */
- sprintf(path,"%s%lx_%lx_%x", tmp_file_prefix,current_pid,
+ sprintf(path,"%s-subquery-%lx-%lx-%x", tmp_file_prefix,current_pid,
(ulong) thd->thread_id, thd->tmp_table++);
}
fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME);
@@ -4435,8 +4479,8 @@ 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, "SJ_TMP_TABLE",
- TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(PSI_INSTRUMENT_ME, &own_root, TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
&share, sizeof(*share),
@@ -4515,7 +4559,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
}
uint reclength= field->pack_length();
- if (using_unique_constraint)
+ if (using_unique_constraint || thd->variables.tmp_memory_table_size == 0)
{
share->db_plugin= ha_lock_engine(0, TMP_ENGINE_HTON);
table->file= get_new_handler(share, &table->mem_root,
@@ -4590,15 +4634,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
/* Make entry for create table */
recinfo->length=length;
- if (field->flags & BLOB_FLAG)
- recinfo->type= FIELD_BLOB;
- else if (use_packed_rows &&
- field->real_type() == MYSQL_TYPE_STRING &&
- length >= MIN_STRING_LENGTH_TO_PACK_ROWS)
- recinfo->type=FIELD_SKIP_ENDSPACE;
- else
- recinfo->type=FIELD_NORMAL;
-
+ recinfo->type= field->tmp_engine_column_type(use_packed_rows);
field->set_table_name(&table->alias);
}
@@ -4608,7 +4644,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
share->max_rows= (ha_rows) (((share->db_type() == heap_hton) ?
MY_MIN(thd->variables.tmp_memory_table_size,
thd->variables.max_heap_table_size) :
- thd->variables.tmp_memory_table_size) /
+ thd->variables.tmp_disk_table_size) /
share->reclength);
set_if_bigger(share->max_rows,1); // For dummy start options
@@ -5372,7 +5408,7 @@ int rewrite_to_index_subquery_engine(JOIN *join)
change_engine(new
subselect_uniquesubquery_engine(thd,
join_tab,
- unit->item,
+ unit->item->get_IN_subquery(),
where)));
}
else if (join_tab[0].type == JT_REF &&
@@ -5386,7 +5422,7 @@ int rewrite_to_index_subquery_engine(JOIN *join)
change_engine(new
subselect_indexsubquery_engine(thd,
join_tab,
- unit->item,
+ unit->item->get_IN_subquery(),
where,
NULL,
0)));
@@ -5402,7 +5438,7 @@ int rewrite_to_index_subquery_engine(JOIN *join)
DBUG_RETURN(unit->item->
change_engine(new subselect_indexsubquery_engine(thd,
join_tab,
- unit->item,
+ unit->item->get_IN_subquery(),
join->conds,
join->having,
1)));
@@ -5715,12 +5751,6 @@ int select_value_catcher::send_data(List<Item> &items)
DBUG_ASSERT(!assigned);
DBUG_ASSERT(items.elements == n_elements);
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
-
Item *val_item;
List_iterator_fast<Item> li(items);
for (uint i= 0; (val_item= li++); i++)
@@ -6083,11 +6113,13 @@ bool execute_degenerate_jtbm_semi_join(THD *thd,
subq_pred->jtbm_const_row_found= TRUE;
Item *eq_cond;
- for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
+ Item *left_exp= subq_pred->left_exp();
+ uint ncols= left_exp->cols();
+ for (uint i= 0; i < ncols; i++)
{
eq_cond=
new (thd->mem_root) Item_func_eq(thd,
- subq_pred->left_expr->element_index(i),
+ left_exp->element_index(i),
new_sink->row[i]);
if (!eq_cond || eq_cond->fix_fields(thd, NULL) ||
eq_list.push_back(eq_cond, thd->mem_root))
@@ -6382,7 +6414,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
if (is_in_subquery())
{
- in_subs= (Item_in_subselect*) unit->item;
+ in_subs= unit->item->get_IN_subquery();
if (in_subs->create_in_to_exists_cond(this))
return true;
}
@@ -6575,7 +6607,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
Set the limit of this JOIN object as well, because normally its being
set in the beginning of JOIN::optimize, which was already done.
*/
- select_limit= in_subs->unit->select_limit_cnt;
+ select_limit= in_subs->unit->lim.get_select_limit();
}
else if (in_subs->test_strategy(SUBS_IN_TO_EXISTS))
{
@@ -6666,12 +6698,12 @@ bool JOIN::choose_tableless_subquery_plan()
everything as-is, setup_jtbm_semi_joins() has special handling for cases
like this.
*/
- if (subs_predicate->is_in_predicate() &&
- !(subs_predicate->substype() == Item_subselect::IN_SUBS &&
- ((Item_in_subselect*)subs_predicate)->is_jtbm_merged))
+ Item_in_subselect *in_subs;
+ in_subs= subs_predicate->get_IN_subquery();
+ if (in_subs &&
+ !(subs_predicate->substype() == Item_subselect::IN_SUBS &&
+ in_subs->is_jtbm_merged))
{
- Item_in_subselect *in_subs;
- in_subs= (Item_in_subselect*) subs_predicate;
in_subs->set_strategy(SUBS_IN_TO_EXISTS);
if (in_subs->create_in_to_exists_cond(this) ||
in_subs->inject_in_to_exists_cond(this))
@@ -6688,7 +6720,8 @@ bool Item::pushable_equality_checker_for_subquery(uchar *arg)
{
return
get_corresponding_field_pair(this,
- ((Item_in_subselect *)arg)->corresponding_fields);
+ ((Item *)arg)->get_IN_subquery()->
+ corresponding_fields);
}
@@ -6827,7 +6860,7 @@ Item *get_corresponding_item(THD *thd, Item *item,
Item *Item_field::in_subq_field_transformer_for_where(THD *thd, uchar *arg)
{
- Item_in_subselect *subq_pred= (Item_in_subselect *)arg;
+ Item_in_subselect *subq_pred= ((Item *)arg)->get_IN_subquery();
Item *producing_item= get_corresponding_item(thd, this, subq_pred);
if (producing_item)
return producing_item->build_clone(thd);
@@ -6840,7 +6873,7 @@ Item *Item_direct_view_ref::in_subq_field_transformer_for_where(THD *thd,
{
if (item_equal)
{
- Item_in_subselect *subq_pred= (Item_in_subselect *)arg;
+ Item_in_subselect *subq_pred= ((Item *)arg)->get_IN_subquery();
Item *producing_item= get_corresponding_item(thd, this, subq_pred);
DBUG_ASSERT (producing_item != NULL);
return producing_item->build_clone(thd);
@@ -6879,8 +6912,7 @@ get_corresponding_item_for_in_subq_having(THD *thd, Item *in_item,
Item_ref *ref=
new (thd->mem_root) Item_ref(thd,
&subq_pred->unit->first_select()->context,
- NullS, NullS,
- &new_item->name);
+ new_item->name);
if (!ref)
DBUG_ASSERT(0);
return ref;
@@ -6891,6 +6923,7 @@ get_corresponding_item_for_in_subq_having(THD *thd, Item *in_item,
Item *Item_field::in_subq_field_transformer_for_having(THD *thd, uchar *arg)
{
+ DBUG_ASSERT(((Item *)arg)->get_IN_subquery());
return get_corresponding_item_for_in_subq_having(thd, this,
(Item_in_subselect *)arg);
}
@@ -6903,6 +6936,7 @@ Item *Item_direct_view_ref::in_subq_field_transformer_for_having(THD *thd,
return this;
else
{
+ DBUG_ASSERT(((Item *)arg)->get_IN_subquery());
Item *new_item= get_corresponding_item_for_in_subq_having(thd, this,
(Item_in_subselect *)arg);
if (!new_item)
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index d7978e9ef73..abd37f1e98e 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -324,6 +324,7 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab);
bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab);
+uint get_number_of_tables_at_top_level(JOIN *join);
/*
diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc
index 9957e524e90..ddec6d5ed2d 100644
--- a/sql/opt_trace.cc
+++ b/sql/opt_trace.cc
@@ -17,7 +17,7 @@
#include "sql_class.h"
#include "sql_show.h"
#include "field.h"
-#include "table.h"
+#include "sql_i_s.h"
#include "opt_trace.h"
#include "sql_parse.h"
#include "set_var.h"
@@ -68,18 +68,21 @@ bool sets_var_optimizer_trace(enum enum_sql_command sql_command,
}
+namespace Show {
+
+
ST_FIELD_INFO optimizer_trace_info[]=
{
- /* name, length, type, value, maybe_null, old_name, open_method */
- {"QUERY", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
- {"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
- {"MISSING_BYTES_BEYOND_MAX_MEM_SIZE", 20, MYSQL_TYPE_LONG, 0, false, NULL,
- SKIP_OPEN_TABLE},
- {"INSUFFICIENT_PRIVILEGES", 1, MYSQL_TYPE_TINY, 0, false, NULL,
- SKIP_OPEN_TABLE},
- {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
+ Column("QUERY", Longtext(65535), NOT_NULL),
+ Column("TRACE", Longtext(65535), NOT_NULL),
+ Column("MISSING_BYTES_BEYOND_MAX_MEM_SIZE", SLong(20), NOT_NULL),
+ Column("INSUFFICIENT_PRIVILEGES", STiny(1), NOT_NULL),
+ CEnd()
};
+} // namespace Show
+
+
/*
TODO: one-line needs to be implemented seperately
*/
@@ -260,7 +263,7 @@ void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl)
bool rc =
check_table_access(thd, SELECT_ACL, t, false, 1, true) || // (1)
- ((t->grant.privilege & SELECT_ACL) == 0); // (2)
+ ((t->grant.privilege & SELECT_ACL) == NO_ACL); // (2)
if (t->is_view())
{
/*
@@ -414,7 +417,7 @@ bool Opt_trace_context::is_enabled()
return false;
}
-Opt_trace_context::Opt_trace_context()
+Opt_trace_context::Opt_trace_context() : traces(PSI_INSTRUMENT_MEM)
{
current_trace= NULL;
max_mem_size= 0;
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index 9f08964e62c..8ad14ca260c 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2006, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, MariaDB Corporation.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
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,11 +30,11 @@
// NOT_A_PARTITION_ID
#include "partition_info.h"
#include "sql_parse.h"
-#include "sql_acl.h" // *_ACL
#include "sql_base.h" // fill_record
-#include "sql_statistics.h" // vers_stat_end
-#include "vers_utils.h"
#include "lock.h"
+#include "table.h"
+#include "sql_class.h"
+#include "vers_string.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -196,49 +196,6 @@ bool partition_info::set_named_partition_bitmap(const char *part_name, size_t le
}
-
-/**
- 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 false Success
- @retval true Failure
-*/
-bool partition_info::set_read_partitions(List<char> *partition_names)
-{
- DBUG_ENTER("partition_info::set_read_partitions");
- if (!partition_names || !partition_names->elements)
- {
- DBUG_RETURN(true);
- }
-
- uint num_names= partition_names->elements;
- List_iterator<char> partition_names_it(*partition_names);
- uint i= 0;
- /*
- 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
- {
- char *part_name= partition_names_it++;
- if (add_named_partition(part_name, strlen(part_name)))
- DBUG_RETURN(true);
- } while (++i < num_names);
- DBUG_RETURN(false);
-}
-
-
-
/**
Prune away partitions not mentioned in the PARTITION () clause,
if used.
@@ -379,7 +336,8 @@ char *partition_info::create_default_partition_names(THD *thd, uint part_no,
{
do
{
- sprintf(move_ptr, "p%u", (start_no + i));
+ if (make_partition_name(move_ptr, (start_no + i)))
+ DBUG_RETURN(NULL);
move_ptr+= MAX_PART_NAME_SIZE;
} while (++i < num_parts_arg);
}
@@ -446,7 +404,13 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file,
bool result= TRUE;
DBUG_ENTER("partition_info::set_up_default_partitions");
- if (part_type != HASH_PARTITION)
+ if (part_type == VERSIONING_PARTITION)
+ {
+ if (start_no == 0 && use_default_num_partitions)
+ num_parts= 2;
+ use_default_num_partitions= false;
+ }
+ else if (part_type != HASH_PARTITION)
{
const char *error_string;
if (part_type == RANGE_PARTITION)
@@ -484,7 +448,17 @@ bool partition_info::set_up_default_partitions(THD *thd, handler *file,
{
part_elem->engine_type= default_engine_type;
part_elem->partition_name= default_name;
+ part_elem->id= i;
default_name+=MAX_PART_NAME_SIZE;
+ if (part_type == VERSIONING_PARTITION)
+ {
+ if (start_no > 0 || i < num_parts - 1) {
+ part_elem->type= partition_element::HISTORY;
+ } else {
+ part_elem->type= partition_element::CURRENT;
+ part_elem->partition_name= "pn";
+ }
+ }
}
else
goto end;
@@ -589,8 +563,9 @@ bool partition_info::set_up_defaults_for_partitioning(THD *thd, handler *file,
if (!default_partitions_setup)
{
default_partitions_setup= TRUE;
- if (use_default_partitions)
- DBUG_RETURN(set_up_default_partitions(thd, file, info, start_no));
+ if (use_default_partitions &&
+ set_up_default_partitions(thd, file, info, start_no))
+ DBUG_RETURN(TRUE);
if (is_sub_partitioned() &&
use_default_subpartitions)
DBUG_RETURN(set_up_default_subpartitions(thd, file, info));
@@ -758,7 +733,7 @@ char *partition_info::find_duplicate_name()
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,
+ if (my_hash_init(PSI_INSTRUMENT_ME, &partition_names, system_charset_info, max_names, 0, 0,
(my_hash_get_key) get_part_name_from_elem, 0, HASH_UNIQUE))
{
DBUG_ASSERT(0);
@@ -832,6 +807,15 @@ bool partition_info::has_unique_name(partition_element *element)
DBUG_RETURN(TRUE);
}
+
+/**
+ @brief Switch history partition according limit or interval
+
+ @note
+ vers_info->limit Limit by number of partition records
+ vers_info->interval Limit by fixed time interval
+ vers_info->hist_part (out) Working history partition
+*/
void partition_info::vers_set_hist_part(THD *thd)
{
if (vers_info->limit)
@@ -850,11 +834,16 @@ void partition_info::vers_set_hist_part(THD *thd)
vers_info->hist_part= next;
records= next_records;
}
- if (records > vers_info->limit)
+ if (records >= vers_info->limit)
{
if (next == vers_info->now_part)
- goto warn;
- vers_info->hist_part= next;
+ {
+ my_error(WARN_VERS_PART_FULL, MYF(ME_WARNING|ME_ERROR_LOG),
+ table->s->db.str, table->s->table_name.str,
+ vers_info->hist_part->partition_name, "LIMIT");
+ }
+ else
+ vers_info->hist_part= next;
}
return;
}
@@ -876,54 +865,6 @@ void partition_info::vers_set_hist_part(THD *thd)
return;
}
}
- return;
-warn:
- my_error(WARN_VERS_PART_FULL, MYF(ME_WARNING|ME_ERROR_LOG),
- table->s->db.str, table->s->table_name.str,
- vers_info->hist_part->partition_name);
-}
-
-
-bool partition_info::vers_setup_expression(THD * thd, uint32 alter_add)
-{
- if (!table->versioned())
- {
- // frm must be corrupted, normally CREATE/ALTER TABLE checks for that
- my_error(ER_FILE_CORRUPT, MYF(0), table->s->path.str);
- return true;
- }
-
- DBUG_ASSERT(part_type == VERSIONING_PARTITION);
- DBUG_ASSERT(table->versioned(VERS_TIMESTAMP));
-
- if (!alter_add)
- {
- Field *row_end= table->vers_end_field();
- // needed in handle_list_of_fields()
- row_end->flags|= GET_FIXED_FIELDS_FLAG;
- Name_resolution_context *context= &thd->lex->current_select->context;
- Item *row_end_item= new (thd->mem_root) Item_field(thd, context, row_end);
- Item *row_end_ts= new (thd->mem_root) Item_func_unix_timestamp(thd, row_end_item);
- set_part_expr(thd, row_end_ts, false);
- }
-
- if (alter_add)
- {
- List_iterator<partition_element> it(partitions);
- partition_element *el;
- for(uint32 id= 0; ((el= it++)); id++)
- {
- DBUG_ASSERT(el->type != partition_element::CONVENTIONAL);
- /* Newly added element is inserted before AS_OF_NOW. */
- if (el->id == UINT_MAX32 || el->type == partition_element::CURRENT)
- {
- el->id= id;
- if (el->type == partition_element::CURRENT)
- break;
- }
- }
- }
- return false;
}
@@ -1190,7 +1131,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
part_type == LIST_PARTITION ||
part_type == VERSIONING_PARTITION))))
{
- /* Only RANGE and LIST partitioning can be subpartitioned */
+ /* Only RANGE, LIST and SYSTEM_TIME partitioning can be subpartitioned */
my_error(ER_SUBPARTITION_ERROR, MYF(0));
goto end;
}
@@ -1254,7 +1195,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
if (part_type == VERSIONING_PARTITION)
{
DBUG_ASSERT(vers_info);
- if (num_parts < 2 || !vers_info->now_part)
+ if (num_parts < 2 || !(use_default_partitions || vers_info->now_part))
{
DBUG_ASSERT(info);
DBUG_ASSERT(info->alias.str);
@@ -1404,9 +1345,8 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
if (add_or_reorg_part)
{
- if (unlikely(part_type == VERSIONING_PARTITION &&
- vers_setup_expression(thd, add_or_reorg_part->partitions.elements)))
- goto end;
+ if (part_type == VERSIONING_PARTITION && add_or_reorg_part->partitions.elements)
+ vers_update_el_ids();
if (check_constants(thd, this))
goto end;
}
@@ -2141,7 +2081,6 @@ bool partition_info::fix_column_value_functions(THD *thd,
{
uchar *val_ptr;
uint len= field->pack_length();
- sql_mode_t save_sql_mode;
bool save_got_warning;
if (!(column_item= get_column_item(column_item, field)))
@@ -2149,20 +2088,17 @@ bool partition_info::fix_column_value_functions(THD *thd,
result= TRUE;
goto end;
}
- save_sql_mode= thd->variables.sql_mode;
- thd->variables.sql_mode= 0;
+ Sql_mode_instant_set sms(thd, 0);
save_got_warning= thd->got_warning;
thd->got_warning= 0;
if (column_item->save_in_field(field, TRUE) ||
thd->got_warning)
{
my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
- thd->variables.sql_mode= save_sql_mode;
result= TRUE;
goto end;
}
thd->got_warning= save_got_warning;
- thd->variables.sql_mode= save_sql_mode;
if (!(val_ptr= (uchar*) thd->memdup(field->ptr, len)))
{
result= TRUE;
@@ -2699,6 +2635,102 @@ bool partition_info::vers_init_info(THD * thd)
}
+/**
+ Assign INTERVAL and STARTS for SYSTEM_TIME partitions.
+
+ @return true on error
+*/
+
+bool partition_info::vers_set_interval(THD* thd, Item* interval,
+ interval_type int_type, Item* starts,
+ const char *table_name)
+{
+ DBUG_ASSERT(part_type == VERSIONING_PARTITION);
+
+ MYSQL_TIME ltime;
+ uint err;
+ vers_info->interval.type= int_type;
+
+ /* 1. assign INTERVAL to interval.step */
+ if (interval->fix_fields_if_needed_for_scalar(thd, &interval))
+ return true;
+ bool error= get_interval_value(thd, interval, int_type, &vers_info->interval.step) ||
+ vers_info->interval.step.neg || vers_info->interval.step.second_part ||
+ !(vers_info->interval.step.year || vers_info->interval.step.month ||
+ vers_info->interval.step.day || vers_info->interval.step.hour ||
+ vers_info->interval.step.minute || vers_info->interval.step.second);
+ if (error)
+ {
+ my_error(ER_PART_WRONG_VALUE, MYF(0), table_name, "INTERVAL");
+ return true;
+ }
+
+ /* 2. assign STARTS to interval.start */
+ if (starts)
+ {
+ if (starts->fix_fields_if_needed_for_scalar(thd, &starts))
+ return true;
+ switch (starts->result_type())
+ {
+ case INT_RESULT:
+ case DECIMAL_RESULT:
+ case REAL_RESULT:
+ /* When table member is defined, we are inside mysql_unpack_partition(). */
+ if (!table || starts->val_int() > TIMESTAMP_MAX_VALUE)
+ goto interval_starts_error;
+ vers_info->interval.start= (my_time_t) starts->val_int();
+ break;
+ case STRING_RESULT:
+ case TIME_RESULT:
+ {
+ Datetime::Options opt(TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE, thd);
+ starts->get_date(thd, &ltime, opt);
+ vers_info->interval.start= TIME_to_timestamp(thd, &ltime, &err);
+ if (err)
+ goto interval_starts_error;
+ break;
+ }
+ case ROW_RESULT:
+ default:
+ goto interval_starts_error;
+ }
+ if (!table)
+ {
+ if (thd->query_start() < vers_info->interval.start) {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_PART_STARTS_BEYOND_INTERVAL,
+ ER_THD(thd, ER_PART_STARTS_BEYOND_INTERVAL),
+ table_name);
+ }
+ }
+ }
+ else // calculate default STARTS depending on INTERVAL
+ {
+ thd->variables.time_zone->gmt_sec_to_TIME(&ltime, thd->query_start());
+ if (vers_info->interval.step.second)
+ goto interval_set_starts;
+ ltime.second= 0;
+ if (vers_info->interval.step.minute)
+ goto interval_set_starts;
+ ltime.minute= 0;
+ if (vers_info->interval.step.hour)
+ goto interval_set_starts;
+ ltime.hour= 0;
+
+interval_set_starts:
+ vers_info->interval.start= TIME_to_timestamp(thd, &ltime, &err);
+ if (err)
+ goto interval_starts_error;
+ }
+
+ return false;
+
+interval_starts_error:
+ my_error(ER_PART_WRONG_VALUE, MYF(0), table_name, "STARTS");
+ return true;
+}
+
+
bool partition_info::error_if_requires_values() const
{
switch (part_type) {
diff --git a/sql/partition_info.h b/sql/partition_info.h
index 00ef815ce09..0656238ec07 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -34,6 +34,9 @@ typedef bool (*check_constants_func)(THD *thd, partition_info *part_info);
struct st_ddl_log_memory_entry;
+#define MAX_PART_NAME_SIZE 8
+
+
struct Vers_part_info : public Sql_alloc
{
Vers_part_info() :
@@ -385,36 +388,16 @@ private:
uint start_no);
char *create_default_subpartition_name(THD *thd, uint subpart_no,
const char *part_name);
- // FIXME: prune_partition_bitmaps() is duplicate of set_read_partitions()
- bool prune_partition_bitmaps(List<String> *partition_names);
+ bool prune_partition_bitmaps(List<String> *partition_names); // set_read_partitions() in 8.0
bool add_named_partition(const char *part_name, size_t length);
public:
- bool set_read_partitions(List<char> *partition_names);
bool has_unique_name(partition_element *element);
bool field_in_partition_expr(Field *field) const;
bool vers_init_info(THD *thd);
- bool vers_set_interval(THD *thd, Item *item,
- interval_type int_type, my_time_t start)
- {
- DBUG_ASSERT(part_type == VERSIONING_PARTITION);
- vers_info->interval.type= int_type;
- vers_info->interval.start= start;
- if (item->fix_fields_if_needed_for_scalar(thd, &item))
- return true;
- bool error= get_interval_value(thd, item, int_type, &vers_info->interval.step) ||
- vers_info->interval.step.neg || vers_info->interval.step.second_part ||
- !(vers_info->interval.step.year || vers_info->interval.step.month ||
- vers_info->interval.step.day || vers_info->interval.step.hour ||
- vers_info->interval.step.minute || vers_info->interval.step.second);
- if (error)
- {
- my_error(ER_PART_WRONG_VALUE, MYF(0),
- thd->lex->create_last_non_select_table->table_name.str,
- "INTERVAL");
- }
- return error;
- }
+ bool vers_set_interval(THD *thd, Item *interval,
+ interval_type int_type, Item *starts,
+ const char *table_name);
bool vers_set_limit(ulonglong limit)
{
DBUG_ASSERT(part_type == VERSIONING_PARTITION);
@@ -422,7 +405,8 @@ public:
return !limit;
}
void vers_set_hist_part(THD *thd);
- bool vers_setup_expression(THD *thd, uint32 alter_add= 0); /* Stage 1. */
+ bool vers_fix_field_list(THD *thd);
+ void vers_update_el_ids();
partition_element *get_partition(uint part_id)
{
List_iterator<partition_element> it(partitions);
@@ -434,6 +418,7 @@ public:
}
return NULL;
}
+ uint next_part_no(uint new_parts) const;
};
uint32 get_next_partition_id_range(struct st_partition_iter* part_iter);
@@ -463,4 +448,104 @@ void init_all_partitions_iterator(partition_info *part_info,
part_iter->get_next= get_next_partition_id_range;
}
+
+/**
+ @brief Update part_field_list by row_end field name
+
+ @returns true on error; false on success
+*/
+inline
+bool partition_info::vers_fix_field_list(THD * thd)
+{
+ if (!table->versioned())
+ {
+ // frm must be corrupted, normally CREATE/ALTER TABLE checks for that
+ my_error(ER_FILE_CORRUPT, MYF(0), table->s->path.str);
+ return true;
+ }
+ DBUG_ASSERT(part_type == VERSIONING_PARTITION);
+ DBUG_ASSERT(table->versioned(VERS_TIMESTAMP));
+
+ Field *row_end= table->vers_end_field();
+ // needed in handle_list_of_fields()
+ row_end->flags|= GET_FIXED_FIELDS_FLAG;
+ Name_resolution_context *context= &thd->lex->current_select->context;
+ Item *row_end_item= new (thd->mem_root) Item_field(thd, context, row_end);
+ Item *row_end_ts= new (thd->mem_root) Item_func_unix_timestamp(thd, row_end_item);
+ set_part_expr(thd, row_end_ts, false);
+
+ return false;
+}
+
+
+/**
+ @brief Update partition_element's id
+
+ @returns true on error; false on success
+*/
+inline
+void partition_info::vers_update_el_ids()
+{
+ DBUG_ASSERT(part_type == VERSIONING_PARTITION);
+ DBUG_ASSERT(table->versioned(VERS_TIMESTAMP));
+
+ List_iterator<partition_element> it(partitions);
+ partition_element *el;
+ for(uint32 id= 0; ((el= it++)); id++)
+ {
+ DBUG_ASSERT(el->type != partition_element::CONVENTIONAL);
+ /* Newly added element is inserted before AS_OF_NOW. */
+ if (el->id == UINT_MAX32 || el->type == partition_element::CURRENT)
+ {
+ el->id= id;
+ if (el->type == partition_element::CURRENT)
+ break;
+ }
+ }
+}
+
+
+inline
+bool make_partition_name(char *move_ptr, uint i)
+{
+ int res= snprintf(move_ptr, MAX_PART_NAME_SIZE + 1, "p%u", i);
+ return res < 0 || res > MAX_PART_NAME_SIZE;
+}
+
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+inline
+uint partition_info::next_part_no(uint new_parts) const
+{
+ if (part_type != VERSIONING_PARTITION)
+ return num_parts;
+ DBUG_ASSERT(new_parts > 0);
+ /* Choose first non-occupied name suffix */
+ uint32 suffix= num_parts - 1;
+ DBUG_ASSERT(suffix > 0);
+ char part_name[MAX_PART_NAME_SIZE + 1];
+ List_iterator_fast<partition_element> it(table->part_info->partitions);
+ for (uint cur_part= 0; cur_part < new_parts; ++cur_part, ++suffix)
+ {
+ uint32 cur_suffix= suffix;
+ if (make_partition_name(part_name, suffix))
+ return 0;
+ partition_element *el;
+ it.rewind();
+ while ((el= it++))
+ {
+ if (0 == my_strcasecmp(&my_charset_latin1, el->partition_name, part_name))
+ {
+ if (make_partition_name(part_name, ++suffix))
+ return 0;
+ it.rewind();
+ }
+ }
+ if (cur_part > 0 && suffix > cur_suffix)
+ cur_part= 0;
+ }
+ return suffix - new_parts;
+}
+#endif
+
#endif /* PARTITION_INFO_INCLUDED */
diff --git a/sql/privilege.h b/sql/privilege.h
new file mode 100644
index 00000000000..37cdf4da01a
--- /dev/null
+++ b/sql/privilege.h
@@ -0,0 +1,738 @@
+#ifndef PRIVILEGE_H_INCLUDED
+#define PRIVILEGE_H_INCLUDED
+
+/* Copyright (c) 2020, MariaDB Corporation.
+
+ 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-1335 USA */
+
+#include "my_global.h" // ulonglong
+
+
+/*
+ A strict enum to store privilege bits.
+
+ We should eventually make if even stricter using "enum class privilege_t" and:
+ - Replace all code pieces like `if (priv)` to `if (priv != NO_ACL)`
+ - Remove "delete" comparison operators below
+*/
+enum privilege_t: unsigned long long
+{
+ NO_ACL = (0),
+ SELECT_ACL = (1UL << 0),
+ INSERT_ACL = (1UL << 1),
+ UPDATE_ACL = (1UL << 2),
+ DELETE_ACL = (1UL << 3),
+ CREATE_ACL = (1UL << 4),
+ DROP_ACL = (1UL << 5),
+ RELOAD_ACL = (1UL << 6),
+ SHUTDOWN_ACL = (1UL << 7),
+ PROCESS_ACL = (1UL << 8),
+ FILE_ACL = (1UL << 9),
+ GRANT_ACL = (1UL << 10),
+ REFERENCES_ACL = (1UL << 11),
+ INDEX_ACL = (1UL << 12),
+ ALTER_ACL = (1UL << 13),
+ SHOW_DB_ACL = (1UL << 14),
+ SUPER_ACL = (1UL << 15),
+ CREATE_TMP_ACL = (1UL << 16),
+ LOCK_TABLES_ACL = (1UL << 17),
+ EXECUTE_ACL = (1UL << 18),
+ REPL_SLAVE_ACL = (1UL << 19),
+ BINLOG_MONITOR_ACL = (1UL << 20), // Was REPL_CLIENT_ACL prior to 10.5.2
+ CREATE_VIEW_ACL = (1UL << 21),
+ SHOW_VIEW_ACL = (1UL << 22),
+ CREATE_PROC_ACL = (1UL << 23),
+ ALTER_PROC_ACL = (1UL << 24),
+ CREATE_USER_ACL = (1UL << 25),
+ EVENT_ACL = (1UL << 26),
+ TRIGGER_ACL = (1UL << 27),
+ CREATE_TABLESPACE_ACL = (1UL << 28),
+ DELETE_HISTORY_ACL = (1UL << 29), // Added in 10.3.4
+ SET_USER_ACL = (1UL << 30), // Added in 10.5.2
+ FEDERATED_ADMIN_ACL = (1UL << 31), // Added in 10.5.2
+ CONNECTION_ADMIN_ACL = (1ULL << 32), // Added in 10.5.2
+ READ_ONLY_ADMIN_ACL = (1ULL << 33), // Added in 10.5.2
+ REPL_SLAVE_ADMIN_ACL = (1ULL << 34), // Added in 10.5.2
+ REPL_MASTER_ADMIN_ACL = (1ULL << 35), // Added in 10.5.2
+ BINLOG_ADMIN_ACL = (1ULL << 36), // Added in 10.5.2
+ BINLOG_REPLAY_ACL = (1ULL << 37) // Added in 10.5.2
+ /*
+ When adding new privilege bits, don't forget to update:
+ In this file:
+ - Add a new LAST_version_ACL
+ - Add a new ALL_KNOWN_ACL_version
+ - Change ALL_KNOWN_ACL to ALL_KNOWN_ACL_version
+ - Change GLOBAL_ACLS if needed
+ - Change SUPER_ADDED_SINCE_USER_TABLE_ACL if needed
+
+ In other files:
+ - static struct show_privileges_st sys_privileges[]
+ - static const char *command_array[] and static uint command_lengths[]
+ - mysql_system_tables.sql and mysql_system_tables_fix.sql
+ - acl_init() or whatever - to define behaviour for old privilege tables
+ - Update User_table_json::get_access()
+ - sql_yacc.yy - for GRANT/REVOKE to work
+
+ Important: the enum should contain only single-bit values.
+ In this case, debuggers print bit combinations in the readable form:
+ (gdb) p (privilege_t) (15)
+ $8 = (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)
+
+ Bit-OR combinations of the above values should be declared outside!
+ */
+};
+
+
+// Version markers
+constexpr privilege_t LAST_100304_ACL= DELETE_HISTORY_ACL;
+constexpr privilege_t LAST_100502_ACL= BINLOG_REPLAY_ACL;
+
+// Current version markers
+constexpr privilege_t LAST_CURRENT_ACL= LAST_100502_ACL;
+constexpr uint PRIVILEGE_T_MAX_BIT=
+ my_bit_log2_uint64((ulonglong) LAST_CURRENT_ACL);
+
+static_assert((privilege_t)(1ULL << PRIVILEGE_T_MAX_BIT) == LAST_CURRENT_ACL,
+ "Something went fatally badly: "
+ "LAST_CURRENT_ACL and PRIVILEGE_T_MAX_BIT do not match");
+
+// A combination of all bits defined in 10.3.4 (and earlier)
+constexpr privilege_t ALL_KNOWN_ACL_100304 =
+ (privilege_t) ((LAST_100304_ACL << 1) - 1);
+
+// A combination of all bits defined in 10.5.2
+constexpr privilege_t ALL_KNOWN_ACL_100502=
+ (privilege_t) ((LAST_100502_ACL << 1) - 1);
+
+// A combination of all bits defined as of the current version
+constexpr privilege_t ALL_KNOWN_ACL= ALL_KNOWN_ACL_100502;
+
+
+// Unary operators
+static inline constexpr ulonglong operator~(privilege_t access)
+{
+ return ~static_cast<ulonglong>(access);
+}
+
+/*
+ Comparison operators.
+ Delete automatic conversion between to/from integer types as much as possible.
+ This forces to use `(priv == NO_ACL)` instead of `(priv == 0)`.
+
+ Note: these operators will be gone when we change privilege_t to
+ "enum class privilege_t". See comments above.
+*/
+static inline bool operator==(privilege_t, ulonglong)= delete;
+static inline bool operator==(privilege_t, ulong)= delete;
+static inline bool operator==(privilege_t, uint)= delete;
+static inline bool operator==(privilege_t, uchar)= delete;
+static inline bool operator==(privilege_t, longlong)= delete;
+static inline bool operator==(privilege_t, long)= delete;
+static inline bool operator==(privilege_t, int)= delete;
+static inline bool operator==(privilege_t, char)= delete;
+static inline bool operator==(privilege_t, bool)= delete;
+
+static inline bool operator==(ulonglong, privilege_t)= delete;
+static inline bool operator==(ulong, privilege_t)= delete;
+static inline bool operator==(uint, privilege_t)= delete;
+static inline bool operator==(uchar, privilege_t)= delete;
+static inline bool operator==(longlong, privilege_t)= delete;
+static inline bool operator==(long, privilege_t)= delete;
+static inline bool operator==(int, privilege_t)= delete;
+static inline bool operator==(char, privilege_t)= delete;
+static inline bool operator==(bool, privilege_t)= delete;
+
+static inline bool operator!=(privilege_t, ulonglong)= delete;
+static inline bool operator!=(privilege_t, ulong)= delete;
+static inline bool operator!=(privilege_t, uint)= delete;
+static inline bool operator!=(privilege_t, uchar)= delete;
+static inline bool operator!=(privilege_t, longlong)= delete;
+static inline bool operator!=(privilege_t, long)= delete;
+static inline bool operator!=(privilege_t, int)= delete;
+static inline bool operator!=(privilege_t, char)= delete;
+static inline bool operator!=(privilege_t, bool)= delete;
+
+static inline bool operator!=(ulonglong, privilege_t)= delete;
+static inline bool operator!=(ulong, privilege_t)= delete;
+static inline bool operator!=(uint, privilege_t)= delete;
+static inline bool operator!=(uchar, privilege_t)= delete;
+static inline bool operator!=(longlong, privilege_t)= delete;
+static inline bool operator!=(long, privilege_t)= delete;
+static inline bool operator!=(int, privilege_t)= delete;
+static inline bool operator!=(char, privilege_t)= delete;
+static inline bool operator!=(bool, privilege_t)= delete;
+
+
+// Dyadic bitwise operators
+static inline constexpr privilege_t operator&(privilege_t a, privilege_t b)
+{
+ return static_cast<privilege_t>(static_cast<ulonglong>(a) &
+ static_cast<ulonglong>(b));
+}
+
+static inline constexpr privilege_t operator&(ulonglong a, privilege_t b)
+{
+ return static_cast<privilege_t>(a & static_cast<ulonglong>(b));
+}
+
+static inline constexpr privilege_t operator&(privilege_t a, ulonglong b)
+{
+ return static_cast<privilege_t>(static_cast<ulonglong>(a) & b);
+}
+
+static inline constexpr privilege_t operator|(privilege_t a, privilege_t b)
+{
+ return static_cast<privilege_t>(static_cast<ulonglong>(a) |
+ static_cast<ulonglong>(b));
+}
+
+
+// Dyadyc bitwise assignment operators
+static inline privilege_t& operator&=(privilege_t &a, privilege_t b)
+{
+ return a= a & b;
+}
+
+static inline privilege_t& operator&=(privilege_t &a, ulonglong b)
+{
+ return a= a & b;
+}
+
+static inline privilege_t& operator|=(privilege_t &a, privilege_t b)
+{
+ return a= a | b;
+}
+
+
+/*
+ A combination of all SUPER privileges added since the old user table format.
+ These privileges are automatically added when upgrading from the
+ old format mysql.user table if a user has the SUPER privilege.
+*/
+constexpr privilege_t GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS=
+ SET_USER_ACL |
+ FEDERATED_ADMIN_ACL |
+ CONNECTION_ADMIN_ACL |
+ READ_ONLY_ADMIN_ACL |
+ REPL_SLAVE_ADMIN_ACL |
+ BINLOG_ADMIN_ACL |
+ BINLOG_REPLAY_ACL;
+
+
+constexpr privilege_t COL_DML_ACLS=
+ SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL;
+
+constexpr privilege_t VIEW_ACLS=
+ CREATE_VIEW_ACL | SHOW_VIEW_ACL;
+
+constexpr privilege_t STD_TABLE_DDL_ACLS=
+ CREATE_ACL | DROP_ACL | ALTER_ACL;
+
+constexpr privilege_t ALL_TABLE_DDL_ACLS=
+ STD_TABLE_DDL_ACLS | INDEX_ACL;
+
+constexpr privilege_t COL_ACLS=
+ SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL;
+
+constexpr privilege_t PROC_DDL_ACLS=
+ CREATE_PROC_ACL | ALTER_PROC_ACL;
+
+constexpr privilege_t SHOW_PROC_ACLS=
+ PROC_DDL_ACLS | EXECUTE_ACL;
+
+constexpr privilege_t TABLE_ACLS=
+ COL_DML_ACLS | ALL_TABLE_DDL_ACLS | VIEW_ACLS |
+ GRANT_ACL | REFERENCES_ACL |
+ TRIGGER_ACL | DELETE_HISTORY_ACL;
+
+constexpr privilege_t DB_ACLS=
+ TABLE_ACLS | PROC_DDL_ACLS | EXECUTE_ACL |
+ CREATE_TMP_ACL | LOCK_TABLES_ACL | EVENT_ACL;
+
+constexpr privilege_t PROC_ACLS=
+ ALTER_PROC_ACL | EXECUTE_ACL | GRANT_ACL;
+
+constexpr privilege_t GLOBAL_ACLS=
+ DB_ACLS | SHOW_DB_ACL |
+ CREATE_USER_ACL | CREATE_TABLESPACE_ACL |
+ SUPER_ACL | RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL |
+ REPL_SLAVE_ACL | BINLOG_MONITOR_ACL |
+ GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS |
+ REPL_MASTER_ADMIN_ACL;
+
+constexpr privilege_t DEFAULT_CREATE_PROC_ACLS=
+ ALTER_PROC_ACL | EXECUTE_ACL;
+
+constexpr privilege_t SHOW_CREATE_TABLE_ACLS=
+ COL_DML_ACLS | ALL_TABLE_DDL_ACLS |
+ TRIGGER_ACL | REFERENCES_ACL | GRANT_ACL | VIEW_ACLS;
+
+/**
+ Table-level privileges which are automatically "granted" to everyone on
+ existing temporary tables (CREATE_ACL is necessary for ALTER ... RENAME).
+*/
+constexpr privilege_t TMP_TABLE_ACLS=
+ COL_DML_ACLS | ALL_TABLE_DDL_ACLS;
+
+
+
+/*
+ Allow to set an object definer:
+ CREATE DEFINER=xxx {TRIGGER|VIEW|FUNCTION|PROCEDURE}
+ Was SUPER prior to 10.5.2
+*/
+constexpr privilege_t PRIV_DEFINER_CLAUSE= SET_USER_ACL | SUPER_ACL;
+/*
+ If a VIEW has a `definer=invoker@host` clause and
+ the specified definer does not exists, then
+ - The invoker with REVEAL_MISSING_DEFINER_ACL gets:
+ ERROR: The user specified as a definer ('definer1'@'localhost') doesn't exist
+ - The invoker without MISSING_DEFINER_ACL gets a generic access error,
+ without revealing details that the definer does not exists.
+
+ TODO: we should eventually test the same privilege when processing
+ other objects that have the DEFINER clause (e.g. routines, triggers).
+ Currently the missing definer is revealed for non-privileged invokers
+ in case of routines, triggers, etc.
+
+ Was SUPER prior to 10.5.2
+*/
+constexpr privilege_t PRIV_REVEAL_MISSING_DEFINER= SET_USER_ACL | SUPER_ACL;
+
+/* Actions that require only the SUPER privilege */
+constexpr privilege_t PRIV_DES_DECRYPT_ONE_ARG= SUPER_ACL;
+constexpr privilege_t PRIV_LOG_BIN_TRUSTED_SP_CREATOR= SUPER_ACL;
+constexpr privilege_t PRIV_DEBUG= SUPER_ACL;
+constexpr privilege_t PRIV_SET_GLOBAL_SYSTEM_VARIABLE= SUPER_ACL;
+constexpr privilege_t PRIV_SET_RESTRICTED_SESSION_SYSTEM_VARIABLE= SUPER_ACL;
+
+/* The following variables respected only SUPER_ACL prior to 10.5.2 */
+constexpr privilege_t PRIV_SET_SYSTEM_VAR_BINLOG_FORMAT=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_VAR_BINLOG_DIRECT_NON_TRANSACTIONAL_UPDATES=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_VAR_BINLOG_ANNOTATE_ROW_EVENTS=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_VAR_BINLOG_ROW_IMAGE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_VAR_SQL_LOG_BIN=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_CACHE_SIZE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_FILE_CACHE_SIZE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_STMT_CACHE_SIZE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_COMMIT_WAIT_COUNT=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_COMMIT_WAIT_USEC=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_ROW_METADATA=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_EXPIRE_LOGS_DAYS=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_LOG_BIN_COMPRESS=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_LOG_BIN_COMPRESS_MIN_LEN=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_LOG_BIN_TRUST_FUNCTION_CREATORS=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_BINLOG_CACHE_SIZE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_BINLOG_STMT_CACHE_SIZE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_BINLOG_SIZE=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_BINLOG=
+ SUPER_ACL | BINLOG_ADMIN_ACL;
+
+
+
+/* Privileges related to --read-only */
+// Was super prior to 10.5.2
+constexpr privilege_t PRIV_IGNORE_READ_ONLY= READ_ONLY_ADMIN_ACL | SUPER_ACL;
+// Was super prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_READ_ONLY=
+ READ_ONLY_ADMIN_ACL | SUPER_ACL;
+
+/*
+ Privileges related to connection handling.
+*/
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_IGNORE_INIT_CONNECT= CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_IGNORE_MAX_USER_CONNECTIONS= CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_IGNORE_MAX_CONNECTIONS= CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_IGNORE_MAX_PASSWORD_ERRORS= CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_KILL_OTHER_USER_PROCESS= CONNECTION_ADMIN_ACL | SUPER_ACL;
+
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_CONNECT_TIMEOUT=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_DISCONNECT_ON_EXPIRED_PASSWORD=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_EXTRA_MAX_CONNECTIONS=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_INIT_CONNECT=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_CONNECTIONS=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_CONNECT_ERRORS=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_PASSWORD_ERRORS=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_PROXY_PROTOCOL_NETWORKS=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SECURE_AUTH=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLOW_LAUNCH_TIME=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL=
+ CONNECTION_ADMIN_ACL | SUPER_ACL;
+
+
+/*
+ Binary log related privileges that are checked regardless
+ of active replication running.
+*/
+
+/*
+ This command was renamed from "SHOW MASTER STATUS"
+ to "SHOW BINLOG STATUS" in 10.5.2.
+ Was SUPER_ACL | REPL_CLIENT_ACL prior to 10.5.2
+ REPL_CLIENT_ACL was renamed to BINLOG_MONITOR_ACL.
+*/
+constexpr privilege_t PRIV_STMT_SHOW_BINLOG_STATUS= BINLOG_MONITOR_ACL | SUPER_ACL;
+
+/*
+ Was SUPER_ACL | REPL_CLIENT_ACL prior to 10.5.2
+ REPL_CLIENT_ACL was renamed to BINLOG_MONITOR_ACL.
+*/
+constexpr privilege_t PRIV_STMT_SHOW_BINARY_LOGS= BINLOG_MONITOR_ACL | SUPER_ACL;
+
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_PURGE_BINLOG= BINLOG_ADMIN_ACL | SUPER_ACL;
+
+// Was REPL_SLAVE_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_SHOW_BINLOG_EVENTS= BINLOG_MONITOR_ACL;
+
+
+/*
+ Privileges for replication related statements and commands
+ that are executed on the master.
+*/
+constexpr privilege_t PRIV_COM_REGISTER_SLAVE= REPL_SLAVE_ACL;
+constexpr privilege_t PRIV_COM_BINLOG_DUMP= REPL_SLAVE_ACL;
+// Was REPL_SLAVE_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_SHOW_SLAVE_HOSTS= REPL_MASTER_ADMIN_ACL;
+
+/*
+ Replication master related variable privileges.
+ Where SUPER prior to 10.5.2
+*/
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_ENABLED=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_TIMEOUT=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_TRACE_LEVEL=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_WAIT_POINT=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_MASTER_VERIFY_CHECKSUM=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_BINLOG_STATE=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SERVER_ID=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_DOMAIN_ID=
+ REPL_MASTER_ADMIN_ACL | SUPER_ACL;
+
+
+/* Privileges for statements that are executed on the slave */
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_START_SLAVE= REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_STOP_SLAVE= REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_CHANGE_MASTER= REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+// Was (SUPER_ACL | REPL_CLIENT_ACL) prior to 10.5.2
+constexpr privilege_t PRIV_STMT_SHOW_SLAVE_STATUS= REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+// Was REPL_SLAVE_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_SHOW_RELAYLOG_EVENTS= REPL_SLAVE_ADMIN_ACL;
+
+/*
+ Privileges related to binlog replying.
+ Were SUPER_ACL prior to 10.5.2
+*/
+constexpr privilege_t PRIV_STMT_BINLOG= BINLOG_REPLAY_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_SESSION_VAR_GTID_SEQ_NO=
+ BINLOG_REPLAY_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_SESSION_VAR_PSEUDO_THREAD_ID=
+ BINLOG_REPLAY_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_SESSION_VAR_SERVER_ID=
+ BINLOG_REPLAY_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_SESSION_VAR_GTID_DOMAIN_ID=
+ BINLOG_REPLAY_ACL | SUPER_ACL;
+
+/*
+ Privileges for slave related global variables.
+ Were SUPER prior to 10.5.2.
+*/
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_EVENTS_MARKED_FOR_SKIP=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_DO_DB=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_DO_TABLE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_IGNORE_DB=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_IGNORE_TABLE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_WILD_DO_TABLE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_WILD_IGNORE_TABLE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_READ_BINLOG_SPEED_LIMIT=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_COMPRESSED_PROTOCOL=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_DDL_EXEC_MODE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_DOMAIN_PARALLEL_THREADS=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_EXEC_MODE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_MAX_ALLOWED_PACKET=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_NET_TIMEOUT=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_MAX_QUEUED=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_MODE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_THREADS=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_WORKERS=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_RUN_TRIGGERS_FOR_RBR=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_SQL_VERIFY_CHECKSUM=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_TRANSACTION_RETRY_INTERVAL=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_TYPE_CONVERSIONS=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_INIT_SLAVE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_ENABLED=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_DELAY_MASTER=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_KILL_CONN_TIMEOUT=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RELAY_LOG_PURGE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_RELAY_LOG_RECOVERY=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_MASTER_INFO=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_RELAY_LOG=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_RELAY_LOG_INFO=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_CLEANUP_BATCH_SIZE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_IGNORE_DUPLICATES=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_POS_AUTO_ENGINES=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_SLAVE_POS=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+constexpr privilege_t PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_STRICT_MODE=
+ REPL_SLAVE_ADMIN_ACL | SUPER_ACL;
+
+
+/* Privileges for federated database related statements */
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_CREATE_SERVER= FEDERATED_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_ALTER_SERVER= FEDERATED_ADMIN_ACL | SUPER_ACL;
+// Was SUPER_ACL prior to 10.5.2
+constexpr privilege_t PRIV_STMT_DROP_SERVER= FEDERATED_ADMIN_ACL | SUPER_ACL;
+
+
+/* Privileges related to processes */
+constexpr privilege_t PRIV_COM_PROCESS_INFO= PROCESS_ACL;
+constexpr privilege_t PRIV_STMT_SHOW_EXPLAIN= PROCESS_ACL;
+constexpr privilege_t PRIV_STMT_SHOW_ENGINE_STATUS= PROCESS_ACL;
+constexpr privilege_t PRIV_STMT_SHOW_ENGINE_MUTEX= PROCESS_ACL;
+constexpr privilege_t PRIV_STMT_SHOW_PROCESSLIST= PROCESS_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
+*/
+
+/* Privileges that need to be reallocated (in continous chunks) */
+constexpr privilege_t DB_CHUNK0 (COL_DML_ACLS | CREATE_ACL | DROP_ACL);
+constexpr privilege_t DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL);
+constexpr privilege_t DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL);
+constexpr privilege_t DB_CHUNK3 (VIEW_ACLS | PROC_DDL_ACLS);
+constexpr privilege_t DB_CHUNK4 (EXECUTE_ACL);
+constexpr privilege_t DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL);
+constexpr privilege_t DB_CHUNK6 (DELETE_HISTORY_ACL);
+
+
+static inline privilege_t fix_rights_for_db(privilege_t access)
+{
+ ulonglong A(access);
+ return static_cast<privilege_t>
+ (((A) & DB_CHUNK0) |
+ ((A << 4) & DB_CHUNK1) |
+ ((A << 6) & DB_CHUNK2) |
+ ((A << 9) & DB_CHUNK3) |
+ ((A << 2) & DB_CHUNK4) |
+ ((A << 9) & DB_CHUNK5) |
+ ((A << 10) & DB_CHUNK6));
+}
+
+static inline privilege_t get_rights_for_db(privilege_t access)
+{
+ ulonglong A(access);
+ return static_cast<privilege_t>
+ ((A & DB_CHUNK0) |
+ ((A & DB_CHUNK1) >> 4) |
+ ((A & DB_CHUNK2) >> 6) |
+ ((A & DB_CHUNK3) >> 9) |
+ ((A & DB_CHUNK4) >> 2) |
+ ((A & DB_CHUNK5) >> 9) |
+ ((A & DB_CHUNK6) >> 10));
+}
+
+
+#define TBL_CHUNK0 DB_CHUNK0
+#define TBL_CHUNK1 DB_CHUNK1
+#define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+#define TBL_CHUNK3 TRIGGER_ACL
+#define TBL_CHUNK4 (DELETE_HISTORY_ACL)
+
+
+static inline privilege_t fix_rights_for_table(privilege_t access)
+{
+ ulonglong A(access);
+ return static_cast<privilege_t>
+ ((A & TBL_CHUNK0) |
+ ((A << 4) & TBL_CHUNK1) |
+ ((A << 11) & TBL_CHUNK2) |
+ ((A << 15) & TBL_CHUNK3) |
+ ((A << 16) & TBL_CHUNK4));
+}
+
+
+static inline privilege_t get_rights_for_table(privilege_t access)
+{
+ ulonglong A(access);
+ return static_cast<privilege_t>
+ ((A & TBL_CHUNK0) |
+ ((A & TBL_CHUNK1) >> 4) |
+ ((A & TBL_CHUNK2) >> 11) |
+ ((A & TBL_CHUNK3) >> 15) |
+ ((A & TBL_CHUNK4) >> 16));
+}
+
+
+static inline privilege_t fix_rights_for_column(privilege_t A)
+{
+ const ulonglong mask(SELECT_ACL | INSERT_ACL | UPDATE_ACL);
+ return (A & mask) | static_cast<privilege_t>((A & ~mask) << 8);
+}
+
+
+static inline privilege_t get_rights_for_column(privilege_t A)
+{
+ const ulonglong mask(SELECT_ACL | INSERT_ACL | UPDATE_ACL);
+ return static_cast<privilege_t>((static_cast<ulonglong>(A) & mask) |
+ (static_cast<ulonglong>(A) >> 8));
+}
+
+
+static inline privilege_t fix_rights_for_procedure(privilege_t access)
+{
+ ulonglong A(access);
+ return static_cast<privilege_t>
+ (((A << 18) & EXECUTE_ACL) |
+ ((A << 23) & ALTER_PROC_ACL) |
+ ((A << 8) & GRANT_ACL));
+}
+
+
+static inline privilege_t get_rights_for_procedure(privilege_t access)
+{
+ ulonglong A(access);
+ return static_cast<privilege_t>
+ (((A & EXECUTE_ACL) >> 18) |
+ ((A & ALTER_PROC_ACL) >> 23) |
+ ((A & GRANT_ACL) >> 8));
+}
+
+
+#endif /* PRIVILEGE_H_INCLUDED */
diff --git a/sql/procedure.h b/sql/procedure.h
index 15fd525ec65..769eac5f217 100644
--- a/sql/procedure.h
+++ b/sql/procedure.h
@@ -2,6 +2,7 @@
#define PROCEDURE_INCLUDED
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -44,7 +45,7 @@ public:
this->name.length= strlen(name_par);
}
enum Type type() const { return Item::PROC_ITEM; }
- Field *create_tmp_field_ex(TABLE *table, Tmp_field_src *src,
+ Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src,
const Tmp_field_param *param)
{
/*
@@ -52,7 +53,7 @@ public:
DECLARE c CURSOR FOR SELECT * FROM t1 PROCEDURE analyse();
OPEN c;
*/
- return create_tmp_field_ex_simple(table, src, param);
+ return create_tmp_field_ex_simple(root, table, src, param);
}
virtual void set(double nr)=0;
virtual void set(const char *str,uint length,CHARSET_INFO *cs)=0;
@@ -88,7 +89,7 @@ public:
{
int err_not_used;
char *end_not_used;
- value= my_strntod(cs,(char*) str,length, &end_not_used, &err_not_used);
+ value= cs->strntod((char*) str,length, &end_not_used, &err_not_used);
}
double val_real() { return value; }
longlong val_int() { return (longlong) value; }
@@ -107,11 +108,16 @@ class Item_proc_int :public Item_proc
public:
Item_proc_int(THD *thd, const char *name_par): Item_proc(thd, name_par)
{ max_length=11; }
- const Type_handler *type_handler() const { return &type_handler_longlong; }
+ const Type_handler *type_handler() const
+ {
+ if (unsigned_flag)
+ return &type_handler_ulonglong;
+ return &type_handler_slonglong;
+ }
void set(double nr) { value=(longlong) nr; }
void set(longlong nr) { value=nr; }
void set(const char *str,uint length, CHARSET_INFO *cs)
- { int err; value=my_strntoll(cs,str,length,10,NULL,&err); }
+ { int err; value= cs->strntoll(str,length,10,NULL,&err); }
double val_real() { return (double) value; }
longlong val_int() { return value; }
String *val_str(String *s) { s->set(value, default_charset()); return s; }
@@ -135,14 +141,14 @@ public:
int err_not_used;
char *end_not_used;
CHARSET_INFO *cs= str_value.charset();
- return my_strntod(cs, (char*) str_value.ptr(), str_value.length(),
- &end_not_used, &err_not_used);
+ return cs->strntod((char*) str_value.ptr(), str_value.length(),
+ &end_not_used, &err_not_used);
}
longlong val_int()
{
int err;
CHARSET_INFO *cs=str_value.charset();
- return my_strntoll(cs,str_value.ptr(),str_value.length(),10,NULL,&err);
+ return cs->strntoll(str_value.ptr(),str_value.length(),10,NULL,&err);
}
String *val_str(String*)
{
diff --git a/sql/protocol.cc b/sql/protocol.cc
index aa9651e974c..dfab9e50ac9 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -32,17 +32,16 @@
#include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
-/* Declared non-static only because of the embedded library. */
-bool net_send_error_packet(THD *, uint, const char *, const char *);
-/* Declared non-static only because of the embedded library. */
-bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
- bool, bool);
-/* Declared non-static only because of the embedded library. */
-bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
#ifndef EMBEDDED_LIBRARY
static bool write_eof_packet(THD *, NET *, uint, uint);
#endif
+CHARSET_INFO *Protocol::character_set_results() const
+{
+ return thd->variables.character_set_results;
+}
+
+
#ifndef EMBEDDED_LIBRARY
bool Protocol::net_store_data(const uchar *from, size_t length)
#else
@@ -147,11 +146,11 @@ bool Protocol_binary::net_store_data_cs(const uchar *from, size_t length,
@retval TRUE An error occurred and the message wasn't sent properly
*/
-bool net_send_error(THD *thd, uint sql_errno, const char *err,
- const char* sqlstate)
+bool Protocol::net_send_error(THD *thd, uint sql_errno, const char *err,
+ const char* sqlstate)
{
bool error;
- DBUG_ENTER("net_send_error");
+ DBUG_ENTER("Protocol::net_send_error");
DBUG_ASSERT(!thd->spcont);
DBUG_ASSERT(sql_errno);
@@ -209,11 +208,11 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
#ifndef EMBEDDED_LIBRARY
bool
-net_send_ok(THD *thd,
- uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong id, const char *message,
- bool is_eof,
- bool skip_flush)
+Protocol::net_send_ok(THD *thd,
+ uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong id,
+ const char *message, bool is_eof,
+ bool skip_flush)
{
NET *net= &thd->net;
StringBuffer<MYSQL_ERRMSG_SIZE + 10> store;
@@ -221,7 +220,7 @@ net_send_ok(THD *thd,
bool state_changed= false;
bool error= FALSE;
- DBUG_ENTER("net_send_ok");
+ DBUG_ENTER("Protocol::net_send_ok");
if (! net->vio) // hack for re-parsing queries
{
@@ -324,11 +323,11 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
*/
bool
-net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
+Protocol::net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
{
NET *net= &thd->net;
bool error= FALSE;
- DBUG_ENTER("net_send_eof");
+ DBUG_ENTER("Protocol::net_send_eof");
/*
Check if client understand new format packets (OK instead of EOF)
@@ -415,8 +414,8 @@ static bool write_eof_packet(THD *thd, NET *net,
@retval TRUE An error occurred and the messages wasn't sent properly
*/
-bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
- const char* sqlstate)
+bool Protocol::net_send_error_packet(THD *thd, uint sql_errno, const char *err,
+ const char* sqlstate)
{
NET *net= &thd->net;
@@ -429,7 +428,7 @@ bool net_send_error_packet(THD *thd, uint sql_errno, const char *err,
char buff[2+1+SQLSTATE_LENGTH+MYSQL_ERRMSG_SIZE], *pos;
my_bool ret;
uint8 save_compress;
- DBUG_ENTER("send_error_packet");
+ DBUG_ENTER("Protocol::send_error_packet");
if (net->vio == 0)
{
@@ -770,6 +769,7 @@ void Protocol::init(THD *thd_arg)
convert= &thd->convert_buffer;
#ifndef DBUG_OFF
field_handlers= 0;
+ field_pos= 0;
#endif
}
@@ -801,6 +801,41 @@ bool Protocol::flush()
#ifndef EMBEDDED_LIBRARY
+
+class Send_field_packed_extended_metadata: public Binary_string
+{
+public:
+ bool append_chunk(mariadb_field_attr_t type, const LEX_CSTRING &value)
+ {
+ /*
+ If we eventually support many metadata chunk types and long metadata
+ values, we'll need to encode type and length using net_store_length()
+ and do corresponding changes to the unpacking code in libmariadb.
+ For now let's just assert that type and length fit into one byte.
+ */
+ DBUG_ASSERT(net_length_size(type) == 1);
+ DBUG_ASSERT(net_length_size(value.length) == 1);
+ size_t nbytes= 1/*type*/ + 1/*length*/ + value.length;
+ if (reserve(nbytes))
+ return true;
+ qs_append((char) (uchar) type);
+ qs_append((char) (uchar) value.length);
+ qs_append(&value);
+ return false;
+ }
+ bool pack(const Send_field_extended_metadata &src)
+ {
+ for (uint i= 0 ; i <= MARIADB_FIELD_ATTR_LAST; i++)
+ {
+ const LEX_CSTRING attr= src.attr(i);
+ if (attr.str && append_chunk((mariadb_field_attr_t) i, attr))
+ return true;
+ }
+ return false;
+ }
+};
+
+
bool Protocol_text::store_field_metadata(const THD * thd,
const Send_field &field,
CHARSET_INFO *charset_for_protocol,
@@ -808,18 +843,31 @@ bool Protocol_text::store_field_metadata(const THD * thd,
{
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
char *pos;
- CHARSET_INFO *cs= system_charset_info;
DBUG_ASSERT(field.is_sane());
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
- if (store(STRING_WITH_LEN("def"), cs, thd_charset) ||
- store_str(field.db_name, cs, thd_charset) ||
- store_str(field.table_name, cs, thd_charset) ||
- store_str(field.org_table_name, cs, thd_charset) ||
- store_str(field.col_name, cs, thd_charset) ||
- store_str(field.org_col_name, cs, thd_charset) ||
- packet->realloc(packet->length() + 12))
+ const LEX_CSTRING def= {STRING_WITH_LEN("def")};
+ if (store_ident(def) ||
+ store_ident(field.db_name) ||
+ store_ident(field.table_name) ||
+ store_ident(field.org_table_name) ||
+ store_ident(field.col_name) ||
+ store_ident(field.org_col_name))
+ return true;
+ if (thd->client_capabilities & MARIADB_CLIENT_EXTENDED_METADATA)
+ {
+ Send_field_packed_extended_metadata metadata;
+ metadata.pack(field);
+
+ /*
+ Don't apply character set conversion:
+ extended metadata is a binary encoded data.
+ */
+ if (store_binary_string(metadata.ptr(), metadata.length()))
+ return true;
+ }
+ if (packet->realloc(packet->length() + 12))
return true;
/* Store fixed length fields */
pos= (char*) packet->end();
@@ -849,8 +897,8 @@ bool Protocol_text::store_field_metadata(const THD * thd,
}
else
{
- if (store_str(field.table_name, cs, thd_charset) ||
- store_str(field.col_name, cs, thd_charset) ||
+ if (store_ident(field.table_name) ||
+ store_ident(field.col_name) ||
packet->realloc(packet->length() + 10))
return true;
pos= (char*) packet->end();
@@ -910,7 +958,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
for (uint pos= 0; (item=it++); pos++)
{
prot.prepare_for_resend();
- if (prot.store_field_metadata(thd, item, pos))
+ if (prot.store_item_metadata(thd, item, pos))
goto err;
if (prot.write())
DBUG_RETURN(1);
@@ -990,7 +1038,7 @@ bool Protocol::write()
#endif /* EMBEDDED_LIBRARY */
-bool Protocol_text::store_field_metadata(THD *thd, Item *item, uint pos)
+bool Protocol_text::store_item_metadata(THD *thd, Item *item, uint pos)
{
Send_field field(thd, item);
return store_field_metadata(thd, field, item->charset_for_protocol(), pos);
@@ -1003,7 +1051,7 @@ bool Protocol_text::store_field_metadata_for_list_fields(const THD *thd,
uint pos)
{
Send_field field= tl->view ?
- Send_field(fld, tl->view_db.str, tl->view_name.str) :
+ Send_field(fld, tl->view_db, tl->view_name) :
Send_field(fld);
return store_field_metadata(thd, field, fld->charset_for_protocol(), pos);
}
@@ -1133,9 +1181,7 @@ bool Protocol::store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
/* 'tocs' is set 0 when client issues SET character_set_results=NULL */
- if (tocs && !my_charset_same(fromcs, tocs) &&
- fromcs != &my_charset_bin &&
- tocs != &my_charset_bin)
+ if (needs_conversion(fromcs, tocs))
{
/* Store with conversion */
return net_store_data_cs((uchar*) from, length, fromcs, tocs);
@@ -1145,10 +1191,35 @@ bool Protocol::store_string_aux(const char *from, size_t length,
}
-bool Protocol_text::store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+bool Protocol_text::store_numeric_string_aux(const char *from, size_t length)
+{
+ CHARSET_INFO *tocs= thd->variables.character_set_results;
+ // 'tocs' is NULL when the client issues SET character_set_results=NULL
+ if (tocs && (tocs->state & MY_CS_NONASCII)) // Conversion needed
+ return net_store_data_cs((uchar*) from, length, &my_charset_latin1, tocs);
+ return net_store_data((uchar*) from, length); // No conversion
+}
+
+
+bool Protocol::store_warning(const char *from, size_t length)
+{
+ BinaryStringBuffer<MYSQL_ERRMSG_SIZE> tmp;
+ CHARSET_INFO *cs= thd->variables.character_set_results;
+ if (!cs || cs == &my_charset_bin)
+ cs= system_charset_info;
+ if (tmp.copy_printable_hhhh(cs, system_charset_info, from, length))
+ return net_store_data((const uchar*)"", 0);
+ return net_store_data((const uchar *) tmp.ptr(), tmp.length());
+}
+
+
+bool Protocol_text::store_str(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
#ifndef DBUG_OFF
+ DBUG_PRINT("info", ("Protocol_text::store field %u : %.*b", field_pos,
+ (int) length, (length == 0 ? "" : from)));
+ DBUG_ASSERT(field_handlers == 0 || field_pos < field_count);
DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_STRING));
field_pos++;
#endif
@@ -1156,18 +1227,19 @@ bool Protocol_text::store(const char *from, size_t length,
}
-bool Protocol_text::store(const char *from, size_t length,
- CHARSET_INFO *fromcs)
+bool Protocol_text::store_numeric_zerofill_str(const char *from,
+ size_t length,
+ protocol_send_type_t send_type)
{
- CHARSET_INFO *tocs= this->thd->variables.character_set_results;
#ifndef DBUG_OFF
- DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos,
- field_count, (int) length, (length == 0 ? "" : from)));
+ DBUG_PRINT("info",
+ ("Protocol_text::store_numeric_zerofill_str field %u : %.*b",
+ field_pos, (int) length, (length == 0 ? "" : from)));
DBUG_ASSERT(field_handlers == 0 || field_pos < field_count);
- DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_STRING));
+ DBUG_ASSERT(valid_handler(field_pos, send_type));
field_pos++;
#endif
- return store_string_aux(from, length, fromcs, tocs);
+ return store_numeric_string_aux(from, length);
}
@@ -1178,8 +1250,8 @@ bool Protocol_text::store_tiny(longlong from)
field_pos++;
#endif
char buff[22];
- return net_store_data((uchar*) buff,
- (size_t) (int10_to_str((int) from, buff, -10) - buff));
+ size_t length= (size_t) (int10_to_str((int) from, buff, -10) - buff);
+ return store_numeric_string_aux(buff, length);
}
@@ -1190,9 +1262,8 @@ bool Protocol_text::store_short(longlong from)
field_pos++;
#endif
char buff[22];
- return net_store_data((uchar*) buff,
- (size_t) (int10_to_str((int) from, buff, -10) -
- buff));
+ size_t length= (size_t) (int10_to_str((int) from, buff, -10) - buff);
+ return store_numeric_string_aux(buff, length);
}
@@ -1203,9 +1274,9 @@ bool Protocol_text::store_long(longlong from)
field_pos++;
#endif
char buff[22];
- return net_store_data((uchar*) buff,
- (size_t) (int10_to_str((long int)from, buff,
- (from <0)?-10:10)-buff));
+ size_t length= (size_t) (int10_to_str((long int)from, buff,
+ (from < 0) ? - 10 : 10) - buff);
+ return store_numeric_string_aux(buff, length);
}
@@ -1216,10 +1287,10 @@ bool Protocol_text::store_longlong(longlong from, bool unsigned_flag)
field_pos++;
#endif
char buff[22];
- return net_store_data((uchar*) buff,
- (size_t) (longlong10_to_str(from,buff,
- unsigned_flag ? 10 : -10)-
- buff));
+ size_t length= (size_t) (longlong10_to_str(from, buff,
+ unsigned_flag ? 10 : -10) -
+ buff);
+ return store_numeric_string_aux(buff, length);
}
@@ -1231,29 +1302,29 @@ bool Protocol_text::store_decimal(const my_decimal *d)
#endif
StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
(void) d->to_string(&str);
- return net_store_data((uchar*) str.ptr(), str.length());
+ return store_numeric_string_aux(str.ptr(), str.length());
}
-bool Protocol_text::store(float from, uint32 decimals, String *buffer)
+bool Protocol_text::store_float(float from, uint32 decimals)
{
#ifndef DBUG_OFF
DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_FLOAT));
field_pos++;
#endif
- Float(from).to_string(buffer, decimals);
- return net_store_data((uchar*) buffer->ptr(), buffer->length());
+ Float(from).to_string(&buffer, decimals);
+ return store_numeric_string_aux(buffer.ptr(), buffer.length());
}
-bool Protocol_text::store(double from, uint32 decimals, String *buffer)
+bool Protocol_text::store_double(double from, uint32 decimals)
{
#ifndef DBUG_OFF
DBUG_ASSERT(valid_handler(field_pos, PROTOCOL_SEND_DOUBLE));
field_pos++;
#endif
- buffer->set_real(from, decimals, thd->charset());
- return net_store_data((uchar*) buffer->ptr(), buffer->length());
+ buffer.set_real(from, decimals, thd->charset());
+ return store_numeric_string_aux(buffer.ptr(), buffer.length());
}
@@ -1261,12 +1332,6 @@ bool Protocol_text::store(Field *field)
{
if (field->is_null())
return store_null();
-#ifndef DBUG_OFF
- field_pos++;
-#endif
- char buff[MAX_FIELD_WIDTH];
- String str(buff,sizeof(buff), &my_charset_bin);
- CHARSET_INFO *tocs= this->thd->variables.character_set_results;
#ifdef DBUG_ASSERT_EXISTS
TABLE *table= field->table;
my_bitmap_map *old_map= 0;
@@ -1274,13 +1339,14 @@ bool Protocol_text::store(Field *field)
old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
- field->val_str(&str);
+ bool rc= field->send(this);
+
#ifdef DBUG_ASSERT_EXISTS
if (old_map)
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
- return store_string_aux(str.ptr(), str.length(), str.charset(), tocs);
+ return rc;
}
@@ -1292,7 +1358,7 @@ bool Protocol_text::store(MYSQL_TIME *tm, int decimals)
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= my_datetime_to_str(tm, buff, decimals);
- return net_store_data((uchar*) buff, length);
+ return store_numeric_string_aux(buff, length);
}
@@ -1304,7 +1370,7 @@ bool Protocol_text::store_date(MYSQL_TIME *tm)
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
size_t length= my_date_to_str(tm, buff);
- return net_store_data((uchar*) buff, length);
+ return store_numeric_string_aux(buff, length);
}
@@ -1316,7 +1382,7 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
#endif
char buff[MAX_DATE_STRING_REP_LENGTH];
uint length= my_time_to_str(tm, buff, decimals);
- return net_store_data((uchar*) buff, length);
+ return store_numeric_string_aux(buff, length);
}
/**
@@ -1398,16 +1464,8 @@ void Protocol_binary::prepare_for_resend()
}
-bool Protocol_binary::store(const char *from, size_t length,
- CHARSET_INFO *fromcs)
-{
- CHARSET_INFO *tocs= thd->variables.character_set_results;
- field_pos++;
- return store_string_aux(from, length, fromcs, tocs);
-}
-
-bool Protocol_binary::store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+bool Protocol_binary::store_str(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
@@ -1469,14 +1527,14 @@ bool Protocol_binary::store_decimal(const my_decimal *d)
{
#ifndef DBUG_OFF
DBUG_ASSERT(0); // This method is not used yet
- field_pos++;
#endif
StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
(void) d->to_string(&str);
- return store(str.ptr(), str.length(), str.charset());
+ return store_str(str.ptr(), str.length(), str.charset(),
+ thd->variables.character_set_results);
}
-bool Protocol_binary::store(float from, uint32 decimals, String *buffer)
+bool Protocol_binary::store_float(float from, uint32 decimals)
{
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1487,7 +1545,7 @@ bool Protocol_binary::store(float from, uint32 decimals, String *buffer)
}
-bool Protocol_binary::store(double from, uint32 decimals, String *buffer)
+bool Protocol_binary::store_double(double from, uint32 decimals)
{
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
@@ -1501,12 +1559,12 @@ bool Protocol_binary::store(double from, uint32 decimals, String *buffer)
bool Protocol_binary::store(Field *field)
{
/*
- We should not increment field_pos here as send_binary() will call another
+ We should not increment field_pos here as send() will call another
protocol function to do this for us
*/
if (field->is_null())
return store_null();
- return field->send_binary(this);
+ return field->send(this);
}
diff --git a/sql/protocol.h b/sql/protocol.h
index 3b2c905ed9e..eb11304a4d5 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -2,6 +2,7 @@
#define PROTOCOL_INCLUDED
/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2020, MariaDB Corporation.
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
@@ -49,18 +50,25 @@ protected:
}
#endif
uint field_count;
-#ifndef EMBEDDED_LIBRARY
- bool net_store_data(const uchar *from, size_t length);
- bool net_store_data_cs(const uchar *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
-#else
virtual bool net_store_data(const uchar *from, size_t length);
virtual bool net_store_data_cs(const uchar *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ virtual bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
+ bool, bool);
+ virtual bool net_send_error_packet(THD *, uint, const char *, const char *);
+#ifdef EMBEDDED_LIBRARY
char **next_field;
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
#endif
+ bool needs_conversion(CHARSET_INFO *fromcs,
+ CHARSET_INFO *tocs) const
+ {
+ // 'tocs' is set 0 when client issues SET character_set_results=NULL
+ return tocs && !my_charset_same(fromcs, tocs) &&
+ fromcs != &my_charset_bin &&
+ tocs != &my_charset_bin;
+ }
/*
The following two are low-level functions that are invoked from
higher-level store_xxx() funcs. The data is stored into this->packet.
@@ -77,6 +85,8 @@ protected:
virtual bool send_error(uint sql_errno, const char *err_msg,
const char *sql_state);
+ CHARSET_INFO *character_set_results() const;
+
public:
THD *thd;
Protocol(THD *thd_arg) { init(thd_arg); }
@@ -90,6 +100,7 @@ public:
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
+ bool store_warning(const char *from, size_t length);
String *storage_packet() { return packet; }
inline void free() { packet->free(); }
virtual bool write();
@@ -119,25 +130,37 @@ public:
virtual bool store_long(longlong from)=0;
virtual bool store_longlong(longlong from, bool unsigned_flag)=0;
virtual bool store_decimal(const my_decimal *)=0;
- virtual bool store(const char *from, size_t length, CHARSET_INFO *cs)=0;
- virtual bool store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0;
- bool store_str(const char *s, CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
- {
- DBUG_ASSERT(s);
- return store(s, (uint) strlen(s), fromcs, tocs);
- }
- bool store_str(const LEX_CSTRING &s, CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
- {
- return store(s.str, (uint) s.length, fromcs, tocs);
- }
- virtual bool store(float from, uint32 decimals, String *buffer)=0;
- virtual bool store(double from, uint32 decimals, String *buffer)=0;
+ virtual bool store_str(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)=0;
+ virtual bool store_float(float from, uint32 decimals)=0;
+ virtual bool store_double(double from, uint32 decimals)=0;
virtual bool store(MYSQL_TIME *time, int decimals)=0;
virtual bool store_date(MYSQL_TIME *time)=0;
virtual bool store_time(MYSQL_TIME *time, int decimals)=0;
virtual bool store(Field *field)=0;
+ // Various useful wrappers for the virtual store*() methods.
+ // Backward wrapper for store_str()
+ bool store(const char *from, size_t length, CHARSET_INFO *cs)
+ {
+ return store_str(from, length, cs, character_set_results());
+ }
+ bool store_lex_cstring(const LEX_CSTRING &s,
+ CHARSET_INFO *fromcs,
+ CHARSET_INFO *tocs)
+ {
+ return store_str(s.str, (uint) s.length, fromcs, tocs);
+ }
+ bool store_binary_string(const char *str, size_t length)
+ {
+ return store_str(str, (uint) length, &my_charset_bin, &my_charset_bin);
+ }
+ bool store_ident(const LEX_CSTRING &s)
+ {
+ return store_lex_cstring(s, system_charset_info, character_set_results());
+ }
+ // End of wrappers
+
virtual bool send_out_parameters(List<Item_param> *sp_params)=0;
#ifdef EMBEDDED_LIBRARY
bool begin_dataset();
@@ -152,10 +175,14 @@ public:
Before adding a new type, please make sure
there is enough storage for it in Query_cache_query_flags.
*/
- PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1, PROTOCOL_LOCAL= 2
+ PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1, PROTOCOL_LOCAL= 2,
+ PROTOCOL_DISCARD= 3 /* Should be last, not used by Query_cache */
};
virtual enum enum_protocol_type type()= 0;
+ virtual bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
+ bool net_send_error(THD *thd, uint sql_errno, const char *err,
+ const char* sqlstate);
void end_statement();
friend int send_answer_1(Protocol *protocol, String *s1, String *s2,
@@ -168,6 +195,8 @@ public:
class Protocol_text :public Protocol
{
+ StringBuffer<FLOATING_POINT_BUFFER> buffer;
+ bool store_numeric_string_aux(const char *from, size_t length);
public:
Protocol_text(THD *thd_arg, ulong prealloc= 0)
:Protocol(thd_arg)
@@ -175,71 +204,73 @@ public:
if (prealloc)
packet->alloc(prealloc);
}
- virtual void prepare_for_resend();
- virtual bool store_null();
- virtual bool store_tiny(longlong from);
- virtual bool store_short(longlong from);
- virtual bool store_long(longlong from);
- virtual bool store_longlong(longlong from, bool unsigned_flag);
- virtual bool store_decimal(const my_decimal *);
- virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
- virtual bool store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
- virtual bool store(MYSQL_TIME *time, int decimals);
- virtual bool store_date(MYSQL_TIME *time);
- virtual bool store_time(MYSQL_TIME *time, int decimals);
- virtual bool store(float nr, uint32 decimals, String *buffer);
- virtual bool store(double from, uint32 decimals, String *buffer);
- virtual bool store(Field *field);
-
- virtual bool send_out_parameters(List<Item_param> *sp_params);
+ void prepare_for_resend() override;
+ bool store_null() override;
+ bool store_tiny(longlong from) override;
+ bool store_short(longlong from) override;
+ bool store_long(longlong from) override;
+ bool store_longlong(longlong from, bool unsigned_flag) override;
+ bool store_decimal(const my_decimal *) override;
+ bool store_str(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs) override;
+ bool store(MYSQL_TIME *time, int decimals) override;
+ bool store_date(MYSQL_TIME *time) override;
+ bool store_time(MYSQL_TIME *time, int decimals) override;
+ bool store_float(float nr, uint32 decimals) override;
+ bool store_double(double from, uint32 decimals) override;
+ bool store(Field *field) override;
+
+ bool send_out_parameters(List<Item_param> *sp_params) override;
+
+ bool store_numeric_zerofill_str(const char *from, size_t length,
+ protocol_send_type_t send_type);
+
#ifdef EMBEDDED_LIBRARY
- void remove_last_row();
+ void remove_last_row() override;
#endif
- bool store_field_metadata(const THD *thd, const Send_field &field,
- CHARSET_INFO *charset_for_protocol,
- uint pos);
- bool store_field_metadata(THD *thd, Item *item, uint pos);
+ virtual bool store_field_metadata(const THD *thd, const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos);
+ bool store_item_metadata(THD *thd, Item *item, uint pos);
bool store_field_metadata_for_list_fields(const THD *thd, Field *field,
const TABLE_LIST *table_list,
uint pos);
- virtual enum enum_protocol_type type() { return PROTOCOL_TEXT; };
+ enum enum_protocol_type type() override { return PROTOCOL_TEXT; };
};
-class Protocol_binary :public Protocol
+class Protocol_binary final :public Protocol
{
private:
uint bit_fields;
public:
Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
- virtual bool prepare_for_send(uint num_columns);
- virtual void prepare_for_resend();
+ bool prepare_for_send(uint num_columns) override;
+ void prepare_for_resend() override;
#ifdef EMBEDDED_LIBRARY
- virtual bool write();
- bool net_store_data(const uchar *from, size_t length);
+ bool write() override;
+ bool net_store_data(const uchar *from, size_t length) override;
bool net_store_data_cs(const uchar *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs) override;
#endif
- virtual bool store_null();
- virtual bool store_tiny(longlong from);
- virtual bool store_short(longlong from);
- virtual bool store_long(longlong from);
- virtual bool store_longlong(longlong from, bool unsigned_flag);
- virtual bool store_decimal(const my_decimal *);
- virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
- virtual bool store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
- virtual bool store(MYSQL_TIME *time, int decimals);
- virtual bool store_date(MYSQL_TIME *time);
- virtual bool store_time(MYSQL_TIME *time, int decimals);
- virtual bool store(float nr, uint32 decimals, String *buffer);
- virtual bool store(double from, uint32 decimals, String *buffer);
- virtual bool store(Field *field);
-
- virtual bool send_out_parameters(List<Item_param> *sp_params);
-
- virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
+ bool store_null() override;
+ bool store_tiny(longlong from) override;
+ bool store_short(longlong from) override;
+ bool store_long(longlong from) override;
+ bool store_longlong(longlong from, bool unsigned_flag) override;
+ bool store_decimal(const my_decimal *) override;
+ bool store_str(const char *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs) override;
+ bool store(MYSQL_TIME *time, int decimals) override;
+ bool store_date(MYSQL_TIME *time) override;
+ bool store_time(MYSQL_TIME *time, int decimals) override;
+ bool store_float(float nr, uint32 decimals) override;
+ bool store_double(double from, uint32 decimals) override;
+ bool store(Field *field) override;
+
+ bool send_out_parameters(List<Item_param> *sp_params) override;
+
+ enum enum_protocol_type type() override { return PROTOCOL_BINARY; };
};
@@ -257,40 +288,41 @@ public:
select_send::send_data() & co., and also uses Protocol_discard object.
*/
-class Protocol_discard : public Protocol_text
+class Protocol_discard final : public Protocol
{
public:
- Protocol_discard(THD *thd_arg) : Protocol_text(thd_arg) {}
- bool write() { return 0; }
- bool send_result_set_metadata(List<Item> *, uint) { return 0; }
- bool send_eof(uint, uint) { return 0; }
- void prepare_for_resend() { IF_DBUG(field_pos= 0,); }
+ Protocol_discard(THD *thd_arg) : Protocol(thd_arg) {}
+ bool write() override { return 0; }
+ bool send_result_set_metadata(List<Item> *, uint) override { return 0; }
+ bool send_eof(uint, uint) override { return 0; }
+ void prepare_for_resend() override { IF_DBUG(field_pos= 0,); }
+ bool send_out_parameters(List<Item_param> *sp_params) override { return false; }
/*
Provide dummy overrides for any storage methods so that we
avoid allocating and copying of data
*/
- bool store_null() { return false; }
- bool store_tiny(longlong) { return false; }
- bool store_short(longlong) { return false; }
- bool store_long(longlong) { return false; }
- bool store_longlong(longlong, bool) { return false; }
- bool store_decimal(const my_decimal *) { return false; }
- bool store(const char *, size_t, CHARSET_INFO *) { return false; }
- bool store(const char *, size_t, CHARSET_INFO *, CHARSET_INFO *) { return false; }
- bool store(MYSQL_TIME *, int) { return false; }
- bool store_date(MYSQL_TIME *) { return false; }
- bool store_time(MYSQL_TIME *, int) { return false; }
- bool store(float, uint32, String *) { return false; }
- bool store(double, uint32, String *) { return false; }
- bool store(Field *) { return false; }
-
+ bool store_null() override { return false; }
+ bool store_tiny(longlong) override { return false; }
+ bool store_short(longlong) override { return false; }
+ bool store_long(longlong) override { return false; }
+ bool store_longlong(longlong, bool) override { return false; }
+ bool store_decimal(const my_decimal *) override { return false; }
+ bool store_str(const char *, size_t, CHARSET_INFO *, CHARSET_INFO *) override
+ {
+ return false;
+ }
+ bool store(MYSQL_TIME *, int) override { return false; }
+ bool store_date(MYSQL_TIME *) override { return false; }
+ bool store_time(MYSQL_TIME *, int) override { return false; }
+ bool store_float(float, uint32) override { return false; }
+ bool store_double(double, uint32) override { return false; }
+ bool store(Field *) override { return false; }
+ enum enum_protocol_type type() override { return PROTOCOL_DISCARD; };
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
-bool net_send_error(THD *thd, uint sql_errno, const char *err,
- const char* sqlstate);
void net_send_progress_packet(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
uchar *net_store_data(uchar *to,int32 from);
diff --git a/sql/proxy_protocol.cc b/sql/proxy_protocol.cc
index 550813c6457..689d1af88f0 100644
--- a/sql/proxy_protocol.cc
+++ b/sql/proxy_protocol.cc
@@ -364,7 +364,8 @@ static int parse_networks(const char *subnets_str, subnet **out_subnets, size_t
}
max_subnets= MY_MAX(3,strlen(subnets_str)/2);
- subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL);
+ subnets= (subnet *)my_malloc(PSI_INSTRUMENT_ME,
+ max_subnets * sizeof(subnet), MY_ZEROFILL);
/* Check for special case '*'. */
if (strcmp(subnets_str, "*") == 0)
diff --git a/sql/records.cc b/sql/records.cc
index 817caada8e2..900eacf5943 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -38,8 +38,8 @@
static int rr_quick(READ_RECORD *info);
int rr_sequential(READ_RECORD *info);
static int rr_from_tempfile(READ_RECORD *info);
-static int rr_unpack_from_tempfile(READ_RECORD *info);
-static int rr_unpack_from_buffer(READ_RECORD *info);
+template<bool> static int rr_unpack_from_tempfile(READ_RECORD *info);
+template<bool,bool> static int rr_unpack_from_buffer(READ_RECORD *info);
int rr_from_pointers(READ_RECORD *info);
static int rr_from_cache(READ_RECORD *info);
static int init_rr_cache(THD *thd, READ_RECORD *info);
@@ -187,23 +187,24 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
bool disable_rr_cache)
{
IO_CACHE *tempfile;
- SORT_ADDON_FIELD *addon_field= filesort ? filesort->addon_field : 0;
DBUG_ENTER("init_read_record");
+ const bool using_addon_fields= filesort && filesort->using_addon_fields();
+ bool using_packed_sortkeys= filesort && filesort->using_packed_sortkeys();
+
bzero((char*) info,sizeof(*info));
info->thd=thd;
info->table=table;
- info->addon_field= addon_field;
+ info->sort_info= filesort;
if ((table->s->tmp_table == INTERNAL_TMP_TABLE) &&
- !addon_field)
+ !using_addon_fields)
(void) table->file->extra(HA_EXTRA_MMAP);
- if (addon_field)
+ if (using_addon_fields)
{
- info->rec_buf= (uchar*) filesort->addon_buf.str;
- info->ref_length= (uint)filesort->addon_buf.length;
- info->unpack= filesort->unpack;
+ info->rec_buf= filesort->addon_fields->get_addon_buf();
+ info->ref_length= filesort->addon_fields->get_addon_buf_length();
}
else
{
@@ -223,9 +224,20 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
if (tempfile && !(select && select->quick))
{
- DBUG_PRINT("info",("using rr_from_tempfile"));
- info->read_record_func=
- addon_field ? rr_unpack_from_tempfile : rr_from_tempfile;
+ if (using_addon_fields)
+ {
+ DBUG_PRINT("info",("using rr_from_tempfile"));
+ if (filesort->addon_fields->using_packed_addons())
+ info->read_record_func= rr_unpack_from_tempfile<true>;
+ else
+ info->read_record_func= rr_unpack_from_tempfile<false>;
+ }
+ else
+ {
+ DBUG_PRINT("info",("using rr_from_tempfile"));
+ info->read_record_func= rr_from_tempfile;
+ }
+
info->io_cache= tempfile;
reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0);
info->ref_pos=table->file->ref;
@@ -239,7 +251,7 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
and filesort->io_cache is read sequentially
*/
if (!disable_rr_cache &&
- !addon_field &&
+ !using_addon_fields &&
thd->variables.read_rnd_buff_size &&
!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
(table->db_stat & HA_READ_ONLY ||
@@ -264,16 +276,38 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
DBUG_PRINT("info",("using rr_quick"));
info->read_record_func= rr_quick;
}
- else if (filesort && filesort->record_pointers)
+ else if (filesort && filesort->has_filesort_result_in_memory())
{
DBUG_PRINT("info",("using record_pointers"));
if (unlikely(table->file->ha_rnd_init_with_error(0)))
DBUG_RETURN(1);
+
info->cache_pos= filesort->record_pointers;
- info->cache_end= (info->cache_pos+
- filesort->return_rows * info->ref_length);
- info->read_record_func=
- addon_field ? rr_unpack_from_buffer : rr_from_pointers;
+ if (using_addon_fields)
+ {
+ DBUG_PRINT("info",("using rr_unpack_from_buffer"));
+ DBUG_ASSERT(filesort->sorted_result_in_fsbuf);
+ info->unpack_counter= 0;
+
+ if (filesort->using_packed_addons())
+ {
+ info->read_record_func= using_packed_sortkeys ?
+ rr_unpack_from_buffer<true, true> :
+ rr_unpack_from_buffer<true, false>;
+ }
+ else
+ {
+ info->read_record_func= using_packed_sortkeys ?
+ rr_unpack_from_buffer<false, true> :
+ rr_unpack_from_buffer<false, false>;
+ }
+ }
+ else
+ {
+ info->cache_end= (info->cache_pos+
+ filesort->return_rows * info->ref_length);
+ info->read_record_func= rr_from_pointers;
+ }
}
else if (table->file->keyread_enabled())
{
@@ -518,7 +552,11 @@ static int rr_from_tempfile(READ_RECORD *info)
the fields values use in the result set from this buffer into their
positions in the regular record buffer.
- @param info Reference to the context including record descriptors
+ @param info Reference to the context including record
+ descriptors
+ @param Packed_addon_fields Are the addon fields packed?
+ This is a compile-time constant, to
+ avoid if (....) tests during execution.
@retval
0 Record successfully read.
@@ -526,12 +564,38 @@ static int rr_from_tempfile(READ_RECORD *info)
-1 There is no record to be read anymore.
*/
+template<bool Packed_addon_fields>
static int rr_unpack_from_tempfile(READ_RECORD *info)
{
- if (my_b_read(info->io_cache, info->rec_buf, info->ref_length))
- return -1;
- (*info->unpack)(info->addon_field, info->rec_buf,
- info->rec_buf + info->ref_length);
+ uchar *destination= info->rec_buf;
+#ifndef DBUG_OFF
+ my_off_t where= my_b_tell(info->io_cache);
+#endif
+ if (Packed_addon_fields)
+ {
+ const uint len_sz= Addon_fields::size_of_length_field;
+
+ // First read length of the record.
+ if (my_b_read(info->io_cache, destination, len_sz))
+ return -1;
+ uint res_length= Addon_fields::read_addon_length(destination);
+ DBUG_PRINT("info", ("rr_unpack from %llu to %p sz %u",
+ static_cast<ulonglong>(where),
+ destination, res_length));
+ DBUG_ASSERT(res_length > len_sz);
+ DBUG_ASSERT(info->sort_info->using_addon_fields());
+
+ // Then read the rest of the record.
+ if (my_b_read(info->io_cache, destination + len_sz, res_length - len_sz))
+ return -1; /* purecov: inspected */
+ }
+ else
+ {
+ if (my_b_read(info->io_cache, destination, info->ref_length))
+ return -1;
+ }
+
+ info->sort_info->unpack_addon_fields<Packed_addon_fields>(destination);
return 0;
}
@@ -568,7 +632,11 @@ int rr_from_pointers(READ_RECORD *info)
the fields values use in the result set from this buffer into their
positions in the regular record buffer.
- @param info Reference to the context including record descriptors
+ @param info Reference to the context including record
+ descriptors
+ @param Packed_addon_fields Are the addon fields packed?
+ This is a compile-time constant, to
+ avoid if (....) tests during execution.
@retval
0 Record successfully read.
@@ -576,13 +644,22 @@ int rr_from_pointers(READ_RECORD *info)
-1 There is no record to be read anymore.
*/
+template<bool Packed_addon_fields, bool Packed_sort_keys>
static int rr_unpack_from_buffer(READ_RECORD *info)
{
- if (info->cache_pos == info->cache_end)
+ if (info->unpack_counter == info->sort_info->return_rows)
return -1; /* End of buffer */
- (*info->unpack)(info->addon_field, info->cache_pos,
- info->cache_end);
- info->cache_pos+= info->ref_length;
+
+ uchar *record= info->sort_info->get_sorted_record(
+ static_cast<uint>(info->unpack_counter));
+
+ uint sort_length= Packed_sort_keys ?
+ Sort_keys::read_sortkey_length(record):
+ info->sort_info->get_sort_length();
+
+ uchar *plen= record + sort_length;
+ info->sort_info->unpack_addon_fields<Packed_addon_fields>(plen);
+ info->unpack_counter++;
return 0;
}
/* cacheing of records from a database */
@@ -717,3 +794,39 @@ static int rr_cmp(uchar *a,uchar *b)
return (int) a[7] - (int) b[7];
#endif
}
+
+
+/**
+ Copy (unpack) values appended to sorted fields from a buffer back to
+ their regular positions specified by the Field::ptr pointers.
+
+ @param addon_field Array of descriptors for appended fields
+ @param buff Buffer which to unpack the value from
+
+ @note
+ The function is supposed to be used only as a callback function
+ when getting field values for the sorted result set.
+
+*/
+template<bool Packed_addon_fields>
+inline void SORT_INFO::unpack_addon_fields(uchar *buff)
+{
+ SORT_ADDON_FIELD *addonf= addon_fields->begin();
+ uchar *buff_end= buff + sort_buffer_size();
+ const uchar *start_of_record= buff + addonf->offset;
+
+ for ( ; addonf != addon_fields->end() ; addonf++)
+ {
+ Field *field= addonf->field;
+ if (addonf->null_bit && (addonf->null_bit & buff[addonf->null_offset]))
+ {
+ field->set_null();
+ continue;
+ }
+ field->set_notnull();
+ if (Packed_addon_fields)
+ start_of_record= field->unpack(field->ptr, start_of_record, buff_end, 0);
+ else
+ field->unpack(field->ptr, buff + addonf->offset, buff_end, 0);
+ }
+}
diff --git a/sql/records.h b/sql/records.h
index 74f64cd286f..272bbd0d9b5 100644
--- a/sql/records.h
+++ b/sql/records.h
@@ -59,13 +59,23 @@ struct READ_RECORD
THD *thd;
SQL_SELECT *select;
uint ref_length, reclength, rec_cache_size, error_offset;
+
+ /**
+ Counting records when reading result from filesort().
+ Used when filesort leaves the result in the filesort buffer.
+ */
+ ha_rows unpack_counter;
+
uchar *ref_pos; /* pointer to form->refpos */
uchar *rec_buf; /* to read field values after filesort */
uchar *cache,*cache_pos,*cache_end,*read_positions;
- struct st_sort_addon_field *addon_field; /* Pointer to the fields info */
+
+ /*
+ Structure storing information about sorting
+ */
+ SORT_INFO *sort_info;
struct st_io_cache *io_cache;
bool print_error;
- void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *);
int read_record() { return read_record_func(this); }
uchar *record() const { return table->record[0]; }
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 18fc3d9431a..1df85759a9c 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -101,7 +101,7 @@ void THD::unregister_slave()
mysql_mutex_lock(&LOCK_thd_data);
slave_info= 0;
mysql_mutex_unlock(&LOCK_thd_data);
- delete old_si;
+ my_free(old_si);
binlog_dump_thread_count--;
}
}
@@ -122,9 +122,10 @@ int THD::register_slave(uchar *packet, size_t packet_length)
uchar *p= packet, *p_end= packet + packet_length;
const char *errmsg= "Wrong parameters to function register_slave";
- if (check_access(this, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
+ if (check_access(this, PRIV_COM_REGISTER_SLAVE, any_db, NULL, NULL, 0, 0))
return 1;
- if (!(si= new Slave_info))
+ if (!(si= (Slave_info*)my_malloc(key_memory_SLAVE_INFO, sizeof(Slave_info),
+ MYF(MY_WME))))
return 1;
variables.server_id= si->server_id= uint4korr(p);
@@ -147,6 +148,9 @@ int THD::register_slave(uchar *packet, size_t packet_length)
if (!(si->master_id= uint4korr(p)))
si->master_id= global_system_variables.server_id;
+ if (!*si->host)
+ ::strmake(si->host, main_security_ctx.host_or_ip, sizeof(si->host));
+
unregister_slave();
mysql_mutex_lock(&LOCK_thd_data);
slave_info= si;
diff --git a/sql/rowid_filter.cc b/sql/rowid_filter.cc
index 9faab828871..4a3746d72db 100644
--- a/sql/rowid_filter.cc
+++ b/sql/rowid_filter.cc
@@ -110,10 +110,12 @@ Range_rowid_filter_cost_info::set_adjusted_gain_param(double access_cost_factor)
void Range_rowid_filter_cost_info::init(Rowid_filter_container_type cont_type,
TABLE *tab, uint idx)
{
+ DBUG_ASSERT(tab->opt_range_keys.is_set(idx));
+
container_type= cont_type;
table= tab;
key_no= idx;
- est_elements= (ulonglong) (table->quick_rows[key_no]);
+ est_elements= (ulonglong) table->opt_range[key_no].rows;
b= build_cost(container_type);
selectivity= est_elements/((double) table->stat_records());
a= avg_access_and_eval_gain_per_row(container_type);
@@ -134,8 +136,9 @@ double
Range_rowid_filter_cost_info::build_cost(Rowid_filter_container_type cont_type)
{
double cost= 0;
+ DBUG_ASSERT(table->opt_range_keys.is_set(key_no));
- cost+= table->quick_index_only_costs[key_no];
+ cost+= table->opt_range[key_no].index_only_cost;
switch (cont_type) {
@@ -345,7 +348,7 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
uint key_no;
key_map usable_range_filter_keys;
usable_range_filter_keys.clear_all();
- key_map::Iterator it(quick_keys);
+ key_map::Iterator it(opt_range_keys);
/*
From all indexes that can be used for range accesses select only such that
@@ -357,9 +360,9 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
{
if (!(file->index_flags(key_no, 0, 1) & HA_DO_RANGE_FILTER_PUSHDOWN)) // !1
continue;
- if (key_no == s->primary_key && file->primary_key_is_clustered()) // !2
+ if (file->is_clustering_key(key_no)) // !2
continue;
- if (quick_rows[key_no] >
+ if (opt_range[key_no].rows >
get_max_range_rowid_filter_elems_for_table(thd, this,
SORTED_ARRAY_CONTAINER)) // !3
continue;
@@ -484,7 +487,7 @@ TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
clustered primary key it would, but the current InnoDB code does not
allow it. Later this limitation will be lifted
*/
- if (access_key_no == s->primary_key && file->primary_key_is_clustered())
+ if (file->is_clustering_key(access_key_no))
return 0;
Range_rowid_filter_cost_info *best_filter= 0;
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
index 3d1c3102e49..5c4a4d9f58a 100644
--- a/sql/rpl_filter.cc
+++ b/sql/rpl_filter.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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,7 +25,7 @@
#define TABLE_RULE_ARR_SIZE 16
Rpl_filter::Rpl_filter() :
- parallel_mode(SLAVE_PARALLEL_CONSERVATIVE),
+ parallel_mode(SLAVE_PARALLEL_OPTIMISTIC),
table_rules_on(0),
do_table_inited(0), ignore_table_inited(0),
wild_do_table_inited(0), wild_ignore_table_inited(0)
@@ -285,7 +286,7 @@ Rpl_filter::parse_filter_rule(const char* spec, Add_filter add)
if (!spec)
return false;
- if (! (ptr= my_strdup(spec, MYF(MY_WME))))
+ if (! (ptr= my_strdup(key_memory_rpl_filter, spec, MYF(MY_WME))))
return true;
pstr= ptr;
@@ -484,8 +485,9 @@ Rpl_filter::add_table_rule(HASH* h, const char* table_spec)
if (!dot) return 1;
// len is always > 0 because we know the there exists a '.'
uint len = (uint)strlen(table_spec);
- TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
- + len, MYF(MY_WME));
+ TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(key_memory_TABLE_RULE_ENT,
+ sizeof(TABLE_RULE_ENT) + len,
+ MYF(MY_WME));
if (!e) return 1;
e->db= (char*)e + sizeof(TABLE_RULE_ENT);
e->tbl_name= e->db + (dot - table_spec) + 1;
@@ -506,8 +508,9 @@ Rpl_filter::add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec)
const char* dot = strchr(table_spec, '.');
if (!dot) return 1;
uint len = (uint)strlen(table_spec);
- TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT)
- + len, MYF(MY_WME));
+ TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(key_memory_TABLE_RULE_ENT,
+ sizeof(TABLE_RULE_ENT) + len,
+ MYF(MY_WME));
if (!e) return 1;
e->db= (char*)e + sizeof(TABLE_RULE_ENT);
e->tbl_name= e->db + (dot - table_spec) + 1;
@@ -523,7 +526,7 @@ Rpl_filter::add_string_list(I_List<i_string> *list, const char* spec)
char *str;
i_string *node;
- if (! (str= my_strdup(spec, MYF(MY_WME))))
+ if (! (str= my_strdup(key_memory_rpl_filter, spec, MYF(MY_WME))))
return true;
if (! (node= new i_string(str)))
@@ -594,8 +597,9 @@ void free_table_ent(void* a)
void
Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited)
{
- my_hash_init(h, system_charset_info,TABLE_RULE_HASH_SIZE,0,0,
- get_table_key, free_table_ent, 0);
+ my_hash_init(key_memory_TABLE_RULE_ENT, h,
+ system_charset_info,TABLE_RULE_HASH_SIZE,0,0, get_table_key,
+ free_table_ent, 0);
*h_inited = 1;
}
@@ -603,8 +607,8 @@ Rpl_filter::init_table_rule_hash(HASH* h, bool* h_inited)
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, MYF(0));
+ my_init_dynamic_array(key_memory_TABLE_RULE_ENT, a, sizeof(TABLE_RULE_ENT*),
+ TABLE_RULE_ARR_SIZE, TABLE_RULE_ARR_SIZE, MYF(0));
*a_inited = 1;
}
@@ -619,10 +623,10 @@ Rpl_filter::find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
{
TABLE_RULE_ENT* e ;
get_dynamic(a, (uchar*)&e, i);
- if (!my_wildcmp(system_charset_info, key, key_end,
- (const char*)e->db,
- (const char*)(e->db + e->key_len),
- '\\',wild_one,wild_many))
+ if (!system_charset_info->wildcmp(key, key_end,
+ (const char*)e->db,
+ (const char*)(e->db + e->key_len),
+ '\\', wild_one, wild_many))
return e;
}
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index f58df09623c..af0c4747d2b 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -249,9 +249,10 @@ rpl_slave_state::rpl_slave_state()
{
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),
+ my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, offsetof(element, domain_id),
sizeof(uint32), NULL, rpl_slave_state_free_element, HASH_UNIQUE);
- my_init_dynamic_array(&gtid_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &gtid_sort_array, sizeof(rpl_gtid),
+ 8, 8, MYF(0));
}
@@ -334,7 +335,8 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
}
- if (!(list_elem= (list_element *)my_malloc(sizeof(*list_elem), MYF(MY_WME))))
+ if (!(list_elem= (list_element *)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(*list_elem), MYF(MY_WME))))
return 1;
list_elem->domain_id= domain_id;
list_elem->server_id= server_id;
@@ -368,7 +370,7 @@ rpl_slave_state::get_element(uint32 domain_id)
if (elem)
return elem;
- if (!(elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME))))
+ if (!(elem= (element *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*elem), MYF(MY_WME))))
return NULL;
elem->list= NULL;
elem->domain_id= domain_id;
@@ -421,12 +423,14 @@ rpl_slave_state::truncate_state_table(THD *thd)
TABLE_LIST tlist;
int err= 0;
- tmp_disable_binlog(thd);
- tlist.init_one_table(&MYSQL_SCHEMA_NAME, &rpl_gtid_slave_state_table_name, NULL, TL_WRITE);
- if (!(err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+ tlist.init_one_table(&MYSQL_SCHEMA_NAME, &rpl_gtid_slave_state_table_name,
+ NULL, TL_WRITE);
+ tlist.mdl_request.set_type(MDL_EXCLUSIVE);
+ if (!(err= open_and_lock_tables(thd, &tlist, FALSE,
+ MYSQL_OPEN_IGNORE_LOGGING_FORMAT)))
{
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, "mysql",
- rpl_gtid_slave_state_table_name.str, false);
+ DBUG_ASSERT(!tlist.table->file->row_logging);
+ tlist.table->s->tdc->flush(thd, true);
err= tlist.table->file->ha_truncate();
if (err)
@@ -443,8 +447,6 @@ rpl_slave_state::truncate_state_table(THD *thd)
}
thd->mdl_context.release_transactional_locks();
}
-
- reenable_binlog(thd);
return err;
}
@@ -505,7 +507,7 @@ rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_CSTRING *out_tablename)
Ha_trx_info *ha_info;
uint count = 0;
- for (ha_info= thd->transaction.all.ha_list; ha_info; ha_info= ha_info->next())
+ for (ha_info= thd->transaction->all.ha_list; ha_info; ha_info= ha_info->next())
{
void *trx_hton= ha_info->ht();
auto table_entry= list;
@@ -674,6 +676,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
table_opened= true;
table= tlist.table;
hton= table->s->db_type();
+ table->file->row_logging= 0; // No binary logging
if ((err= gtid_check_rpl_slave_state_table(table)))
goto end;
@@ -1103,8 +1106,9 @@ rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data,
int res= 1;
bool locked= false;
- my_hash_init(&gtid_hash, &my_charset_bin, 32, offsetof(rpl_gtid, domain_id),
- sizeof(uint32), NULL, NULL, HASH_UNIQUE);
+ my_hash_init(PSI_INSTRUMENT_ME, &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])))
@@ -1321,7 +1325,7 @@ gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len)
}
if ((!list || len >= alloc_len) &&
!(list=
- (rpl_gtid *)my_realloc(list,
+ (rpl_gtid *)my_realloc(PSI_INSTRUMENT_ME, list,
(alloc_len= alloc_len*2) * sizeof(rpl_gtid),
MYF(MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR))))
return NULL;
@@ -1455,10 +1459,8 @@ rpl_slave_state::alloc_gtid_pos_table(LEX_CSTRING *table_name, void *hton,
struct gtid_pos_table *p;
char *allocated_str;
- if (!my_multi_malloc(MYF(MY_WME),
- &p, sizeof(*p),
- &allocated_str, table_name->length+1,
- NULL))
+ if (!my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME), &p, sizeof(*p),
+ &allocated_str, table_name->length+1, NULL))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*p) + table_name->length+1));
return NULL;
@@ -1475,9 +1477,9 @@ rpl_slave_state::alloc_gtid_pos_table(LEX_CSTRING *table_name, void *hton,
void rpl_binlog_state::init()
{
- my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id),
+ my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32, offsetof(element, domain_id),
sizeof(uint32), NULL, my_free, HASH_UNIQUE);
- my_init_dynamic_array(&gtid_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &gtid_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0));
mysql_mutex_init(key_LOCK_binlog_state, &LOCK_binlog_state,
MY_MUTEX_INIT_SLOW);
initialized= 1;
@@ -1674,7 +1676,8 @@ rpl_binlog_state::element::update_element(const rpl_gtid *gtid)
}
/* Allocate a new GTID and insert it. */
- lookup_gtid= (rpl_gtid *)my_malloc(sizeof(*lookup_gtid), MYF(MY_WME));
+ lookup_gtid= (rpl_gtid *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*lookup_gtid),
+ MYF(MY_WME));
if (!lookup_gtid)
return 1;
memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid));
@@ -1695,12 +1698,13 @@ rpl_binlog_state::alloc_element_nolock(const rpl_gtid *gtid)
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));
+ elem= (element *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*elem), MYF(MY_WME));
+ lookup_gtid= (rpl_gtid *)my_malloc(PSI_INSTRUMENT_ME, 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,
+ my_hash_init(PSI_INSTRUMENT_ME, &elem->hash, &my_charset_bin, 32,
offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free,
HASH_UNIQUE);
elem->last_gtid= lookup_gtid;
@@ -1773,14 +1777,15 @@ rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no)
}
/* We need to allocate a new, empty element to remember the next seq_no. */
- if (!(elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME))))
+ if (!(elem= (element *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*elem),
+ MYF(MY_WME))))
{
res= 1;
goto end;
}
elem->domain_id= domain_id;
- my_hash_init(&elem->hash, &my_charset_bin, 32,
+ my_hash_init(PSI_INSTRUMENT_ME, &elem->hash, &my_charset_bin, 32,
offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free,
HASH_UNIQUE);
elem->last_gtid= NULL;
@@ -1995,8 +2000,8 @@ rpl_binlog_state::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
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))))
+ if (!(*list= (rpl_gtid *)my_malloc(PSI_INSTRUMENT_ME,
+ alloc_size * sizeof(rpl_gtid), MYF(MY_WME))))
{
res= 1;
goto end;
@@ -2111,7 +2116,7 @@ rpl_binlog_state::drop_domain(DYNAMIC_ARRAY *ids,
DBUG_ENTER("rpl_binlog_state::drop_domain");
- my_init_dynamic_array2(&domain_unique,
+ my_init_dynamic_array2(PSI_INSTRUMENT_ME, &domain_unique,
sizeof(element*), domain_unique_buffer,
sizeof(domain_unique_buffer) / sizeof(element*), 4, 0);
@@ -2236,10 +2241,10 @@ end:
slave_connection_state::slave_connection_state()
{
- my_hash_init(&hash, &my_charset_bin, 32,
+ my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32,
offsetof(entry, gtid) + offsetof(rpl_gtid, domain_id),
sizeof(uint32), NULL, my_free, HASH_UNIQUE);
- my_init_dynamic_array(&gtid_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &gtid_sort_array, sizeof(rpl_gtid), 8, 8, MYF(0));
}
@@ -2283,7 +2288,7 @@ slave_connection_state::load(const char *slave_request, size_t len)
return 0;
for (;;)
{
- if (!(rec= (uchar *)my_malloc(sizeof(entry), MYF(MY_WME))))
+ if (!(rec= (uchar *)my_malloc(PSI_INSTRUMENT_ME, sizeof(entry), MYF(MY_WME))))
return 1;
gtid= &((entry *)rec)->gtid;
if (gtid_parser_helper(&p, end, gtid))
@@ -2386,7 +2391,7 @@ slave_connection_state::update(const rpl_gtid *in_gtid)
return 0;
}
- if (!(e= (entry *)my_malloc(sizeof(*e), MYF(MY_WME))))
+ if (!(e= (entry *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*e), MYF(MY_WME))))
return 1;
e->gtid= *in_gtid;
e->flags= 0;
@@ -2863,7 +2868,7 @@ free_hash_element(void *p)
void
gtid_waiting::init()
{
- my_hash_init(&hash, &my_charset_bin, 32,
+ my_hash_init(PSI_INSTRUMENT_ME, &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);
@@ -2900,7 +2905,7 @@ gtid_waiting::get_entry(uint32 domain_id)
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))))
+ if (!(e= (hash_element *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*e), MYF(MY_WME))))
return NULL;
if (init_queue(&e->queue, 8, offsetof(queue_element, wait_seq_no), 0,
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index b633716bfe5..11541c8000c 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -18,7 +18,7 @@
#include "hash.h"
#include "queues.h"
-
+#include <atomic>
/* Definitions for MariaDB global transaction ID (GTID). */
@@ -27,6 +27,8 @@ extern const LEX_CSTRING rpl_gtid_slave_state_table_name;
class String;
+#define GTID_MAX_STR_LENGTH (10+1+10+1+20)
+
struct rpl_gtid
{
uint32 domain_id;
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index 597a357e4e2..0726697211b 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -37,7 +37,8 @@ injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd)
LOG_INFO log_info;
log->get_current_log(&log_info);
/* !!! binlog_pos does not follow RAII !!! */
- m_start_pos.m_file_name= my_strdup(log_info.log_file_name, MYF(0));
+ m_start_pos.m_file_name= my_strdup(key_memory_binlog_pos,
+ log_info.log_file_name, MYF(0));
m_start_pos.m_file_pos= log_info.pos;
m_thd->lex->start_transaction_opt= 0; /* for begin_trans() */
@@ -99,6 +100,7 @@ int injector::transaction::commit()
}
+#ifdef TO_BE_DELETED
int injector::transaction::use_table(server_id_type sid, table tbl)
{
DBUG_ENTER("injector::transaction::use_table");
@@ -110,12 +112,12 @@ int injector::transaction::use_table(server_id_type sid, table tbl)
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());
+ DBUG_ASSERT(tbl.is_transactional() == tbl.get_table()->file->row_logging_has_trans);
+ error= m_thd->binlog_write_table_map(tbl.get_table(), 0);
m_thd->set_server_id(save_id);
DBUG_RETURN(error);
}
-
+#endif
injector::transaction::binlog_pos injector::transaction::start_pos() const
diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h
index ecf16ba28cf..669a8e29543 100644
--- a/sql/rpl_injector.h
+++ b/sql/rpl_injector.h
@@ -177,8 +177,9 @@ public:
>0 Failure
*/
+#ifdef TO_BE_DELETED
int use_table(server_id_type sid, table tbl);
-
+#endif
/*
Commit a transaction.
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 43a02147496..0649a8f05e8 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -58,7 +58,7 @@ Master_info::Master_info(LEX_CSTRING *connection_name_arg,
connection_name.length= cmp_connection_name.length=
connection_name_arg->length;
if ((connection_name.str= tmp= (char*)
- my_malloc(connection_name_arg->length*2+2, MYF(MY_WME))))
+ my_malloc(PSI_INSTRUMENT_ME, connection_name_arg->length*2+2, MYF(MY_WME))))
{
strmake(tmp, connection_name_arg->str, connection_name.length);
tmp+= connection_name_arg->length+1;
@@ -77,7 +77,7 @@ Master_info::Master_info(LEX_CSTRING *connection_name_arg,
parallel_mode= rpl_filter->get_parallel_mode();
- my_init_dynamic_array(&ignore_server_ids,
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &ignore_server_ids,
sizeof(global_system_variables.server_id), 16, 16,
MYF(0));
bzero((char*) &file, sizeof(file));
@@ -742,7 +742,8 @@ int flush_master_info(Master_info* mi,
char* ignore_server_ids_buf;
{
ignore_server_ids_buf=
- (char *) my_malloc((sizeof(global_system_variables.server_id) * 3 + 1) *
+ (char *) my_malloc(PSI_INSTRUMENT_ME,
+ (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); /* error */
@@ -1098,7 +1099,7 @@ bool Master_info_index::init_all_master_info()
}
/* Initialize Master_info Hash Table */
- if (my_hash_init(&master_info_hash, system_charset_info,
+ if (my_hash_init(PSI_INSTRUMENT_ME, &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))
@@ -1570,6 +1571,8 @@ uint any_slave_sql_running(bool already_locked)
if (!already_locked)
mysql_mutex_lock(&LOCK_active_mi);
+ else
+ mysql_mutex_assert_owner(&LOCK_active_mi);
if (unlikely(abort_loop || !master_info_index))
count= 1;
else
@@ -1739,7 +1742,8 @@ Domain_id_filter::Domain_id_filter() : m_filter(false)
{
for (int i= DO_DOMAIN_IDS; i <= IGNORE_DOMAIN_IDS; i ++)
{
- my_init_dynamic_array(&m_domain_ids[i], sizeof(ulong), 16, 16, MYF(0));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &m_domain_ids[i], sizeof(ulong),
+ 16, 16, MYF(0));
}
}
@@ -1902,7 +1906,7 @@ char *Domain_id_filter::as_string(enum_list_type type)
sz= (sizeof(ulong) * 3 + 1) * (1 + ids->elements);
- if (!(buf= (char *) my_malloc(sz, MYF(MY_WME))))
+ if (!(buf= (char *) my_malloc(PSI_INSTRUMENT_ME, sz, MYF(MY_WME))))
return NULL;
// Store the total number of elements followed by the individual elements.
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 154636480ca..72347d93ad1 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -692,12 +692,14 @@ convert_kill_to_deadlock_error(rpl_group_info *rgi)
static int
is_group_ending(Log_event *ev, Log_event_type event_type)
{
- if (event_type == XID_EVENT)
+ if (event_type == XID_EVENT || event_type == XA_PREPARE_LOG_EVENT)
return 1;
if (event_type == QUERY_EVENT) // COMMIT/ROLLBACK are never compressed
{
Query_log_event *qev = (Query_log_event *)ev;
- if (qev->is_commit())
+ if (qev->is_commit() ||
+ !strncmp(qev->query, STRING_WITH_LEN("XA COMMIT")) ||
+ !strncmp(qev->query, STRING_WITH_LEN("XA ROLLBACK")))
return 1;
if (qev->is_rollback())
return 2;
@@ -813,7 +815,7 @@ do_retry:
else
{
/*
- A failure of a preceeding "parent" transaction may not be
+ A failure of a preceding "parent" transaction may not be
seen by the current one through its own worker_error.
Such induced error gets set by ourselves now.
*/
@@ -1577,7 +1579,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
to allocate, and will not be left with a half-functional thread pool.
*/
if (new_count &&
- !my_multi_malloc(MYF(MY_WME|MY_ZEROFILL),
+ !my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME|MY_ZEROFILL),
&new_list, new_count*sizeof(*new_list),
&rpt_array, new_count*sizeof(*rpt_array),
NULL))
@@ -1809,7 +1811,7 @@ rpl_parallel_thread::get_qev_common(Log_event *ev, ulonglong event_size)
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))))
+ else if(!(qev= (queued_event *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*qev), MYF(0))))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*qev));
return NULL;
@@ -1972,7 +1974,7 @@ rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev,
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))))
+ else if(!(gco= (group_commit_orderer *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*gco), MYF(0))))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gco));
return NULL;
@@ -2122,23 +2124,34 @@ rpl_parallel_thread_pool::release_thread(rpl_parallel_thread *rpt)
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,
+ When `gtid_ev' is not NULL the last worker thread will be returned again,
if it is still available. Otherwise a new worker thread is allocated.
+
+ A worker for XA transaction is determined through xid hashing which
+ ensure for a XA-complete to be scheduled to the same-xid XA-prepare worker.
*/
rpl_parallel_thread *
rpl_parallel_entry::choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
- PSI_stage_info *old_stage, bool reuse)
+ PSI_stage_info *old_stage,
+ Gtid_log_event *gtid_ev)
{
uint32 idx;
Relay_log_info *rli= rgi->rli;
rpl_parallel_thread *thr;
idx= rpl_thread_idx;
- if (!reuse)
+ if (gtid_ev)
{
- ++idx;
- if (idx >= rpl_thread_max)
- idx= 0;
+ if (gtid_ev->flags2 &
+ (Gtid_log_event::FL_COMPLETED_XA | Gtid_log_event::FL_PREPARED_XA))
+ idx= my_hash_sort(&my_charset_bin, gtid_ev->xid.key(),
+ gtid_ev->xid.key_length()) % rpl_thread_max;
+ else
+ {
+ ++idx;
+ if (idx >= rpl_thread_max)
+ idx= 0;
+ }
rpl_thread_idx= idx;
}
thr= rpl_threads[idx];
@@ -2233,7 +2246,7 @@ free_rpl_parallel_entry(void *element)
rpl_parallel::rpl_parallel() :
current(NULL), sql_thread_stopping(false)
{
- my_hash_init(&domain_hash, &my_charset_bin, 32,
+ my_hash_init(PSI_INSTRUMENT_ME, &domain_hash, &my_charset_bin, 32,
offsetof(rpl_parallel_entry, domain_id), sizeof(uint32),
NULL, free_rpl_parallel_entry, HASH_UNIQUE);
}
@@ -2267,7 +2280,7 @@ rpl_parallel::find(uint32 domain_id)
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),
+ if (!my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME|MY_ZEROFILL),
&e, sizeof(*e),
&p, count*sizeof(*p),
NULL))
@@ -2696,7 +2709,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
else
{
DBUG_ASSERT(rli->gtid_skip_flag == GTID_SKIP_TRANSACTION);
- if (typ == XID_EVENT ||
+ if (typ == XID_EVENT || typ == XA_PREPARE_LOG_EVENT ||
(typ == QUERY_EVENT && // COMMIT/ROLLBACK are never compressed
(((Query_log_event *)ev)->is_commit() ||
((Query_log_event *)ev)->is_rollback())))
@@ -2707,10 +2720,11 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
}
}
+ Gtid_log_event *gtid_ev= NULL;
if (typ == GTID_EVENT)
{
rpl_gtid gtid;
- Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
+ gtid_ev= static_cast<Gtid_log_event *>(ev);
uint32 domain_id= (rli->mi->using_gtid == Master_info::USE_GTID_NO ||
rli->mi->parallel_mode <= SLAVE_PARALLEL_MINIMAL ?
0 : gtid_ev->domain_id);
@@ -2749,8 +2763,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
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);
+ e->choose_thread(serial_rgi, &did_enter_cond, &old_stage, gtid_ev);
if (!cur_thread)
{
/* This means we were killed. The error is already signalled. */
@@ -2768,7 +2781,6 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
if (typ == GTID_EVENT)
{
- Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
bool new_gco;
enum_slave_parallel_mode mode= rli->mi->parallel_mode;
uchar gtid_flags= gtid_ev->flags2;
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 0fa28e32291..b88e77d5427 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -347,7 +347,8 @@ struct rpl_parallel_entry {
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);
+ PSI_stage_info *old_stage,
+ Gtid_log_event *gtid_ev);
int queue_master_restart(rpl_group_info *rgi,
Format_description_log_event *fdev);
};
diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc
index 738ae52782d..aa69168d44c 100644
--- a/sql/rpl_reporting.cc
+++ b/sql/rpl_reporting.cc
@@ -19,6 +19,7 @@
#include "rpl_reporting.h"
#include "log.h" // sql_print_error, sql_print_warning,
// sql_print_information
+#include "sql_class.h"
Slave_reporting_capability::Slave_reporting_capability(char const *thread_name)
: m_thread_name(thread_name)
@@ -70,7 +71,8 @@ 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 %s%sInternal MariaDB error code: %d",
+ report_function("%s %s: %s%s %s%sInternal MariaDB error code: %d",
+ (current_thd && current_thd->rgi_fake) ? "" : "Slave",
m_thread_name, pbuff,
(pbuff[0] && *(strend(pbuff)-1) == '.') ? "" : ",",
(extra_info ? extra_info : ""), (extra_info ? ", " : ""),
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 3a2fd5fc123..941616a26d5 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -35,7 +35,7 @@
#include "sql_table.h"
static int count_relay_log_space(Relay_log_info* rli);
-
+bool xa_trans_force_rollback(THD *thd);
/**
Current replication state (hash of last GTID executed, per replication
domain).
@@ -46,8 +46,8 @@ gtid_waiting rpl_global_gtid_waiting;
const char *const Relay_log_info::state_delaying_string = "Waiting until MASTER_DELAY seconds after master executed event";
-Relay_log_info::Relay_log_info(bool is_slave_recovery)
- :Slave_reporting_capability("SQL"),
+Relay_log_info::Relay_log_info(bool is_slave_recovery, const char* thread_name)
+ :Slave_reporting_capability(thread_name),
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),
@@ -74,7 +74,9 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
key_RELAYLOG_COND_relay_log_updated,
key_RELAYLOG_COND_bin_log_updated,
key_file_relaylog,
+ key_file_relaylog_cache,
key_file_relaylog_index,
+ key_file_relaylog_index_cache,
key_RELAYLOG_COND_queue_busy,
key_LOCK_relaylog_end_pos);
#endif
@@ -238,7 +240,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
*/
mysql_mutex_lock(log_lock);
if (relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) ||
- relay_log.open(ln, LOG_BIN, 0, 0, SEQ_READ_APPEND,
+ relay_log.open(ln, 0, 0, SEQ_READ_APPEND,
(ulong)max_relay_log_size, 1, TRUE))
{
mysql_mutex_unlock(log_lock);
@@ -266,7 +268,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
msg= current_thd->get_stmt_da()->message();
goto err;
}
- if (init_io_cache(&info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0,
+ if (init_io_cache(&info_file, info_fd, LOG_BIN_IO_SIZE, READ_CACHE, 0L,0,
MYF(MY_WME)))
{
sql_print_error("Failed to create a cache on relay log info file '%s'",
@@ -301,7 +303,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
error= 1;
}
else if (init_io_cache(&info_file, info_fd,
- IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME)))
+ LOG_BIN_IO_SIZE, READ_CACHE, 0L, 0, MYF(MY_WME)))
{
sql_print_error("Failed to create a cache on relay log info file '%s'",
fname);
@@ -1180,7 +1182,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
DBUG_RETURN(1);
}
mysql_mutex_lock(rli->relay_log.get_log_lock());
- if (rli->relay_log.open(ln, LOG_BIN, 0, 0, SEQ_READ_APPEND,
+ if (rli->relay_log.open(ln, 0, 0, SEQ_READ_APPEND,
(ulong)(rli->max_relay_log_size ? rli->max_relay_log_size :
max_binlog_size), 1, TRUE))
{
@@ -1493,8 +1495,8 @@ Relay_log_info::alloc_inuse_relaylog(const char *name)
rpl_gtid *gtid_list;
gtid_count= relay_log_state.count();
- if (!(gtid_list= (rpl_gtid *)my_malloc(sizeof(*gtid_list)*gtid_count,
- MYF(MY_WME))))
+ if (!(gtid_list= (rpl_gtid *)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(*gtid_list)*gtid_count, MYF(MY_WME))))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gtid_list)*gtid_count);
return 1;
@@ -1642,8 +1644,8 @@ scan_one_gtid_slave_pos_table(THD *thd, HASH *hash, DYNAMIC_ARRAY *array,
}
else
{
- if (!(entry= (struct gtid_pos_element *)my_malloc(sizeof(*entry),
- MYF(MY_WME))))
+ if (!(entry= (struct gtid_pos_element *)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(*entry), MYF(MY_WME))))
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry));
err= 1;
@@ -1884,10 +1886,11 @@ rpl_load_gtid_slave_state(THD *thd)
cb_data.table_list= NULL;
cb_data.default_entry= NULL;
- my_hash_init(&hash, &my_charset_bin, 32,
+ my_hash_init(PSI_INSTRUMENT_ME, &hash, &my_charset_bin, 32,
offsetof(gtid_pos_element, gtid) + offsetof(rpl_gtid, domain_id),
sizeof(uint32), NULL, my_free, HASH_UNIQUE);
- if ((err= my_init_dynamic_array(&array, sizeof(gtid_pos_element), 0, 0, MYF(0))))
+ if ((err= my_init_dynamic_array(PSI_INSTRUMENT_ME, &array,
+ sizeof(gtid_pos_element), 0, 0, MYF(0))))
goto end;
array_inited= true;
@@ -2282,6 +2285,13 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
if (unlikely(error))
{
+ /*
+ trans_rollback above does not rollback XA transactions
+ (todo/fixme consider to do so.
+ */
+ if (thd->transaction->xid_state.is_explicit_XA())
+ xa_trans_force_rollback(thd);
+
thd->mdl_context.release_transactional_locks();
if (thd == rli->sql_driver_thd)
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index f89b40b1efc..4223e015ecd 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -387,11 +387,11 @@ public:
*/
slave_connection_state restart_gtid_pos;
- Relay_log_info(bool is_slave_recovery);
+ Relay_log_info(bool is_slave_recovery, const char* thread_name= "SQL");
~Relay_log_info();
/*
- Invalidate cached until_log_name and group_relay_log_name comparison
+ Invalidate cached until_log_name and group_relay_log_name comparison
result. Should be called after any update of group_realy_log_name if
there chances that sql_thread is running.
*/
diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc
index b2da9092e3a..a230b9f6f29 100644
--- a/sql/rpl_tblmap.cc
+++ b/sql/rpl_tblmap.cc
@@ -34,6 +34,12 @@
table_mapping::table_mapping()
: m_free(0)
{
+#ifdef MYSQL_CLIENT
+ PSI_memory_key psi_key= PSI_NOT_INSTRUMENTED;
+#else
+ PSI_memory_key psi_key= key_memory_table_mapping_root;
+#endif
+
DBUG_ENTER("table_mapping::table_mapping");
/*
No "free_element" function for entries passed here, as the entries are
@@ -42,12 +48,11 @@ table_mapping::table_mapping()
Note that below we don't test if my_hash_init() succeeded. This
constructor is called at startup only.
*/
- (void) my_hash_init(&m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE,
+ (void) my_hash_init(psi_key, &m_table_ids,&my_charset_bin,TABLE_ID_HASH_SIZE,
offsetof(entry,table_id),sizeof(ulonglong),
- 0,0,0);
+ 0,0,0);
/* We don't preallocate any block, this is consistent with m_free=0 above */
- init_alloc_root(&m_mem_root, "table_mapping",
- TABLE_ID_HASH_SIZE*sizeof(entry), 0, MYF(0));
+ init_alloc_root(psi_key, &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 437d58d772f..7c347eba51f 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -19,185 +19,7 @@
#include "rpl_utility.h"
#include "log_event.h"
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-#include "rpl_rli.h"
-#include "sql_select.h"
-/**
- 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
- @param metadata The metadata from the master for the field.
- @return Maximum length of the field in bytes.
-
- The precise values calculated by field->max_display_length() and
- calculated by max_display_length_for_field() can differ (by +1 or -1)
- for integer data types (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT).
- This slight difference is not important here, because we call
- this function only for two *different* integer data types.
- */
-static uint32
-max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
-{
- DBUG_PRINT("debug", ("sql_type: %d, metadata: 0x%x", sql_type, metadata));
- DBUG_ASSERT(metadata >> 16 == 0);
-
- switch (sql_type) {
- case MYSQL_TYPE_NEWDECIMAL:
- return metadata >> 8;
-
- case MYSQL_TYPE_FLOAT:
- return 12;
-
- case MYSQL_TYPE_DOUBLE:
- return 22;
-
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_ENUM:
- return metadata & 0x00ff;
-
- case MYSQL_TYPE_STRING:
- {
- uchar type= metadata >> 8;
- if (type == MYSQL_TYPE_SET || type == MYSQL_TYPE_ENUM)
- return metadata & 0xff;
- else
- /* This is taken from Field_string::unpack. */
- return (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
- }
-
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_TINY:
- return 4;
-
- case MYSQL_TYPE_SHORT:
- return 6;
-
- case MYSQL_TYPE_INT24:
- return 9;
-
- case MYSQL_TYPE_LONG:
- return 11;
-
-#ifdef HAVE_LONG_LONG
- case MYSQL_TYPE_LONGLONG:
- return 20;
-
-#endif
- case MYSQL_TYPE_NULL:
- return 0;
-
- case MYSQL_TYPE_NEWDATE:
- return 3;
-
- case MYSQL_TYPE_DATE:
- return 3;
-
- case MYSQL_TYPE_TIME:
- return MIN_TIME_WIDTH;
-
- case MYSQL_TYPE_TIME2:
- return max_display_length_for_temporal2_field(MIN_TIME_WIDTH, metadata);
-
- case MYSQL_TYPE_TIMESTAMP:
- return MAX_DATETIME_WIDTH;
-
- case MYSQL_TYPE_TIMESTAMP2:
- return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
-
- case MYSQL_TYPE_DATETIME:
- return MAX_DATETIME_WIDTH;
-
- 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.
- */
- DBUG_ASSERT((metadata & 0xff) <= 7);
- return 8 * (metadata >> 8U) + (metadata & 0x00ff);
-
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_VARCHAR:
- return metadata;
- case MYSQL_TYPE_VARCHAR_COMPRESSED:
- return metadata - 1;
-
- /*
- The actual length for these types does not really matter since
- they are used to calc_pack_length, which ignores the given
- length for these types.
-
- Since we want this to be accurate for other uses, we return the
- maximum size in bytes of these BLOBs.
- */
-
- case MYSQL_TYPE_TINY_BLOB:
- return (uint32)my_set_bits(1 * 8);
-
- case MYSQL_TYPE_MEDIUM_BLOB:
- return (uint32)my_set_bits(3 * 8);
-
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_BLOB_COMPRESSED:
- /*
- For the blob type, Field::real_type() lies and say that all
- blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look
- at the length instead to decide what the max display size is.
- */
- return (uint32)my_set_bits(metadata * 8);
-
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_GEOMETRY:
- return (uint32)my_set_bits(4 * 8);
-
- default:
- return ~(uint32) 0;
- }
-}
-
-
-/*
- Compare the pack lengths of a source field (on the master) and a
- target field (on the slave).
-
- @param field Target field.
- @param type Source field type.
- @param metadata Source field metadata.
-
- @retval -1 The length of the source field is smaller than the target field.
- @retval 0 The length of the source and target fields are the same.
- @retval 1 The length of the source field is greater than the target field.
- */
-int compare_lengths(Field *field, enum_field_types source_type, uint16 metadata)
-{
- DBUG_ENTER("compare_lengths");
- size_t const source_length=
- max_display_length_for_field(source_type, metadata);
- size_t const target_length= field->max_display_length();
- DBUG_PRINT("debug", ("source_length: %lu, source_type: %u,"
- " target_length: %lu, target_type: %u",
- (unsigned long) source_length, source_type,
- (unsigned long) target_length, field->real_type()));
- int result= source_length < target_length ? -1 : source_length > target_length;
- DBUG_PRINT("result", ("%d", result));
- DBUG_RETURN(result);
-}
-#endif //MYSQL_CLIENT
/*********************************************************************
* table_def member definitions *
*********************************************************************/
@@ -349,750 +171,7 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
return length;
}
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-/**
- */
-void show_sql_type(enum_field_types type, uint16 metadata, String *str,
- bool char_with_octets)
-{
- DBUG_ENTER("show_sql_type");
- DBUG_PRINT("enter", ("type: %d, metadata: 0x%x", type, metadata));
-
- switch (type)
- {
- case MYSQL_TYPE_TINY:
- str->set_ascii(STRING_WITH_LEN("tinyint"));
- break;
-
- case MYSQL_TYPE_SHORT:
- str->set_ascii(STRING_WITH_LEN("smallint"));
- break;
-
- case MYSQL_TYPE_LONG:
- str->set_ascii(STRING_WITH_LEN("int"));
- break;
-
- case MYSQL_TYPE_FLOAT:
- str->set_ascii(STRING_WITH_LEN("float"));
- break;
-
- case MYSQL_TYPE_DOUBLE:
- str->set_ascii(STRING_WITH_LEN("double"));
- break;
-
- case MYSQL_TYPE_NULL:
- str->set_ascii(STRING_WITH_LEN("null"));
- break;
-
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_TIMESTAMP2:
- str->set_ascii(STRING_WITH_LEN("timestamp"));
- break;
-
- case MYSQL_TYPE_LONGLONG:
- str->set_ascii(STRING_WITH_LEN("bigint"));
- break;
-
- case MYSQL_TYPE_INT24:
- str->set_ascii(STRING_WITH_LEN("mediumint"));
- break;
-
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_DATE:
- str->set_ascii(STRING_WITH_LEN("date"));
- 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;
-
- case MYSQL_TYPE_YEAR:
- str->set_ascii(STRING_WITH_LEN("year"));
- break;
-
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VARCHAR_COMPRESSED:
- {
- CHARSET_INFO *cs= str->charset();
- size_t length=0;
- if (char_with_octets)
- length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "varchar(%u octets)", metadata);
- else
- length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "varbinary(%u)", metadata);
- str->length(length);
- }
- break;
-
- case MYSQL_TYPE_BIT:
- {
- CHARSET_INFO *cs= str->charset();
- int bit_length= 8 * (metadata >> 8) + (metadata & 0xFF);
- size_t length=
- cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "bit(%d)", bit_length);
- str->length(length);
- }
- break;
-
- case MYSQL_TYPE_DECIMAL:
- {
- CHARSET_INFO *cs= str->charset();
- size_t length=
- cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "decimal(%d,?)/*old*/", metadata);
- str->length(length);
- }
- break;
-
- case MYSQL_TYPE_NEWDECIMAL:
- {
- CHARSET_INFO *cs= str->charset();
- size_t length=
- cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "decimal(%d,%d)", metadata >> 8, metadata & 0xff);
- str->length(length);
- }
- break;
-
- case MYSQL_TYPE_ENUM:
- str->set_ascii(STRING_WITH_LEN("enum"));
- break;
-
- case MYSQL_TYPE_SET:
- str->set_ascii(STRING_WITH_LEN("set"));
- break;
-
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_BLOB_COMPRESSED:
- /*
- Field::real_type() lies regarding the actual type of a BLOB, so
- it is necessary to check the pack length to figure out what kind
- of blob it really is.
- */
- switch (metadata)
- {
- case 1:
- str->set_ascii(STRING_WITH_LEN("tinyblob"));
- break;
-
- case 2:
- str->set_ascii(STRING_WITH_LEN("blob"));
- break;
-
- case 3:
- str->set_ascii(STRING_WITH_LEN("mediumblob"));
- break;
-
- case 4:
- str->set_ascii(STRING_WITH_LEN("longblob"));
- break;
-
- default:
- DBUG_ASSERT(0);
- break;
- }
-
- if (type == MYSQL_TYPE_BLOB_COMPRESSED)
- str->append(STRING_WITH_LEN(" compressed"));
- break;
-
- case MYSQL_TYPE_STRING:
- {
- /*
- This is taken from Field_string::unpack.
- */
- CHARSET_INFO *cs= str->charset();
- uint bytes= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
- size_t length=0;
- if (char_with_octets)
- length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "char(%u octets)", bytes);
- else
- length= cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
- "binary(%u)", bytes);
- str->length(length);
- }
- break;
-
- case MYSQL_TYPE_GEOMETRY:
- str->set_ascii(STRING_WITH_LEN("geometry"));
- break;
-
- default:
- str->set_ascii(STRING_WITH_LEN("<unknown type>"));
- }
- DBUG_VOID_RETURN;
-}
-
-
-/**
- Check the order variable and print errors if the order is not
- acceptable according to the current settings.
-
- @param order The computed order of the conversion needed.
- @param rli The relay log info data structure: for error reporting.
- */
-bool is_conversion_ok(int order, Relay_log_info *rli)
-{
- DBUG_ENTER("is_conversion_ok");
- bool allow_non_lossy, allow_lossy;
-
- allow_non_lossy = slave_type_conversions_options &
- (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY);
- allow_lossy= slave_type_conversions_options &
- (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY);
-
- DBUG_PRINT("enter", ("order: %d, flags:%s%s", order,
- allow_non_lossy ? " ALL_NON_LOSSY" : "",
- allow_lossy ? " ALL_LOSSY" : ""));
- if (order < 0 && !allow_non_lossy)
- {
- /* !!! Add error message saying that non-lossy conversions need to be allowed. */
- DBUG_RETURN(false);
- }
-
- if (order > 0 && !allow_lossy)
- {
- /* !!! Add error message saying that lossy conversions need to be allowed. */
- DBUG_RETURN(false);
- }
-
- DBUG_RETURN(true);
-}
-
-
-/**
- Can a type potentially be converted to another type?
-
- This function check if the types are convertible and what
- conversion is required.
-
- If conversion is not possible, and error is printed.
-
- If conversion is possible:
-
- - *order will be set to -1 if source type is smaller than target
- type and a non-lossy conversion can be required. This includes
- the case where the field types are different but types could
- actually be converted in either direction.
-
- - *order will be set to 0 if no conversion is required.
-
- - *order will be set to 1 if the source type is strictly larger
- than the target type and that conversion is potentially lossy.
-
- @param[in] field Target field
- @param[in] type Source field type
- @param[in] metadata Source field metadata
- @param[in] rli Relay log info (for error reporting)
- @param[in] mflags Flags from the table map event
- @param[out] order Order between source field and target field
-
- @return @c true if conversion is possible according to the current
- settings, @c false if conversion is not possible according to the
- current setting.
- */
-static bool
-can_convert_field_to(Field *field,
- enum_field_types source_type, uint16 metadata,
- Relay_log_info *rli, uint16 mflags,
- int *order_var)
-{
- DBUG_ENTER("can_convert_field_to");
- bool same_type;
-#ifndef DBUG_OFF
- char field_type_buf[MAX_FIELD_WIDTH];
- String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1);
- field->sql_type(field_type);
- DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x",
- field_type.c_ptr_safe(), field->real_type(), source_type, metadata));
-#endif
- /**
- @todo
- Implement Field_varstring_cmopressed::real_type() and
- Field_blob_compressed::real_type() properly. All occurencies
- of Field::real_type() have to be inspected and adjusted if needed.
-
- Until it is not ready we have to compare source_type against
- binlog_type() when replicating from or to compressed data types.
-
- @sa Comment for Field::binlog_type()
- */
- if (source_type == MYSQL_TYPE_VARCHAR_COMPRESSED ||
- source_type == MYSQL_TYPE_BLOB_COMPRESSED ||
- field->binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED ||
- field->binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED)
- same_type= field->binlog_type() == source_type;
- else
- same_type= field->real_type() == source_type;
-
- /*
- If the real type is the same, we need to check the metadata to
- decide if conversions are allowed.
- */
- if (same_type)
- {
- if (metadata == 0) // Metadata can only be zero if no metadata was provided
- {
- /*
- If there is no metadata, we either have an old event where no
- metadata were supplied, or a type that does not require any
- metadata. In either case, conversion can be done but no
- conversion table is necessary.
- */
- DBUG_PRINT("debug", ("Base types are identical, but there is no metadata"));
- *order_var= 0;
- DBUG_RETURN(true);
- }
-
- DBUG_PRINT("debug", ("Base types are identical, doing field size comparison"));
- if (field->compatible_field_size(metadata, rli, mflags, order_var))
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
- 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);
-
- /*
- Here, from and to will always be different. Since the types are
- different, we cannot use the compatible_field_size() function, but
- have to rely on hard-coded max-sizes for fields.
- */
-
- DBUG_PRINT("debug", ("Base types are different, checking conversion"));
- switch (source_type) // Source type (on master)
- {
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- switch (field->real_type())
- {
- case MYSQL_TYPE_NEWDECIMAL:
- /*
- Then the other type is either FLOAT, DOUBLE, or old style
- DECIMAL, so we require lossy conversion.
- */
- *order_var= 1;
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
-
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- {
- if (source_type == MYSQL_TYPE_NEWDECIMAL ||
- source_type == MYSQL_TYPE_DECIMAL)
- *order_var = 1; // Always require lossy conversions
- else
- *order_var= compare_lengths(field, source_type, metadata);
- DBUG_ASSERT(*order_var != 0);
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
- }
-
- default:
- DBUG_RETURN(false);
- }
- break;
-
- /*
- The length comparison check will do the correct job of comparing
- the field lengths (in bytes) of two integer types.
- */
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_LONGLONG:
- switch (field->real_type())
- {
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_LONGLONG:
- /*
- max_display_length_for_field() is not fully precise for the integer
- data types. So its result cannot be compared to the result of
- field->max_dispay_length() when the table field and the binlog field
- are of the same type.
- This code should eventually be rewritten not to use
- compare_lengths(), to detect subtype/supetype relations
- just using the type codes.
- */
- DBUG_ASSERT(source_type != field->real_type());
- *order_var= compare_lengths(field, source_type, metadata);
- DBUG_ASSERT(*order_var != 0);
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
-
- default:
- DBUG_RETURN(false);
- }
- break;
-
- /*
- Since source and target type is different, and it is not possible
- to convert bit types to anything else, this will return false.
- */
- case MYSQL_TYPE_BIT:
- DBUG_RETURN(false);
-
- /*
- If all conversions are disabled, it is not allowed to convert
- between these types. Since the TEXT vs. BINARY is distinguished by
- the charset, and the charset is not replicated, we cannot
- currently distinguish between , e.g., TEXT and BLOB.
- */
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_BLOB_COMPRESSED:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VARCHAR_COMPRESSED:
- switch (field->real_type())
- {
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_BLOB_COMPRESSED:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_VARCHAR_COMPRESSED:
- *order_var= compare_lengths(field, source_type, metadata);
- /*
- Here we know that the types are different, so if the order
- gives that they do not require any conversion, we still need
- to have non-lossy conversion enabled to allow conversion
- between different (string) types of the same length.
- */
- if (*order_var == 0)
- *order_var= -1;
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
-
- default:
- DBUG_RETURN(false);
- }
- break;
-
- case MYSQL_TYPE_GEOMETRY:
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_NULL:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_TIMESTAMP2:
- case MYSQL_TYPE_TIME2:
- DBUG_RETURN(false);
- case MYSQL_TYPE_NEWDATE:
- {
- if (field->real_type() == MYSQL_TYPE_DATETIME2 ||
- field->real_type() == MYSQL_TYPE_DATETIME)
- {
- *order_var= -1;
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
- }
- else
- {
- DBUG_RETURN(false);
- }
- }
- break;
-
- //case MYSQL_TYPE_DATETIME: TODO: fix MDEV-17394 and uncomment.
- //
- //The "old" type does not specify the fraction part size which is required
- //for correct conversion.
- case MYSQL_TYPE_DATETIME2:
- {
- if (field->real_type() == MYSQL_TYPE_NEWDATE)
- {
- *order_var= 1;
- DBUG_RETURN(is_conversion_ok(*order_var, rli));
- }
- else
- {
- DBUG_RETURN(false);
- }
- }
- break;
- }
- DBUG_RETURN(false); // To keep GCC happy
-}
-
-
-/**
- Is the definition compatible with a table?
-
- This function will compare the master table with an existing table
- on the slave and see if they are compatible with respect to the
- current settings of @c SLAVE_TYPE_CONVERSIONS.
-
- If the tables are compatible and conversions are required, @c
- *tmp_table_var will be set to a virtual temporary table with field
- pointers for the fields that require conversions. This allow simple
- checking of whether a conversion are to be applied or not.
-
- If tables are compatible, but no conversions are necessary, @c
- *tmp_table_var will be set to NULL.
-
- @param rli_arg[in]
- Relay log info, for error reporting.
-
- @param table[in]
- Table to compare with
-
- @param tmp_table_var[out]
- Virtual temporary table for performing conversions, if necessary.
-
- @retval true Master table is compatible with slave table.
- @retval false Master table is not compatible with slave table.
-*/
-bool
-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= 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)
- {
- Field *const field= table->field[col];
- int order;
- if (can_convert_field_to(field, type(col), field_metadata(col), rli, m_flags, &order))
- {
- DBUG_PRINT("debug", ("Checking column %d -"
- " field '%s' can be converted - order: %d",
- col, field->field_name.str, order));
- DBUG_ASSERT(order >= -1 && order <= 1);
-
- /*
- If order is not 0, a conversion is required, so we need to set
- up the conversion table.
- */
- if (order != 0 && tmp_table == NULL)
- {
- /*
- 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, rgi, table);
- if (tmp_table == NULL)
- return false;
- /*
- Clear all fields up to, but not including, this column.
- */
- for (unsigned int i= 0; i < col; ++i)
- tmp_table->field[i]= NULL;
- }
-
- if (order == 0 && tmp_table != NULL)
- tmp_table->field[col]= NULL;
- }
- else
- {
- DBUG_PRINT("debug", ("Checking column %d -"
- " field '%s' can not be converted",
- col, field->field_name.str));
- DBUG_ASSERT(col < size() && col < table->s->fields);
- DBUG_ASSERT(table->s->db.str && table->s->table_name.str);
- DBUG_ASSERT(table->in_use);
- const char *db_name= table->s->db.str;
- const char *tbl_name= table->s->table_name.str;
- char source_buf[MAX_FIELD_WIDTH];
- char target_buf[MAX_FIELD_WIDTH];
- String source_type(source_buf, sizeof(source_buf), &my_charset_latin1);
- String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
- THD *thd= table->in_use;
- bool char_with_octets= field->cmp_type() == STRING_RESULT ?
- field->has_charset() : true;
-
- show_sql_type(type(col), field_metadata(col), &source_type,
- char_with_octets);
- field->sql_rpl_type(&target_type);
-
- rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED, rgi->gtid_info(),
- ER_THD(thd, ER_SLAVE_CONVERSION_FAILED),
- col, db_name, tbl_name,
- source_type.c_ptr_safe(), target_type.c_ptr_safe());
- return false;
- }
- }
-
-#ifndef DBUG_OFF
- if (tmp_table)
- {
- for (unsigned int col= 0; col < tmp_table->s->fields; ++col)
- if (tmp_table->field[col])
- {
- char source_buf[MAX_FIELD_WIDTH];
- char target_buf[MAX_FIELD_WIDTH];
- String source_type(source_buf, sizeof(source_buf), &my_charset_latin1);
- String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
- tmp_table->field[col]->sql_type(source_type);
- table->field[col]->sql_type(target_type);
- DBUG_PRINT("debug", ("Field %s - conversion required."
- " Source type: '%s', Target type: '%s'",
- tmp_table->field[col]->field_name.str,
- source_type.c_ptr_safe(), target_type.c_ptr_safe()));
- }
- }
-#endif
-
- *conv_table_var= tmp_table;
- return true;
-}
-
-
-/**
- A wrapper to Virtual_tmp_table, to get access to its constructor,
- which is protected for safety purposes (against illegal use on stack).
-*/
-class Virtual_conversion_table: public Virtual_tmp_table
-{
-public:
- Virtual_conversion_table(THD *thd) :Virtual_tmp_table(thd) { }
- /**
- Add a new field into the virtual table.
- @param sql_type - The real_type of the field.
- @param metadata - The RBR binary log metadata for this field.
- @param target_field - The field from the target table, to get extra
- attributes from (e.g. typelib in case of ENUM).
- */
- bool add(enum_field_types sql_type,
- uint16 metadata, const Field *target_field)
- {
- const Type_handler *handler= Type_handler::get_handler_by_real_type(sql_type);
- if (!handler)
- {
- sql_print_error("In RBR mode, Slave received unknown field type field %d "
- " for column Name: %s.%s.%s.",
- (int) sql_type,
- target_field->table->s->db.str,
- target_field->table->s->table_name.str,
- target_field->field_name.str);
- return true;
- }
- Field *tmp= handler->make_conversion_table_field(this, metadata,
- target_field);
- if (!tmp)
- return true;
- Virtual_tmp_table::add(tmp);
- DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d,"
- " maybe_null: %d, unsigned_flag: %d, pack_length: %u",
- sql_type, target_field->field_name.str,
- tmp->field_length, tmp->decimals(), TRUE,
- tmp->flags, tmp->pack_length()));
- return false;
- }
-};
-
-
-/**
- Create a conversion table.
-
- If the function is unable to create the conversion table, an error
- will be printed and NULL will be returned.
-
- @return Pointer to conversion table, or NULL if unable to create
- conversion table.
- */
-
-TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi,
- TABLE *target_table) const
-{
- DBUG_ENTER("table_def::create_conversion_table");
-
- Virtual_conversion_table *conv_table;
- Relay_log_info *rli= rgi->rli;
- /*
- At slave, columns may differ. So we should create
- MY_MIN(columns@master, columns@slave) columns in the
- conversion table.
- */
- uint const cols_to_create= MY_MIN(target_table->s->fields, size());
- if (!(conv_table= new(thd) Virtual_conversion_table(thd)) ||
- conv_table->init(cols_to_create))
- goto err;
- for (uint col= 0 ; col < cols_to_create; ++col)
- {
- if (conv_table->add(type(col), field_metadata(col),
- target_table->field[col]))
- {
- DBUG_PRINT("debug", ("binlog_type: %d, metadata: %04X, target_field: '%s'"
- " make_conversion_table_field() failed",
- binlog_type(col), field_metadata(col),
- target_table->field[col]->field_name.str));
- goto err;
- }
- }
-
- if (conv_table->open())
- goto err; // Could not allocate record buffer?
-
- DBUG_RETURN(conv_table);
-
-err:
- if (conv_table)
- delete conv_table;
- rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION, rgi->gtid_info(),
- ER_THD(thd, ER_SLAVE_CANT_CREATE_CONVERSION),
- target_table->s->db.str,
- target_table->s->table_name.str);
- DBUG_RETURN(NULL);
-}
-#endif /* MYSQL_CLIENT */
+PSI_memory_key key_memory_table_def_memory;
table_def::table_def(unsigned char *types, ulong size,
uchar *field_metadata, int metadata_size,
@@ -1101,7 +180,7 @@ table_def::table_def(unsigned char *types, ulong size,
m_field_metadata(0), m_null_bits(0), m_flags(flags),
m_memory(NULL)
{
- m_memory= (uchar *)my_multi_malloc(MYF(MY_WME),
+ m_memory= (uchar *)my_multi_malloc(key_memory_table_def_memory, MYF(MY_WME),
&m_type, size,
&m_field_metadata,
size * sizeof(uint16),
@@ -1256,67 +335,3 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, enum enum_binlog_che
}
return res;
}
-
-#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
-
-Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL)
-{
- my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16, MYF(0));
-}
-
-Deferred_log_events::~Deferred_log_events()
-{
- delete_dynamic(&array);
-}
-
-int Deferred_log_events::add(Log_event *ev)
-{
- last_added= ev;
- insert_dynamic(&array, (uchar*) &ev);
- return 0;
-}
-
-bool Deferred_log_events::is_empty()
-{
- return array.elements == 0;
-}
-
-bool Deferred_log_events::execute(rpl_group_info *rgi)
-{
- bool res= false;
- DBUG_ENTER("Deferred_log_events::execute");
- DBUG_ASSERT(rgi->deferred_events_collecting);
-
- 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(rgi);
- }
- rgi->deferred_events_collecting= true;
- DBUG_RETURN(res);
-}
-
-void Deferred_log_events::rewind()
-{
- /*
- Reset preceding Query log event events which execution was
- deferred because of slave side filtering.
- */
- if (!is_empty())
- {
- for (uint i= 0; i < array.elements; i++)
- {
- Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i);
- delete ev;
- }
- last_added= NULL;
- if (array.elements > array.max_element)
- freeze_size(&array);
- reset_dynamic(&array);
- }
- last_added= NULL;
-}
-
-#endif
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index b42b11231e0..c28e8aa10eb 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -118,7 +118,9 @@ public:
return source_type;
}
-
+#ifdef MYSQL_SERVER
+ const Type_handler *field_type_handler(uint index) const;
+#endif
/*
This function allows callers to get the extra field data from the
diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc
new file mode 100644
index 00000000000..8110b142e74
--- /dev/null
+++ b/sql/rpl_utility_server.cc
@@ -0,0 +1,1185 @@
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include "mariadb.h"
+#include <my_bit.h>
+#include "rpl_utility.h"
+#include "log_event.h"
+
+#if defined(MYSQL_CLIENT)
+#error MYSQL_CLIENT must not be defined here
+#endif
+
+#if !defined(MYSQL_SERVER)
+#error MYSQL_SERVER must be defined here
+#endif
+
+#if defined(HAVE_REPLICATION)
+#include "rpl_rli.h"
+#include "sql_select.h"
+#endif
+
+
+/**
+ Compute the maximum display length of a field.
+
+ @param sql_type Type of the field
+ @param metadata The metadata from the master for the field.
+ @return Maximum length of the field in bytes.
+
+ The precise values calculated by field->max_display_length() and
+ calculated by max_display_length_for_field() can differ (by +1 or -1)
+ for integer data types (TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT).
+ This slight difference is not important here, because we call
+ this function only for two *different* integer data types.
+ */
+static uint32
+max_display_length_for_field(const Conv_source &source)
+{
+ DBUG_PRINT("debug", ("sql_type: %s, metadata: 0x%x",
+ source.type_handler()->name().ptr(), source.metadata()));
+ return source.type_handler()->max_display_length_for_field(source);
+}
+
+
+/*
+ Compare the pack lengths of a source field (on the master) and a
+ target field (on the slave).
+
+ @param sh Source type handler
+ @param source_length Source length
+ @param th Target type hander
+ @param target_length Target length
+
+ @retval CONV_TYPE_SUBSET_TO_SUPERSET The length of the source field is
+ smaller than the target field.
+ @retval CONV_TYPE_PRECISE The length of the source and
+ the target fields are equal.
+ @retval CONV_TYPE_SUPERSET_TO_SUBSET The length of the source field is
+ greater than the target field.
+ */
+static enum_conv_type
+compare_lengths(const Type_handler *sh, uint32 source_length,
+ const Type_handler *th, uint32 target_length)
+{
+ DBUG_ENTER("compare_lengths");
+ DBUG_PRINT("debug", ("source_length: %lu, source_type: %s,"
+ " target_length: %lu, target_type: %s",
+ (unsigned long) source_length, sh->name().ptr(),
+ (unsigned long) target_length, th->name().ptr()));
+ enum_conv_type result=
+ source_length < target_length ? CONV_TYPE_SUBSET_TO_SUPERSET :
+ source_length > target_length ? CONV_TYPE_SUPERSET_TO_SUBSET :
+ CONV_TYPE_PRECISE;
+ DBUG_PRINT("result", ("%d", result));
+ DBUG_RETURN(result);
+}
+
+
+/**
+ 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);
+}
+
+
+uint32
+Type_handler_newdecimal::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return src.metadata() >> 8;
+}
+
+
+uint32
+Type_handler_typelib::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ /*
+ Field_enum::rpl_conv_type_from() does not use compare_lengths().
+ So we should not come here.
+ */
+ DBUG_ASSERT(0);
+ return src.metadata() & 0x00ff;
+}
+
+
+uint32
+Type_handler_string::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ /*
+ ENUM and SET are transferred using as STRING,
+ with the exact type code in metadata.
+ Make sure that we previously detected ENUM/SET and
+ translated them into a proper type handler.
+ See table_def::field_type_handler() for details.
+ */
+ DBUG_ASSERT((src.metadata() >> 8) != MYSQL_TYPE_SET);
+ DBUG_ASSERT((src.metadata() >> 8) != MYSQL_TYPE_ENUM);
+ /* This is taken from Field_string::unpack. */
+ return (((src.metadata() >> 4) & 0x300) ^ 0x300) + (src.metadata() & 0x00ff);
+}
+
+
+uint32
+Type_handler_time2::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return max_display_length_for_temporal2_field(MIN_TIME_WIDTH,
+ src.metadata());
+}
+
+
+uint32
+Type_handler_timestamp2::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH,
+ src.metadata());
+}
+
+
+uint32
+Type_handler_datetime2::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH,
+ src.metadata());
+}
+
+
+uint32
+Type_handler_bit::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ /*
+ Decode the size of the bit field from the master.
+ */
+ DBUG_ASSERT((src.metadata() & 0xff) <= 7);
+ return 8 * (src.metadata() >> 8U) + (src.metadata() & 0x00ff);
+}
+
+
+uint32
+Type_handler_var_string::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return src.metadata();
+}
+
+
+uint32
+Type_handler_varchar::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return src.metadata();
+}
+
+
+uint32
+Type_handler_varchar_compressed::
+ max_display_length_for_field(const Conv_source &src) const
+{
+ DBUG_ASSERT(src.metadata() > 0);
+ return src.metadata() - 1;
+}
+
+
+/*
+ The actual length for these types does not really matter since
+ they are used to calc_pack_length, which ignores the given
+ length for these types.
+
+ Since we want this to be accurate for other uses, we return the
+ maximum size in bytes of these BLOBs.
+*/
+uint32
+Type_handler_tiny_blob::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return (uint32) my_set_bits(1 * 8);
+}
+
+
+uint32
+Type_handler_medium_blob::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return (uint32) my_set_bits(3 * 8);
+}
+
+
+uint32
+Type_handler_blob::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ /*
+ For the blob type, Field::real_type() lies and say that all
+ blobs are of type MYSQL_TYPE_BLOB. In that case, we have to look
+ at the length instead to decide what the max display size is.
+ */
+ return (uint32) my_set_bits(src.metadata() * 8);
+}
+
+
+uint32
+Type_handler_blob_compressed::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return (uint32) my_set_bits(src.metadata() * 8);
+}
+
+
+uint32
+Type_handler_long_blob::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return (uint32) my_set_bits(4 * 8);
+}
+
+
+uint32
+Type_handler_olddecimal::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return ~(uint32) 0;
+}
+
+
+void Type_handler::show_binlog_type(const Conv_source &src, const Field &,
+ String *str) const
+{
+ str->set_ascii(name().ptr(), name().length());
+}
+
+
+void Type_handler_var_string::show_binlog_type(const Conv_source &src,
+ const Field &dst,
+ String *str) const
+{
+ CHARSET_INFO *cs= str->charset();
+ const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset()
+ ? "char(%u octets)" : "binary(%u)";
+ size_t length= cs->cset->snprintf(cs, (char*) str->ptr(),
+ str->alloced_length(),
+ fmt, src.metadata());
+ str->length(length);
+}
+
+
+void Type_handler_varchar::show_binlog_type(const Conv_source &src,
+ const Field &dst,
+ String *str) const
+{
+ CHARSET_INFO *cs= str->charset();
+ const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset()
+ ? "varchar(%u octets)" : "varbinary(%u)";
+ size_t length= cs->cset->snprintf(cs, (char*) str->ptr(),
+ str->alloced_length(),
+ fmt, src.metadata());
+ str->length(length);
+}
+
+
+void Type_handler_varchar_compressed::show_binlog_type(const Conv_source &src,
+ const Field &dst,
+ String *str) const
+{
+ CHARSET_INFO *cs= str->charset();
+ const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset()
+ ? "varchar(%u octets) compressed" : "varbinary(%u) compressed";
+ size_t length= cs->cset->snprintf(cs, (char*) str->ptr(),
+ str->alloced_length(),
+ fmt, src.metadata());
+ str->length(length);
+}
+
+void Type_handler_bit::show_binlog_type(const Conv_source &src, const Field &,
+ String *str) const
+{
+ CHARSET_INFO *cs= str->charset();
+ int bit_length= 8 * (src.metadata() >> 8) + (src.metadata() & 0xFF);
+ size_t length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "bit(%d)", bit_length);
+ str->length(length);
+}
+
+
+void Type_handler_olddecimal::show_binlog_type(const Conv_source &src,
+ const Field &,
+ String *str) const
+{
+ CHARSET_INFO *cs= str->charset();
+ size_t length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "decimal(%d,?)/*old*/", src.metadata());
+ str->length(length);
+
+}
+
+
+void Type_handler_newdecimal::show_binlog_type(const Conv_source &src,
+ const Field &,
+ String *str) const
+{
+ CHARSET_INFO *cs= str->charset();
+ size_t length=
+ cs->cset->snprintf(cs, (char*) str->ptr(), str->alloced_length(),
+ "decimal(%d,%d)",
+ src.metadata() >> 8, src.metadata() & 0xff);
+ str->length(length);
+}
+
+
+void Type_handler_blob_compressed::show_binlog_type(const Conv_source &src,
+ const Field &,
+ String *str) const
+{
+ /*
+ Field::real_type() lies regarding the actual type of a BLOB, so
+ it is necessary to check the pack length to figure out what kind
+ of blob it really is.
+ */
+ switch (src.metadata()) {
+ case 1:
+ str->set_ascii(STRING_WITH_LEN("tinyblob compressed"));
+ break;
+ case 2:
+ str->set_ascii(STRING_WITH_LEN("blob compressed"));
+ break;
+ case 3:
+ str->set_ascii(STRING_WITH_LEN("mediumblob compressed"));
+ break;
+ default:
+ DBUG_ASSERT(0);
+ // Fall through
+ case 4:
+ str->set_ascii(STRING_WITH_LEN("longblob compressed"));
+ }
+}
+
+
+void Type_handler_string::show_binlog_type(const Conv_source &src,
+ const Field &dst,
+ String *str) const
+{
+ /*
+ This is taken from Field_string::unpack.
+ */
+ CHARSET_INFO *cs= str->charset();
+ uint bytes= (((src.metadata() >> 4) & 0x300) ^ 0x300) +
+ (src.metadata() & 0x00ff);
+ const char* fmt= dst.cmp_type() != STRING_RESULT || dst.has_charset()
+ ? "char(%u octets)" : "binary(%u)";
+ size_t length= cs->cset->snprintf(cs, (char*) str->ptr(),
+ str->alloced_length(),
+ fmt, bytes);
+ str->length(length);
+}
+
+
+enum_conv_type
+Field::rpl_conv_type_from_same_data_type(uint16 metadata,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (metadata == 0) // Metadata can only be zero if no metadata was provided
+ {
+ /*
+ If there is no metadata, we either have an old event where no
+ metadata were supplied, or a type that does not require any
+ metadata. In either case, conversion can be done but no
+ conversion table is necessary.
+ */
+ DBUG_PRINT("debug", ("Base types are identical, but there is no metadata"));
+ return CONV_TYPE_PRECISE;
+ }
+
+ DBUG_PRINT("debug", ("Base types are identical, doing field size comparison"));
+ int order= 0;
+ if (!compatible_field_size(metadata, rli, param.table_def_flags(), &order))
+ return CONV_TYPE_IMPOSSIBLE;
+ return order == 0 ? CONV_TYPE_PRECISE :
+ order < 0 ? CONV_TYPE_SUBSET_TO_SUPERSET :
+ CONV_TYPE_SUPERSET_TO_SUBSET;
+}
+
+
+enum_conv_type
+Field_new_decimal::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ if (source.type_handler() == &type_handler_olddecimal ||
+ source.type_handler() == &type_handler_newdecimal ||
+ source.type_handler() == &type_handler_float ||
+ source.type_handler() == &type_handler_double)
+ {
+ /*
+ Then the other type is either FLOAT, DOUBLE, or old style
+ DECIMAL, so we require lossy conversion.
+ */
+ return CONV_TYPE_SUPERSET_TO_SUBSET;
+ }
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+/*
+ This covers FLOAT, DOUBLE and old DECIMAL
+*/
+enum_conv_type
+Field_real::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ if (source.type_handler() == &type_handler_olddecimal ||
+ source.type_handler() == &type_handler_newdecimal)
+ return CONV_TYPE_SUPERSET_TO_SUBSET; // Always require lossy conversions
+ if (source.type_handler() == &type_handler_float ||
+ source.type_handler() == &type_handler_double)
+ {
+ enum_conv_type order= compare_lengths(source.type_handler(),
+ max_display_length_for_field(source),
+ type_handler(), max_display_length());
+ DBUG_ASSERT(order != CONV_TYPE_PRECISE);
+ return order;
+ }
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_int::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ /*
+ The length comparison check will do the correct job of comparing
+ the field lengths (in bytes) of two integer types.
+ */
+ if (source.type_handler() == &type_handler_stiny ||
+ source.type_handler() == &type_handler_sshort ||
+ source.type_handler() == &type_handler_sint24 ||
+ source.type_handler() == &type_handler_slong ||
+ source.type_handler() == &type_handler_slonglong)
+ {
+ /*
+ max_display_length_for_field() is not fully precise for the integer
+ data types. So its result cannot be compared to the result of
+ max_dispay_length() when the table field and the binlog field
+ are of the same type.
+ This code should eventually be rewritten not to use
+ compare_lengths(), to detect subtype/supetype relations
+ just using the type codes.
+ */
+ DBUG_ASSERT(source.real_field_type() != real_type());
+ enum_conv_type order= compare_lengths(source.type_handler(),
+ max_display_length_for_field(source),
+ type_handler(), max_display_length());
+ DBUG_ASSERT(order != CONV_TYPE_PRECISE);
+ return order;
+ }
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_enum::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ /*
+ For some reasons Field_enum and Field_set store MYSQL_TYPE_STRING
+ as a type code in the binary log and encode the real type in metadata.
+ So we need to test real_type() here instread of binlog_type().
+ */
+ return real_type() == source.real_field_type() ?
+ rpl_conv_type_from_same_data_type(source.metadata(), rli, param) :
+ CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_longstr::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ /**
+ @todo
+ Implement Field_varstring_compressed::real_type() and
+ Field_blob_compressed::real_type() properly. All occurencies
+ of Field::real_type() have to be inspected and adjusted if needed.
+
+ Until it is not ready we have to compare source_type against
+ binlog_type() when replicating from or to compressed data types.
+
+ @sa Comment for Field::binlog_type()
+ */
+ bool same_type;
+ if (source.real_field_type() == MYSQL_TYPE_VARCHAR_COMPRESSED ||
+ source.real_field_type() == MYSQL_TYPE_BLOB_COMPRESSED ||
+ binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED ||
+ binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED)
+ same_type= binlog_type() == source.real_field_type();
+ else
+ same_type= type_handler() == source.type_handler();
+
+ if (same_type)
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+
+ if (source.type_handler() == &type_handler_tiny_blob ||
+ source.type_handler() == &type_handler_medium_blob ||
+ source.type_handler() == &type_handler_long_blob ||
+ source.type_handler() == &type_handler_blob ||
+ source.type_handler() == &type_handler_blob_compressed ||
+ source.type_handler() == &type_handler_string ||
+ source.type_handler() == &type_handler_var_string ||
+ source.type_handler() == &type_handler_varchar ||
+ source.type_handler() == &type_handler_varchar_compressed)
+ {
+ enum_conv_type order= compare_lengths(source.type_handler(),
+ max_display_length_for_field(source),
+ type_handler(), max_display_length());
+ /*
+ Here we know that the types are different, so if the order
+ gives that they do not require any conversion, we still need
+ to have non-lossy conversion enabled to allow conversion
+ between different (string) types of the same length.
+
+ Also, if all conversions are disabled, it is not allowed to convert
+ between these types. Since the TEXT vs. BINARY is distinguished by
+ the charset, and the charset is not replicated, we cannot
+ currently distinguish between , e.g., TEXT and BLOB.
+ */
+ if (order == CONV_TYPE_PRECISE)
+ order= CONV_TYPE_SUBSET_TO_SUPERSET;
+ return order;
+ }
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_newdate::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (real_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ if (source.type_handler() == &type_handler_datetime2)
+ return CONV_TYPE_SUPERSET_TO_SUBSET;
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_time::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ // 'MySQL56 TIME(N)' -> 'MariaDB-5.3 TIME(N)' is non-lossy
+ if (decimals() == source.metadata() &&
+ source.type_handler() == &type_handler_time2)
+ return CONV_TYPE_VARIANT; // TODO: conversion from FSP1>FSP2
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_timef::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ /*
+ See comment in Field_datetimef::rpl_conv_type_from()
+ 'MariaDB-5.3 TIME(0)' to 'MySQL56 TIME(0)' is non-lossy
+ */
+ if (source.metadata() == 0 && source.type_handler() == &type_handler_time)
+ return CONV_TYPE_VARIANT;
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_timestamp::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ // 'MySQL56 TIMESTAMP(N)' -> MariaDB-5.3 TIMESTAMP(N)' is non-lossy
+ if (source.metadata() == decimals() &&
+ source.type_handler() == &type_handler_timestamp2)
+ return CONV_TYPE_VARIANT; // TODO: conversion from FSP1>FSP2
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_timestampf::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ /*
+ See comment in Field_datetimef::rpl_conv_type_from()
+ 'MariaDB-5.3 TIMESTAMP(0)' to 'MySQL56 TIMESTAMP(0)' is non-lossy
+ */
+ if (source.metadata() == 0 &&
+ source.type_handler() == &type_handler_timestamp)
+ return CONV_TYPE_VARIANT;
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_datetime::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ // 'MySQL56 DATETIME(N)' -> MariaDB-5.3 DATETIME(N) is non-lossy
+ if (source.metadata() == decimals() &&
+ source.type_handler() == &type_handler_datetime2)
+ return CONV_TYPE_VARIANT; // TODO: conversion from FSP1>FSP2
+ if (source.type_handler() == &type_handler_newdate)
+ return CONV_TYPE_SUBSET_TO_SUPERSET;
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_datetimef::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ if (binlog_type() == source.real_field_type())
+ return rpl_conv_type_from_same_data_type(source.metadata(), rli, param);
+ /*
+ 'MariaDB-5.3 DATETIME(N)' does not provide information about fractional
+ precision in metadata. So we assume the precision on the master is equal
+ to the precision on the slave.
+ TODO: See MDEV-17394 what happend in case precisions are in case different
+ 'MariaDB-5.3 DATETIME(0)' to 'MySQL56 DATETIME(0)' is non-lossy
+ */
+ if (source.metadata() == 0 &&
+ source.type_handler() == &type_handler_datetime)
+ return CONV_TYPE_VARIANT;
+ if (source.type_handler() == &type_handler_newdate)
+ return CONV_TYPE_SUBSET_TO_SUPERSET;
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_date::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ // old DATE
+ return binlog_type() == source.real_field_type() ?
+ rpl_conv_type_from_same_data_type(source.metadata(), rli, param) :
+ CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_bit::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ return binlog_type() == source.real_field_type() ?
+ rpl_conv_type_from_same_data_type(source.metadata(), rli, param) :
+ CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_year::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ return binlog_type() == source.real_field_type() ?
+ rpl_conv_type_from_same_data_type(source.metadata(), rli, param) :
+ CONV_TYPE_IMPOSSIBLE;
+}
+
+
+enum_conv_type
+Field_null::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ DBUG_ASSERT(0);
+ return CONV_TYPE_IMPOSSIBLE;
+}
+
+
+/**********************************************************************/
+
+
+#if defined(HAVE_REPLICATION)
+
+/**
+ */
+static void show_sql_type(const Conv_source &src, const Field &dst,
+ String *str)
+{
+ DBUG_ENTER("show_sql_type");
+ DBUG_ASSERT(src.type_handler() != NULL);
+ DBUG_PRINT("enter", ("type: %s, metadata: 0x%x",
+ src.type_handler()->name().ptr(), src.metadata()));
+ src.type_handler()->show_binlog_type(src, dst, str);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Check the order variable and print errors if the order is not
+ acceptable according to the current settings.
+
+ @param order The computed order of the conversion needed.
+ @param rli The relay log info data structure: for error reporting.
+ */
+static bool is_conversion_ok(enum_conv_type type, const Relay_log_info *rli,
+ ulonglong type_conversion_options)
+{
+ DBUG_ENTER("is_conversion_ok");
+ bool allow_non_lossy, allow_lossy;
+
+ allow_non_lossy= type_conversion_options &
+ (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY);
+ allow_lossy= type_conversion_options &
+ (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY);
+
+ DBUG_PRINT("enter", ("order: %d, flags:%s%s", (int) type,
+ allow_non_lossy ? " ALL_NON_LOSSY" : "",
+ allow_lossy ? " ALL_LOSSY" : ""));
+ switch (type) {
+ case CONV_TYPE_PRECISE:
+ case CONV_TYPE_VARIANT:
+ DBUG_RETURN(true);
+ case CONV_TYPE_SUBSET_TO_SUPERSET:
+ /* !!! Add error message saying that non-lossy conversions need to be allowed. */
+ DBUG_RETURN(allow_non_lossy);
+ case CONV_TYPE_SUPERSET_TO_SUBSET:
+ /* !!! Add error message saying that lossy conversions need to be allowed. */
+ DBUG_RETURN(allow_lossy);
+ case CONV_TYPE_IMPOSSIBLE:
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Can a type potentially be converted to another type?
+
+ This function check if the types are convertible and what
+ conversion is required.
+
+ If conversion is not possible, and error is printed.
+
+ If conversion is possible:
+
+ - *order will be set to -1 if source type is smaller than target
+ type and a non-lossy conversion can be required. This includes
+ the case where the field types are different but types could
+ actually be converted in either direction.
+
+ - *order will be set to 0 if no conversion is required.
+
+ - *order will be set to 1 if the source type is strictly larger
+ than the target type and that conversion is potentially lossy.
+
+ @param[in] field Target field
+ @param[in] type Source field type
+ @param[in] metadata Source field metadata
+ @param[in] rli Relay log info (for error reporting)
+ @param[in] mflags Flags from the table map event
+ @param[out] order Order between source field and target field
+
+ @return @c true if conversion is possible according to the current
+ settings, @c false if conversion is not possible according to the
+ current setting.
+ */
+static enum_conv_type
+can_convert_field_to(Field *field, const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param)
+{
+ DBUG_ENTER("can_convert_field_to");
+#ifndef DBUG_OFF
+ char field_type_buf[MAX_FIELD_WIDTH];
+ String field_type(field_type_buf, sizeof(field_type_buf), &my_charset_latin1);
+ field->sql_type(field_type);
+ DBUG_PRINT("enter", ("field_type: %s, target_type: %d, source_type: %d, source_metadata: 0x%x",
+ field_type.c_ptr_safe(), field->real_type(),
+ source.real_field_type(), source.metadata()));
+#endif
+ DBUG_RETURN(field->rpl_conv_type_from(source, rli, param));
+}
+
+
+const Type_handler *table_def::field_type_handler(uint col) const
+{
+ enum_field_types typecode= binlog_type(col);
+ uint16 metadata= field_metadata(col);
+ DBUG_ASSERT(typecode != MYSQL_TYPE_ENUM);
+ DBUG_ASSERT(typecode != MYSQL_TYPE_SET);
+
+ if (typecode == MYSQL_TYPE_BLOB)
+ {
+ switch (metadata & 0xff) {
+ case 1: return &type_handler_tiny_blob;
+ case 2: return &type_handler_blob;
+ case 3: return &type_handler_medium_blob;
+ case 4: return &type_handler_long_blob;
+ default: return NULL;
+ }
+ }
+ if (typecode == MYSQL_TYPE_STRING)
+ {
+ uchar typecode2= metadata >> 8;
+ if (typecode2 == MYSQL_TYPE_SET)
+ return &type_handler_set;
+ if (typecode2 == MYSQL_TYPE_ENUM)
+ return &type_handler_enum;
+ return &type_handler_string;
+ }
+ /*
+ This type has not been used since before row-based replication,
+ so we can safely assume that it really is MYSQL_TYPE_NEWDATE.
+ */
+ if (typecode == MYSQL_TYPE_DATE)
+ return &type_handler_newdate;
+ return Type_handler::get_handler_by_real_type(typecode);
+}
+
+
+/**
+ Is the definition compatible with a table?
+
+ This function will compare the master table with an existing table
+ on the slave and see if they are compatible with respect to the
+ current settings of @c SLAVE_TYPE_CONVERSIONS.
+
+ If the tables are compatible and conversions are required, @c
+ *tmp_table_var will be set to a virtual temporary table with field
+ pointers for the fields that require conversions. This allow simple
+ checking of whether a conversion are to be applied or not.
+
+ If tables are compatible, but no conversions are necessary, @c
+ *tmp_table_var will be set to NULL.
+
+ @param rli_arg[in]
+ Relay log info, for error reporting.
+
+ @param table[in]
+ Table to compare with
+
+ @param tmp_table_var[out]
+ Virtual temporary table for performing conversions, if necessary.
+
+ @retval true Master table is compatible with slave table.
+ @retval false Master table is not compatible with slave table.
+*/
+bool
+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= 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)
+ {
+ Field *const field= table->field[col];
+ const Type_handler *h= field_type_handler(col);
+ if (!h)
+ {
+ sql_print_error("In RBR mode, Slave received unknown field type field %d "
+ " for column Name: %s.%s.%s.",
+ binlog_type(col),
+ field->table->s->db.str,
+ field->table->s->table_name.str,
+ field->field_name.str);
+ return false;
+ }
+
+ if (!h)
+ return false; // An unknown data type found in the binary log
+ Conv_source source(h, field_metadata(col), field->charset());
+ enum_conv_type convtype= can_convert_field_to(field, source, rli,
+ Conv_param(m_flags));
+ if (is_conversion_ok(convtype, rli, slave_type_conversions_options))
+ {
+ DBUG_PRINT("debug", ("Checking column %d -"
+ " field '%s' can be converted - order: %d",
+ col, field->field_name.str, convtype));
+ /*
+ If conversion type is not CONV_TYPE_RECISE, a conversion is required,
+ so we need to set up the conversion table.
+ */
+ if (convtype != CONV_TYPE_PRECISE && tmp_table == NULL)
+ {
+ /*
+ 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, rgi, table);
+ if (tmp_table == NULL)
+ return false;
+ /*
+ Clear all fields up to, but not including, this column.
+ */
+ for (unsigned int i= 0; i < col; ++i)
+ tmp_table->field[i]= NULL;
+ }
+
+ if (convtype == CONV_TYPE_PRECISE && tmp_table != NULL)
+ tmp_table->field[col]= NULL;
+ }
+ else
+ {
+ DBUG_PRINT("debug", ("Checking column %d -"
+ " field '%s' can not be converted",
+ col, field->field_name.str));
+ DBUG_ASSERT(col < size() && col < table->s->fields);
+ DBUG_ASSERT(table->s->db.str && table->s->table_name.str);
+ DBUG_ASSERT(table->in_use);
+ const char *db_name= table->s->db.str;
+ const char *tbl_name= table->s->table_name.str;
+ StringBuffer<MAX_FIELD_WIDTH> source_type(&my_charset_latin1);
+ StringBuffer<MAX_FIELD_WIDTH> target_type(&my_charset_latin1);
+ THD *thd= table->in_use;
+
+ show_sql_type(source, *field, &source_type);
+ field->sql_rpl_type(&target_type);
+ DBUG_ASSERT(source_type.length() > 0);
+ DBUG_ASSERT(target_type.length() > 0);
+ rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED, rgi->gtid_info(),
+ ER_THD(thd, ER_SLAVE_CONVERSION_FAILED),
+ col, db_name, tbl_name,
+ source_type.c_ptr_safe(), target_type.c_ptr_safe());
+ return false;
+ }
+ }
+
+#ifndef DBUG_OFF
+ if (tmp_table)
+ {
+ for (unsigned int col= 0; col < tmp_table->s->fields; ++col)
+ if (tmp_table->field[col])
+ {
+ char source_buf[MAX_FIELD_WIDTH];
+ char target_buf[MAX_FIELD_WIDTH];
+ String source_type(source_buf, sizeof(source_buf), &my_charset_latin1);
+ String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
+ tmp_table->field[col]->sql_type(source_type);
+ table->field[col]->sql_type(target_type);
+ DBUG_PRINT("debug", ("Field %s - conversion required."
+ " Source type: '%s', Target type: '%s'",
+ tmp_table->field[col]->field_name.str,
+ source_type.c_ptr_safe(), target_type.c_ptr_safe()));
+ }
+ }
+#endif
+
+ *conv_table_var= tmp_table;
+ return true;
+}
+
+
+/**
+ A wrapper to Virtual_tmp_table, to get access to its constructor,
+ which is protected for safety purposes (against illegal use on stack).
+*/
+class Virtual_conversion_table: public Virtual_tmp_table
+{
+public:
+ Virtual_conversion_table(THD *thd) :Virtual_tmp_table(thd) { }
+ /**
+ Add a new field into the virtual table.
+ @param handler - The type handler of the field.
+ @param metadata - The RBR binary log metadata for this field.
+ @param target_field - The field from the target table, to get extra
+ attributes from (e.g. typelib in case of ENUM).
+ */
+ bool add(const Type_handler *handler,
+ uint16 metadata, const Field *target_field)
+ {
+ Field *tmp= handler->make_conversion_table_field(in_use->mem_root,
+ this, metadata,
+ target_field);
+ if (!tmp)
+ return true;
+ Virtual_tmp_table::add(tmp);
+ DBUG_PRINT("debug", ("sql_type: %s, target_field: '%s', max_length: %d, decimals: %d,"
+ " maybe_null: %d, unsigned_flag: %d, pack_length: %u",
+ handler->name().ptr(), target_field->field_name.str,
+ tmp->field_length, tmp->decimals(), TRUE,
+ tmp->flags, tmp->pack_length()));
+ return false;
+ }
+};
+
+
+/**
+ Create a conversion table.
+
+ If the function is unable to create the conversion table, an error
+ will be printed and NULL will be returned.
+
+ @return Pointer to conversion table, or NULL if unable to create
+ conversion table.
+ */
+
+TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi,
+ TABLE *target_table) const
+{
+ DBUG_ENTER("table_def::create_conversion_table");
+
+ Virtual_conversion_table *conv_table;
+ Relay_log_info *rli= rgi->rli;
+ /*
+ At slave, columns may differ. So we should create
+ MY_MIN(columns@master, columns@slave) columns in the
+ conversion table.
+ */
+ uint const cols_to_create= MY_MIN(target_table->s->fields, size());
+ if (!(conv_table= new(thd) Virtual_conversion_table(thd)) ||
+ conv_table->init(cols_to_create))
+ goto err;
+ for (uint col= 0 ; col < cols_to_create; ++col)
+ {
+ const Type_handler *ha= field_type_handler(col);
+ DBUG_ASSERT(ha); // Checked at compatible_with() time
+ if (conv_table->add(ha, field_metadata(col), target_table->field[col]))
+ {
+ DBUG_PRINT("debug", ("binlog_type: %d, metadata: %04X, target_field: '%s'"
+ " make_conversion_table_field() failed",
+ binlog_type(col), field_metadata(col),
+ target_table->field[col]->field_name.str));
+ goto err;
+ }
+ }
+
+ if (conv_table->open())
+ goto err; // Could not allocate record buffer?
+
+ DBUG_RETURN(conv_table);
+
+err:
+ if (conv_table)
+ delete conv_table;
+ rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION, rgi->gtid_info(),
+ ER_THD(thd, ER_SLAVE_CANT_CREATE_CONVERSION),
+ target_table->s->db.str,
+ target_table->s->table_name.str);
+ DBUG_RETURN(NULL);
+}
+
+
+
+Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL)
+{
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &array, sizeof(Log_event *), 32, 16, MYF(0));
+}
+
+Deferred_log_events::~Deferred_log_events()
+{
+ delete_dynamic(&array);
+}
+
+int Deferred_log_events::add(Log_event *ev)
+{
+ last_added= ev;
+ insert_dynamic(&array, (uchar*) &ev);
+ return 0;
+}
+
+bool Deferred_log_events::is_empty()
+{
+ return array.elements == 0;
+}
+
+bool Deferred_log_events::execute(rpl_group_info *rgi)
+{
+ bool res= false;
+ DBUG_ENTER("Deferred_log_events::execute");
+ DBUG_ASSERT(rgi->deferred_events_collecting);
+
+ 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(rgi);
+ }
+ rgi->deferred_events_collecting= true;
+ DBUG_RETURN(res);
+}
+
+void Deferred_log_events::rewind()
+{
+ /*
+ Reset preceding Query log event events which execution was
+ deferred because of slave side filtering.
+ */
+ if (!is_empty())
+ {
+ for (uint i= 0; i < array.elements; i++)
+ {
+ Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i);
+ delete ev;
+ }
+ last_added= NULL;
+ if (array.elements > array.max_element)
+ freeze_size(&array);
+ reset_dynamic(&array);
+ }
+ last_added= NULL;
+}
+
+#endif // defined(HAVE_REPLICATION)
+
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
index 5a20566c89e..7380b134f13 100644
--- a/sql/scheduler.cc
+++ b/sql/scheduler.cc
@@ -24,26 +24,11 @@
#include "mariadb.h"
#include "mysqld.h"
-#include "sql_connect.h" // init_new_connection_handler_thread
#include "scheduler.h"
#include "sql_class.h"
#include "sql_callback.h"
#include <violite.h>
-/*
- End connection, in case when we are using 'no-threads'
-*/
-
-static bool no_threads_end(THD *thd, bool put_in_cache)
-{
- if (thd)
- {
- unlink_thd(thd);
- delete thd;
- }
- return 1; // Abort handle_one_connection
-}
-
/** @internal
Helper functions to allow mysys to call the thread scheduler when
waiting for locks.
@@ -127,22 +112,16 @@ void post_kill_notification(THD *thd)
void one_thread_per_connection_scheduler(scheduler_functions *func,
ulong *arg_max_connections,
- uint *arg_connection_count)
+ Atomic_counter<uint> *arg_connection_count)
{
scheduler_init();
func->max_threads= *arg_max_connections + 1;
func->max_connections= arg_max_connections;
func->connection_count= arg_connection_count;
- func->init_new_connection_thread= init_new_connection_handler_thread;
func->add_connection= create_thread_to_handle_connection;
- func->end_thread= one_thread_per_connection_end;
func->post_kill_notification= post_kill_notification;
}
#else
-bool init_new_connection_handler_thread()
-{
- return 0;
-}
void handle_connection_in_main_thread(CONNECT *connect)
{
}
@@ -158,7 +137,5 @@ void one_thread_scheduler(scheduler_functions *func)
func->max_threads= 1;
func->max_connections= &max_connections;
func->connection_count= &connection_count;
- func->init_new_connection_thread= init_new_connection_handler_thread;
func->add_connection= handle_connection_in_main_thread;
- func->end_thread= no_threads_end;
}
diff --git a/sql/scheduler.h b/sql/scheduler.h
index 42895134c83..ebf8d6e9e64 100644
--- a/sql/scheduler.h
+++ b/sql/scheduler.h
@@ -31,15 +31,14 @@ class THD;
struct scheduler_functions
{
- uint max_threads, *connection_count;
+ uint max_threads;
+ Atomic_counter<uint> *connection_count;
ulong *max_connections;
bool (*init)(void);
- bool (*init_new_connection_thread)(void);
void (*add_connection)(CONNECT *connect);
void (*thd_wait_begin)(THD *thd, int wait_type);
void (*thd_wait_end)(THD *thd);
void (*post_kill_notification)(THD *thd);
- bool (*end_thread)(THD *thd, bool cache_thread);
void (*end)(void);
};
@@ -72,7 +71,7 @@ enum scheduler_types
};
void one_thread_per_connection_scheduler(scheduler_functions *func,
- ulong *arg_max_connections, uint *arg_connection_count);
+ ulong *arg_max_connections, Atomic_counter<uint> *arg_connection_count);
void one_thread_scheduler(scheduler_functions *func);
extern void scheduler_init();
@@ -83,24 +82,13 @@ extern void post_kill_notification(THD *);
struct thd_scheduler
{
public:
- /*
- Thread instrumentation for the user job.
- This member holds the instrumentation while the user job is not run
- by a thread.
-
- Note that this member is not conditionally declared
- (ifdef HAVE_PSI_INTERFACE), because doing so will change the binary
- layout of THD, which is exposed to plugin code that may be compiled
- differently.
- */
- PSI_thread *m_psi;
void *data; /* scheduler-specific data structure */
};
#ifdef HAVE_POOL_OF_THREADS
void pool_of_threads_scheduler(scheduler_functions* func,
ulong *arg_max_connections,
- uint *arg_connection_count);
+ Atomic_counter<uint> *arg_connection_count);
#else
#define pool_of_threads_scheduler(A,B,C) \
one_thread_per_connection_scheduler(A, B, C)
diff --git a/sql/select_handler.cc b/sql/select_handler.cc
index 495b6d957b1..795ed8eb641 100644
--- a/sql/select_handler.cc
+++ b/sql/select_handler.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2018, 2019 MariaDB
+ Copyright (c) 2018, 2020, 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,10 +21,10 @@
/**
- The methods of the Pushdown_select class.
+ The methods of the select_handler class.
The objects of this class are used for pushdown of the select queries
- into engines. The main method of the class is Pushdown_select::execute()
+ into engines. The main method of the class is select_handler::execute()
that initiates execution of a select query by a foreign engine, receives the
rows of the result set, put it in a buffer of a temporary table and send
them from the buffer directly into output.
@@ -36,51 +36,55 @@
*/
-Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h)
- : select(sel), handler(h)
-{
- is_analyze= handler->thd->lex->analyze_stmt;
-}
+select_handler::select_handler(THD *thd_arg, handlerton *ht_arg)
+ : thd(thd_arg), ht(ht_arg), table(NULL),
+ is_analyze(thd_arg->lex->analyze_stmt)
+{}
-Pushdown_select::~Pushdown_select()
+select_handler::~select_handler()
{
- if (handler->table)
- free_tmp_table(handler->thd, handler->table);
- delete handler;
- select->select_h= NULL;
+ if (table)
+ free_tmp_table(thd, table);
}
-bool Pushdown_select::init()
+TABLE *select_handler::create_tmp_table(THD *thd, SELECT_LEX *select)
{
+ DBUG_ENTER("select_handler::create_tmp_table");
List<Item> types;
TMP_TABLE_PARAM tmp_table_param;
- THD *thd= handler->thd;
- DBUG_ENTER("Pushdown_select::init");
if (select->master_unit()->join_union_item_types(thd, types, 1))
- DBUG_RETURN(true);
+ DBUG_RETURN(NULL);
tmp_table_param.init();
tmp_table_param.field_count= types.elements;
- handler->table= create_tmp_table(thd, &tmp_table_param, types,
+ TABLE *table= ::create_tmp_table(thd, &tmp_table_param, types,
(ORDER *) 0, false, 0,
TMP_TABLE_ALL_COLUMNS, 1,
&empty_clex_str, true, false);
- if (!handler->table)
- DBUG_RETURN(true);
- if (handler->table->fill_item_list(&result_columns))
+ DBUG_RETURN(table);
+}
+
+
+bool select_handler::prepare()
+{
+ DBUG_ENTER("select_handler::prepare");
+ /*
+ Some engines (e.g. XPand) initialize "table" on their own.
+ So we need to create a temporary table only if "table" is NULL.
+ */
+ if (!table && !(table= create_tmp_table(thd, select)))
DBUG_RETURN(true);
- DBUG_RETURN(false);
+ DBUG_RETURN(table->fill_item_list(&result_columns));
}
-bool Pushdown_select::send_result_set_metadata()
+bool select_handler::send_result_set_metadata()
{
- DBUG_ENTER("Pushdown_select::send_result_set_metadata");
+ DBUG_ENTER("select_handler::send_result_set_metadata");
#ifdef WITH_WSREP
- THD *thd= handler->thd;
if (WSREP(thd) && thd->wsrep_retry_query)
{
WSREP_DEBUG("skipping select metadata");
@@ -96,14 +100,10 @@ bool Pushdown_select::send_result_set_metadata()
}
-bool Pushdown_select::send_data()
+bool select_handler::send_data()
{
- THD *thd= handler->thd;
DBUG_ENTER("Pushdown_select::send_data");
- if (thd->killed == ABORT_QUERY)
- DBUG_RETURN(false);
-
if (select->join->result->send_data(result_columns))
DBUG_RETURN(true);
@@ -111,9 +111,9 @@ bool Pushdown_select::send_data()
}
-bool Pushdown_select::send_eof()
+bool select_handler::send_eof()
{
- DBUG_ENTER("Pushdown_select::send_eof");
+ DBUG_ENTER("select_handler::send_eof");
if (select->join->result->send_eof())
DBUG_RETURN(true);
@@ -121,30 +121,29 @@ bool Pushdown_select::send_eof()
}
-int Pushdown_select::execute()
+int select_handler::execute()
{
int err;
- THD *thd= handler->thd;
- DBUG_ENTER("Pushdown_select::execute");
+ DBUG_ENTER("select_handler::execute");
- if ((err= handler->init_scan()))
+ if ((err= init_scan()))
goto error;
if (is_analyze)
{
- handler->end_scan();
+ end_scan();
DBUG_RETURN(0);
}
if (send_result_set_metadata())
DBUG_RETURN(-1);
- while (!(err= handler->next_row()))
+ while (!(err= next_row()))
{
if (thd->check_killed() || send_data())
{
- handler->end_scan();
+ end_scan();
DBUG_RETURN(-1);
}
}
@@ -152,7 +151,7 @@ int Pushdown_select::execute()
if (err != 0 && err != HA_ERR_END_OF_FILE)
goto error;
- if ((err= handler->end_scan()))
+ if ((err= end_scan()))
goto error_2;
if (send_eof())
@@ -161,9 +160,9 @@ int Pushdown_select::execute()
DBUG_RETURN(0);
error:
- handler->end_scan();
+ end_scan();
error_2:
- handler->print_error(err, MYF(0));
+ print_error(err, MYF(0));
DBUG_RETURN(-1); // Error not sent to client
}
diff --git a/sql/select_handler.h b/sql/select_handler.h
index e2ad13b7cdf..5cc63231641 100644
--- a/sql/select_handler.h
+++ b/sql/select_handler.h
@@ -41,12 +41,24 @@ class select_handler
The table is actually never filled. Only its record buffer is used.
*/
TABLE *table;
+ List<Item> result_columns;
- select_handler(THD *thd_arg, handlerton *ht_arg)
- : thd(thd_arg), ht(ht_arg), table(0) {}
+ bool is_analyze;
- virtual ~select_handler() {}
+ bool send_result_set_metadata();
+ bool send_data();
+ select_handler(THD *thd_arg, handlerton *ht_arg);
+
+ virtual ~select_handler();
+
+ int execute();
+
+ virtual bool prepare();
+
+ static TABLE *create_tmp_table(THD *thd, SELECT_LEX *sel);
+
+protected:
/*
Functions to scan the select result set.
All these returns 0 if ok, error code in case of error.
@@ -67,6 +79,8 @@ class select_handler
/* Report errors */
virtual void print_error(int error, myf errflag);
+
+ bool send_eof();
};
#endif /* SELECT_HANDLER_INCLUDED */
diff --git a/sql/semisync_master.cc b/sql/semisync_master.cc
index b239a9776a7..2480eebf8d7 100644
--- a/sql/semisync_master.cc
+++ b/sql/semisync_master.cc
@@ -682,7 +682,7 @@ int Repl_semi_sync_master::wait_after_commit(THD* thd, bool all)
my_off_t log_pos;
bool is_real_trans=
- (all || thd->transaction.all.ha_list == 0);
+ (all || thd->transaction->all.ha_list == 0);
/*
The coordinates are propagated to this point having been computed
in report_binlog_update
@@ -724,8 +724,8 @@ int Repl_semi_sync_master::report_binlog_update(THD* thd, const char *log_file,
if (!(log_info= thd->semisync_info))
{
- if(!(log_info=
- (Trans_binlog_info*) my_malloc(sizeof(Trans_binlog_info), MYF(0))))
+ if(!(log_info= (Trans_binlog_info*)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(Trans_binlog_info), MYF(0))))
return 1;
thd->semisync_info= log_info;
}
diff --git a/sql/semisync_master.h b/sql/semisync_master.h
index 74f6c24c8ea..9f0acf57a60 100644
--- a/sql/semisync_master.h
+++ b/sql/semisync_master.h
@@ -231,7 +231,7 @@ private:
*/
int allocate_block()
{
- Block *block= (Block *)my_malloc(sizeof(Block), MYF(0));
+ Block *block= (Block *)my_malloc(PSI_INSTRUMENT_ME, sizeof(Block), MYF(0));
if (block)
{
block->next= NULL;
diff --git a/sql/semisync_master_ack_receiver.cc b/sql/semisync_master_ack_receiver.cc
index e189fc5f631..6340b4bd661 100644
--- a/sql/semisync_master_ack_receiver.cc
+++ b/sql/semisync_master_ack_receiver.cc
@@ -164,7 +164,7 @@ void Ack_receiver::remove_slave(THD *thd)
inline void Ack_receiver::set_stage_info(const PSI_stage_info &stage)
{
- MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__);
+ (void)MYSQL_SET_STAGE(stage.m_key, __FILE__, __LINE__);
}
inline void Ack_receiver::wait_for_slave_connection()
diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc
index 4fc927cfd86..e7eee5f67a5 100644
--- a/sql/service_wsrep.cc
+++ b/sql/service_wsrep.cc
@@ -52,13 +52,14 @@ extern "C" const char* wsrep_thd_transaction_state_str(const THD *thd)
return wsrep::to_c_string(thd->wsrep_cs().transaction().state());
}
-
extern "C" const char *wsrep_thd_query(const THD *thd)
{
- if (thd)
+ if (!thd)
+ return "NULL";
+
+ switch(thd->lex->sql_command)
{
- switch(thd->lex->sql_command)
- {
+ // Mask away some security related details from error log
case SQLCOM_CREATE_USER:
return "CREATE USER";
case SQLCOM_GRANT:
@@ -67,12 +68,10 @@ extern "C" const char *wsrep_thd_query(const THD *thd)
return "REVOKE";
case SQLCOM_SET_OPTION:
if (thd->lex->definer)
- return "SET PASSWORD";
+ return "SET PASSWORD";
/* fallthrough */
default:
- if (thd->query())
- return thd->query();
- }
+ return (thd->query() ? thd->query() : "NULL");
}
return "NULL";
}
@@ -312,20 +311,57 @@ extern "C" int wsrep_thd_append_key(THD *thd,
}
ret= client_state.append_key(wsrep_key);
}
+ /*
+ In case of `wsrep_gtid_mode` when WS will be replicated, we need to set
+ `server_id` for events that are going to be written in IO, and in case of
+ manual SET gtid_seq_no=X we are ignoring value.
+ */
+ if (!ret && wsrep_gtid_mode && !thd->slave_thread && !wsrep_thd_is_applying(thd))
+ {
+ thd->variables.server_id= wsrep_gtid_server.server_id;
+ thd->variables.gtid_seq_no= 0;
+ }
return ret;
}
extern "C" void wsrep_commit_ordered(THD *thd)
{
if (wsrep_is_active(thd) &&
- thd->wsrep_trx().state() == wsrep::transaction::s_committing &&
- !wsrep_commit_will_write_binlog(thd))
+ (thd->wsrep_trx().state() == wsrep::transaction::s_committing ||
+ thd->wsrep_trx().state() == wsrep::transaction::s_ordered_commit))
{
- DEBUG_SYNC(thd, "before_wsrep_ordered_commit");
- thd->wsrep_cs().ordered_commit();
+ wsrep_gtid_server.signal_waiters(thd->wsrep_current_gtid_seqno, false);
+ if (wsrep_thd_is_local(thd))
+ {
+ thd->wsrep_last_written_gtid_seqno= thd->wsrep_current_gtid_seqno;
+ }
+ if (thd->wsrep_trx().state() != wsrep::transaction::s_ordered_commit &&
+ !wsrep_commit_will_write_binlog(thd))
+ {
+ DEBUG_SYNC(thd, "before_wsrep_ordered_commit");
+ thd->wsrep_cs().ordered_commit();
+ }
}
}
+extern "C" my_bool wsrep_thd_has_ignored_error(const THD *thd)
+{
+ return thd->wsrep_has_ignored_error;
+}
+
+extern "C" void wsrep_thd_set_ignored_error(THD *thd, my_bool val)
+{
+ thd->wsrep_has_ignored_error= val;
+}
+
+extern "C" ulong wsrep_OSU_method_get(const MYSQL_THD thd)
+{
+ if (thd)
+ return(thd->variables.wsrep_OSU_method);
+ else
+ return(global_system_variables.wsrep_OSU_method);
+}
+
extern "C" bool wsrep_thd_set_wsrep_aborter(THD *bf_thd, THD *victim_thd)
{
WSREP_DEBUG("wsrep_thd_set_wsrep_aborter called");
diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
index 2f6c5e29bf2..de82d8be90c 100644
--- a/sql/session_tracker.cc
+++ b/sql/session_tracker.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- Copyright (c) 2016, MariaDB
+ Copyright (c) 2016, 2020, 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,6 @@
#include "sql_plugin.h"
-#include "hash.h"
#include "table.h"
#include "rpl_gtid.h"
#include "sql_class.h"
@@ -24,7 +23,7 @@
#include "sql_plugin.h"
#include "set_var.h"
-void State_tracker::mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name)
+void State_tracker::set_changed(THD *thd)
{
m_changed= true;
thd->lex->safe_to_cache_query= 0;
@@ -73,7 +72,8 @@ void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
bool Session_sysvars_tracker::vars_list::insert(const sys_var *svar)
{
sysvar_node_st *node;
- if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
+ if (!(node= (sysvar_node_st *) my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(sysvar_node_st),
MYF(MY_WME |
(mysqld_server_initialized ?
MY_THREAD_SPECIFIC : 0)))))
@@ -333,7 +333,8 @@ void Session_sysvars_tracker::init(THD *thd)
global_system_variables.session_track_system_variables);
DBUG_ASSERT(global_system_variables.session_track_system_variables);
thd->variables.session_track_system_variables=
- my_strdup(global_system_variables.session_track_system_variables,
+ my_strdup(PSI_INSTRUMENT_ME,
+ global_system_variables.session_track_system_variables,
MYF(MY_WME | MY_THREAD_SPECIFIC));
}
@@ -387,11 +388,13 @@ bool Session_sysvars_tracker::update(THD *thd, set_var *var)
{
vars_list tool_list;
size_t length= 1;
+
void *copy= var->save_result.string_value.str ?
- my_memdup(var->save_result.string_value.str,
+ my_memdup(PSI_INSTRUMENT_ME, var->save_result.string_value.str,
(length= var->save_result.string_value.length + 1),
MYF(MY_WME | MY_THREAD_SPECIFIC)) :
- my_strdup("", MYF(MY_WME | MY_THREAD_SPECIFIC));
+ my_strdup(PSI_INSTRUMENT_ME, "",
+ MYF(MY_WME | MY_THREAD_SPECIFIC));
if (!copy)
return true;
@@ -511,11 +514,12 @@ bool Session_sysvars_tracker::store(THD *thd, String *buf)
@param [IN] pointer on a variable
*/
-void Session_sysvars_tracker::mark_as_changed(THD *thd,
- LEX_CSTRING *var)
+void Session_sysvars_tracker::mark_as_changed(THD *thd, const sys_var *var)
{
sysvar_node_st *node;
- sys_var *svar= (sys_var *)var;
+
+ if (!is_enabled())
+ return;
if (!m_parsed)
{
@@ -534,10 +538,10 @@ void Session_sysvars_tracker::mark_as_changed(THD *thd,
Check if the specified system variable is being tracked, if so
mark it as changed and also set the class's m_changed flag.
*/
- if (orig_list.is_enabled() && (node= orig_list.insert_or_search(svar)))
+ if (orig_list.is_enabled() && (node= orig_list.insert_or_search(var)))
{
node->m_changed= true;
- State_tracker::mark_as_changed(thd, var);
+ set_changed(thd);
}
}
@@ -684,7 +688,7 @@ bool Transaction_state_tracker::update(THD *thd, set_var *)
}
if (thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS)
tx_changed |= TX_CHG_CHISTICS;
- mark_as_changed(thd, NULL);
+ set_changed(thd);
}
else
m_enabled= false;
@@ -744,7 +748,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
if ((thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) &&
(tx_changed & TX_CHG_CHISTICS))
{
- bool is_xa= thd->transaction.xid_state.is_explicit_XA();
+ bool is_xa= thd->transaction->xid_state.is_explicit_XA();
size_t start;
/* 2 length by 1 byte and code */
@@ -921,7 +925,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf)
if ((tx_curr_state & TX_EXPLICIT) && is_xa)
{
- XID *xid= thd->transaction.xid_state.get_xid();
+ XID *xid= thd->transaction->xid_state.get_xid();
long glen, blen;
buf->append(STRING_WITH_LEN("XA START"));
@@ -1117,7 +1121,7 @@ void Transaction_state_tracker::set_read_flags(THD *thd,
{
tx_read_flags = flags;
tx_changed |= TX_CHG_CHISTICS;
- mark_as_changed(thd, NULL);
+ set_changed(thd);
}
}
@@ -1136,7 +1140,7 @@ void Transaction_state_tracker::set_isol_level(THD *thd,
{
tx_isol_level = level;
tx_changed |= TX_CHG_CHISTICS;
- mark_as_changed(thd, NULL);
+ set_changed(thd);
}
}
@@ -1186,6 +1190,53 @@ bool Session_state_change_tracker::store(THD *thd, String *buf)
return false;
}
+#ifdef USER_VAR_TRACKING
+
+bool User_variables_tracker::update(THD *thd, set_var *)
+{
+ m_enabled= thd->variables.session_track_user_variables;
+ return false;
+}
+
+
+bool User_variables_tracker::store(THD *thd, String *buf)
+{
+ for (ulong i= 0; i < m_changed_user_variables.size(); i++)
+ {
+ const user_var_entry *var= m_changed_user_variables.at(i);
+ String value_str;
+ bool null_value;
+ size_t length;
+
+ var->val_str(&null_value, &value_str, DECIMAL_MAX_SCALE);
+ length= net_length_size(var->name.length) + var->name.length;
+ if (!null_value)
+ length+= net_length_size(value_str.length()) + value_str.length();
+ else
+ length+= 1;
+
+ if (buf->reserve(sizeof(char) + length + net_length_size(length)))
+ return true;
+
+ // TODO: check max packet length MDEV-22709
+ buf->q_append(static_cast<char>(SESSION_TRACK_USER_VARIABLES));
+ buf->q_net_store_length(length);
+ buf->q_net_store_data(reinterpret_cast<const uchar*>(var->name.str),
+ var->name.length);
+ if (!null_value)
+ buf->q_net_store_data(reinterpret_cast<const uchar*>(value_str.ptr()),
+ value_str.length());
+ else
+ {
+ char nullbuff[1]= { (char)251 };
+ buf->q_append(nullbuff, sizeof(nullbuff));
+ }
+ }
+ m_changed_user_variables.clear();
+ return false;
+}
+#endif // USER_VAR_TRACKING
+
///////////////////////////////////////////////////////////////////////////////
/**
diff --git a/sql/session_tracker.h b/sql/session_tracker.h
index 226b026d590..c78778ac73c 100644
--- a/sql/session_tracker.h
+++ b/sql/session_tracker.h
@@ -19,12 +19,14 @@
#include "m_string.h"
#include "thr_lock.h"
+#include "sql_hset.h"
#ifndef EMBEDDED_LIBRARY
/* forward declarations */
class THD;
class set_var;
class String;
+class user_var_entry;
enum enum_session_tracker
@@ -33,6 +35,9 @@ enum enum_session_tracker
CURRENT_SCHEMA_TRACKER, /* Current schema */
SESSION_STATE_CHANGE_TRACKER,
TRANSACTION_INFO_TRACKER, /* Transaction state */
+#ifdef USER_VAR_TRACKING
+ USER_VARIABLES_TRACKER,
+#endif // USER_VAR_TRACKING
SESSION_TRACKER_END /* must be the last */
};
@@ -66,6 +71,8 @@ protected:
*/
bool m_enabled;
+ void set_changed(THD *thd);
+
private:
/** Has the session state type changed ? */
bool m_changed;
@@ -102,7 +109,7 @@ public:
virtual bool store(THD *thd, String *buf)= 0;
/** Mark the entity as changed. */
- virtual void mark_as_changed(THD *thd, LEX_CSTRING *name);
+ void mark_as_changed(THD *thd) { if (is_enabled()) set_changed(thd); }
};
@@ -137,10 +144,9 @@ class Session_sysvars_tracker: public State_tracker
bool track_all;
void init()
{
- my_hash_init(&m_registered_sysvars, &my_charset_bin, 0, 0, 0,
- (my_hash_get_key) sysvars_get_key, my_free,
- HASH_UNIQUE | (mysqld_server_initialized ?
- HASH_THREAD_SPECIFIC : 0));
+ my_hash_init(PSI_INSTRUMENT_ME, &m_registered_sysvars, &my_charset_bin,
+ 0, 0, 0, (my_hash_get_key) sysvars_get_key, my_free,
+ HASH_UNIQUE | (mysqld_server_initialized ? HASH_THREAD_SPECIFIC : 0));
}
void free_hash()
{
@@ -207,7 +213,7 @@ public:
bool enable(THD *thd);
bool update(THD *thd, set_var *var);
bool store(THD *thd, String *buf);
- void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
+ void mark_as_changed(THD *thd, const sys_var *var);
void deinit() { orig_list.deinit(); }
/* callback */
static uchar *sysvars_get_key(const char *entry, size_t *length,
@@ -376,15 +382,43 @@ private:
tx_changed &= uint(~TX_CHG_STATE);
tx_changed |= (tx_curr_state != tx_reported_state) ? TX_CHG_STATE : 0;
if (tx_changed != TX_CHG_NONE)
- mark_as_changed(thd, NULL);
+ set_changed(thd);
}
};
#define TRANSACT_TRACKER(X) \
do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \
thd->session_tracker.transaction_info.X; } while(0)
-#define SESSION_TRACKER_CHANGED(A,B,C) \
- thd->session_tracker.mark_as_changed(A,B,C)
+
+
+/**
+ User_variables_tracker
+
+ This is a tracker class that enables & manages the tracking of user variables.
+*/
+
+#ifdef USER_VAR_TRACKING
+class User_variables_tracker: public State_tracker
+{
+ Hash_set<const user_var_entry> m_changed_user_variables;
+public:
+ User_variables_tracker():
+ m_changed_user_variables(PSI_INSTRUMENT_ME, &my_charset_bin, 0, 0,
+ sizeof(const user_var_entry*), 0, 0, HASH_UNIQUE |
+ mysqld_server_initialized ? HASH_THREAD_SPECIFIC : 0) {}
+ bool update(THD *thd, set_var *var);
+ bool store(THD *thd, String *buf);
+ void mark_as_changed(THD *thd, const user_var_entry *var)
+ {
+ if (is_enabled())
+ {
+ m_changed_user_variables.insert(var);
+ set_changed(thd);
+ }
+ }
+ void deinit() { m_changed_user_variables.~Hash_set(); }
+};
+#endif // USER_VAR_TRACKING
/**
@@ -415,6 +449,9 @@ public:
Session_state_change_tracker state_change;
Transaction_state_tracker transaction_info;
Session_sysvars_tracker sysvars;
+#ifdef USER_VAR_TRACKING
+ User_variables_tracker user_variables;
+#endif // USER_VAR_TRACKING
Session_tracker()
{
@@ -422,6 +459,9 @@ public:
m_trackers[CURRENT_SCHEMA_TRACKER]= &current_schema;
m_trackers[SESSION_STATE_CHANGE_TRACKER]= &state_change;
m_trackers[TRANSACTION_INFO_TRACKER]= &transaction_info;
+#ifdef USER_VAR_TRACKING
+ m_trackers[USER_VARIABLES_TRACKER]= &user_variables;
+#endif // USER_VAR_TRACKING
}
void enable(THD *thd)
@@ -430,14 +470,6 @@ public:
m_trackers[i]->enable(thd);
}
- inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker,
- LEX_CSTRING *data)
- {
- if (m_trackers[tracker]->is_enabled())
- m_trackers[tracker]->mark_as_changed(thd, data);
- }
-
-
void store(THD *thd, String *main_buf);
};
@@ -446,7 +478,20 @@ int session_tracker_init();
#else
#define TRANSACT_TRACKER(X) do{}while(0)
-#define SESSION_TRACKER_CHANGED(A,B,C) do{}while(0)
+
+class Session_tracker
+{
+ class Dummy_tracker
+ {
+ public:
+ void mark_as_changed(THD *thd) {}
+ void mark_as_changed(THD *thd, const sys_var *var) {}
+ };
+public:
+ Dummy_tracker current_schema;
+ Dummy_tracker state_change;
+ Dummy_tracker sysvars;
+};
#endif //EMBEDDED_LIBRARY
diff --git a/sql/set_var.cc b/sql/set_var.cc
index bebed453292..fe98d98fbd4 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -33,9 +33,8 @@
// date_time_format_make
#include "derror.h"
#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
-#include "sql_acl.h" // SUPER_ACL
#include "sql_select.h" // free_underlaid_joins
-#include "sql_show.h"
+#include "sql_i_s.h"
#include "sql_view.h" // updatable_views_with_limit_typelib
#include "lock.h" // lock_global_read_lock,
// make_global_read_lock_block_commit,
@@ -43,6 +42,7 @@
static HASH system_variable_hash;
static PolyLock_mutex PLock_global_system_variables(&LOCK_global_system_variables);
+static ulonglong system_variable_hash_version= 0;
/**
Return variable name and length for hashing of variables.
@@ -64,7 +64,7 @@ int sys_var_init()
/* Must be already initialized. */
DBUG_ASSERT(system_charset_info != NULL);
- if (my_hash_init(&system_variable_hash, system_charset_info, 700, 0,
+ if (my_hash_init(PSI_INSTRUMENT_ME, &system_variable_hash, system_charset_info, 700, 0,
0, (my_hash_get_key) get_sys_var_length, 0, HASH_UNIQUE))
goto error;
@@ -154,8 +154,7 @@ sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
next(0), binlog_status(binlog_status_arg), value_origin(COMPILE_TIME),
flags(flags_arg), show_val_type(show_val_type_arg),
guard(lock), offset(off), on_check(on_check_func), on_update(on_update_func),
- deprecation_substitute(substitute),
- is_os_charset(FALSE)
+ deprecation_substitute(substitute)
{
/*
There is a limitation in handle_options() related to short options:
@@ -220,13 +219,12 @@ bool sys_var::update(THD *thd, set_var *var)
*/
if ((var->type == OPT_SESSION) && (!ret))
{
- SESSION_TRACKER_CHANGED(thd, SESSION_SYSVARS_TRACKER,
- (LEX_CSTRING*)var->var);
+ thd->session_tracker.sysvars.mark_as_changed(thd, var->var);
/*
Here MySQL sends variable name to avoid reporting change of
the tracker itself, but we decided that it is not needed
*/
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
}
return ret;
@@ -510,12 +508,6 @@ bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v)
return false;
}
-CHARSET_INFO *sys_var::charset(THD *thd)
-{
- return is_os_charset ? thd->variables.character_set_filesystem :
- system_charset_info;
-}
-
typedef struct old_names_map_st
{
@@ -584,6 +576,8 @@ int mysql_add_sys_var_chain(sys_var *first)
goto error;
}
}
+ /* Update system_variable_hash version. */
+ system_variable_hash_version++;
return 0;
error:
@@ -614,6 +608,8 @@ int mysql_del_sys_var_chain(sys_var *first)
result|= my_hash_delete(&system_variable_hash, (uchar*) var);
mysql_prlock_unlock(&LOCK_system_variables_hash);
+ /* Update system_variable_hash version. */
+ system_variable_hash_version++;
return result;
}
@@ -624,6 +620,16 @@ static int show_cmp(SHOW_VAR *a, SHOW_VAR *b)
}
+/*
+ Number of records in the system_variable_hash.
+ Requires lock on LOCK_system_variables_hash.
+*/
+ulong get_system_variable_hash_records(void)
+{
+ return system_variable_hash.records;
+}
+
+
/**
Constructs an array of system variables for display to the user.
@@ -750,6 +756,11 @@ err:
Functions to handle SET mysql_internal_variable=const_expr
*****************************************************************************/
+bool sys_var::on_check_access_global(THD *thd) const
+{
+ return check_global_access(thd, PRIV_SET_GLOBAL_SYSTEM_VARIABLE);
+}
+
/**
Verify that the supplied value is correct.
@@ -774,7 +785,7 @@ int set_var::check(THD *thd)
my_error(err, MYF(0), var->name.str);
return -1;
}
- if ((type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL)))
+ if (type == OPT_GLOBAL && var->on_check_access_global(thd))
return 1;
/* value is a NULL pointer if we are using SET ... = DEFAULT */
if (!value)
@@ -787,6 +798,16 @@ int set_var::check(THD *thd)
my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name.str);
return -1;
}
+ switch (type) {
+ case SHOW_OPT_DEFAULT:
+ case SHOW_OPT_SESSION:
+ DBUG_ASSERT(var->scope() != sys_var::GLOBAL);
+ if (var->on_check_access_session(thd))
+ return -1;
+ break;
+ case SHOW_OPT_GLOBAL: // Checked earlier
+ break;
+ }
return var->check(thd, this) ? -1 : 0;
}
@@ -811,7 +832,8 @@ int set_var::light_check(THD *thd)
my_error(err, MYF(0), var->name.str);
return -1;
}
- if (type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))
+ if (type == OPT_GLOBAL &&
+ check_global_access(thd, PRIV_SET_GLOBAL_SYSTEM_VARIABLE))
return 1;
if (value && value->fix_fields_if_needed_for_scalar(thd, &value))
@@ -907,7 +929,7 @@ int set_var_user::update(THD *thd)
return -1;
}
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
return 0;
}
@@ -957,8 +979,7 @@ int set_var_role::update(THD *thd)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
int res= acl_setrole(thd, role.str, access);
if (!res)
- thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER,
- NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
return res;
#else
return 0;
@@ -1025,18 +1046,13 @@ int set_var_collation_client::update(THD *thd)
character_set_results);
/* Mark client collation variables as changed */
-#ifndef EMBEDDED_LIBRARY
- if (thd->session_tracker.sysvars.is_enabled())
- {
- thd->session_tracker.sysvars.
- mark_as_changed(thd, (LEX_CSTRING*)Sys_character_set_client_ptr);
- thd->session_tracker.sysvars.
- mark_as_changed(thd, (LEX_CSTRING*)Sys_character_set_results_ptr);
- thd->session_tracker.sysvars.
- mark_as_changed(thd, (LEX_CSTRING*)Sys_character_set_connection_ptr);
- }
- thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
-#endif //EMBEDDED_LIBRARY
+ thd->session_tracker.sysvars.mark_as_changed(thd,
+ Sys_character_set_client_ptr);
+ thd->session_tracker.sysvars.mark_as_changed(thd,
+ Sys_character_set_results_ptr);
+ thd->session_tracker.sysvars.mark_as_changed(thd,
+ Sys_character_set_connection_ptr);
+ thd->session_tracker.state_change.mark_as_changed(thd);
thd->protocol_text.init(thd);
thd->protocol_binary.init(thd);
@@ -1073,6 +1089,7 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
StringBuffer<STRING_BUFFER_USUAL_SIZE> strbuf(scs);
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : 0;
Field **fields=tables->table->field;
+ bool has_file_acl= !check_access(thd, FILE_ACL, any_db, NULL, NULL, 0, 1);
DBUG_ASSERT(tables->table->in_use == thd);
@@ -1106,6 +1123,7 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
static const LEX_CSTRING origins[]=
{
{ STRING_WITH_LEN("CONFIG") },
+ { STRING_WITH_LEN("COMMAND-LINE") },
{ STRING_WITH_LEN("AUTO") },
{ STRING_WITH_LEN("SQL") },
{ STRING_WITH_LEN("COMPILE-TIME") },
@@ -1234,6 +1252,14 @@ int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
fields[13]->store(arg->str, arg->length, scs);
}
+ // GLOBAL_VALUE_PATH
+ if (var->value_origin == sys_var::CONFIG && has_file_acl)
+ {
+ fields[14]->set_notnull();
+ fields[14]->store(var->origin_filename, strlen(var->origin_filename),
+ files_charset_info);
+ }
+
if (schema_table_store_record(thd, tables->table))
goto end;
thd->get_stmt_da()->inc_current_row_for_warning();
@@ -1381,7 +1407,7 @@ resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
if (temp_copy)
res= (plugin_ref *)thd->calloc((count+1)*sizeof(*res));
else
- res= (plugin_ref *)my_malloc((count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME));
+ res= (plugin_ref *)my_malloc(PSI_INSTRUMENT_ME, (count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME));
if (!res)
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res)));
@@ -1432,7 +1458,7 @@ copy_engine_list(plugin_ref *list)
for (p= list, count= 0; *p; ++p, ++count)
;
- p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0));
+ p= (plugin_ref *)my_malloc(PSI_INSTRUMENT_ME, (count+1)*sizeof(*p), MYF(0));
if (!p)
{
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
@@ -1507,3 +1533,13 @@ pretty_print_engine_list(THD *thd, plugin_ref *list)
*pos= '\0';
return buf;
}
+
+/*
+ Current version of the system_variable_hash.
+ Requires lock on LOCK_system_variables_hash.
+*/
+ulonglong get_system_variable_hash_version(void)
+{
+ return system_variable_hash_version;
+}
+
diff --git a/sql/set_var.h b/sql/set_var.h
index ca862b87fd4..18c4dbc664e 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -66,7 +66,7 @@ public:
READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096,
NO_SET_STATEMENT=8192, AUTO_SET=16384};
enum { NO_GETOPT=-1, GETOPT_ONLY_HELP=-2 };
- enum where { CONFIG, AUTO, SQL, COMPILE_TIME, ENV };
+ enum where { CONFIG, COMMAND_LINE, AUTO, SQL, COMPILE_TIME, ENV };
/**
Enumeration type to indicate for a system variable whether
@@ -77,6 +77,7 @@ public:
my_option option; ///< min, max, default values are stored here
enum where value_origin;
+ const char *origin_filename;
protected:
typedef bool (*on_check_function)(sys_var *self, THD *thd, set_var *var);
@@ -89,7 +90,6 @@ protected:
on_check_function on_check;
on_update_function on_update;
const char *const deprecation_substitute;
- bool is_os_charset; ///< true if the value is in character_set_filesystem
public:
sys_var(sys_var_chain *chain, const char *name_arg, const char *comment,
@@ -129,7 +129,10 @@ public:
SHOW_TYPE show_type() { return show_val_type; }
int scope() const { return flags & SCOPE_MASK; }
- CHARSET_INFO *charset(THD *thd);
+ virtual CHARSET_INFO *charset(THD *thd) const
+ {
+ return system_charset_info;
+ }
bool is_readonly() const { return flags & READONLY; }
/**
the following is only true for keycache variables,
@@ -211,6 +214,12 @@ public:
virtual uchar *default_value_ptr(THD *thd)
{ return (uchar*)&option.def_value; }
+ virtual bool on_check_access_global(THD *thd) const;
+ virtual bool on_check_access_session(THD *thd) const
+ {
+ return false;
+ }
+
private:
virtual bool do_check(THD *thd, set_var *var) = 0;
/**
@@ -348,9 +357,9 @@ public:
class set_var_role: public set_var_base
{
LEX_CSTRING role;
- ulonglong access;
+ privilege_t access;
public:
- set_var_role(LEX_CSTRING role_arg) : role(role_arg) {}
+ set_var_role(LEX_CSTRING role_arg) : role(role_arg), access(NO_ACL) {}
int check(THD *thd);
int update(THD *thd);
};
@@ -404,6 +413,8 @@ extern SHOW_COMP_OPTION have_openssl;
/*
Prototypes for helper functions
*/
+ulong get_system_variable_hash_records(void);
+ulonglong get_system_variable_hash_version(void);
SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type);
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond);
@@ -443,7 +454,7 @@ sql_mode_t expand_sql_mode(sql_mode_t sql_mode);
const char *sql_mode_string_representation(uint bit_number);
bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode,
LEX_CSTRING *ls);
-int default_regex_flags_pcre(const THD *thd);
+int default_regex_flags_pcre(THD *thd);
extern sys_var *Sys_autocommit_ptr, *Sys_last_gtid_ptr,
*Sys_character_set_client_ptr, *Sys_character_set_connection_ptr,
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index ef0dfd5eb63..3f3cb7677fc 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -951,7 +951,7 @@ ER_CON_COUNT_ERROR 08004
ER_OUT_OF_RESOURCES
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"
+ nla "Geen thread geheugen meer; controleer of mariadbd of andere processen al het beschikbare geheugen gebruikt. Zo niet, dan moet u wellicht 'ulimit' gebruiken om mariadbd toe te laten meer geheugen te benutten, of u kunt extra swap ruimte toevoegen"
eng "Out of memory."
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"
@@ -959,7 +959,7 @@ ER_OUT_OF_RESOURCES
greek "Πρόβλημα με τη διαθέσιμη μνήμη (Out of thread space/memory)"
hun "Elfogyott a thread-memoria"
ita "Fine dello spazio/memoria per i thread"
- jpn "メモリが不足しています。mysqld やその他のプロセスがメモリーを使い切っていないか確認して下さい。メモリーを使い切っていない場合、'ulimit'の設定等で mysqld のメモリー使用最大量を多くするか、スワップ領域を増やす必要があるかもしれません。"
+ jpn "メモリが不足しています。mariadbd やその他のプロセスがメモリーを使い切っていないか確認して下さい。メモリーを使い切っていない場合、'ulimit'の設定等で mariadbd のメモリー使用最大量を多くするか、スワップ領域を増やす必要があるかもしれません。"
# This message failed to convert from euc-kr, skipped
nor "Tomt for tråd plass/minne"
norwegian-ny "Tomt for tråd plass/minne"
@@ -2784,25 +2784,25 @@ 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ř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"
- 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 "スレッドスタック不足です(使用: %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"
- rus "Стек потоков переполнен: использовано: %ld из %ld стека. Применяйте 'mysqld --thread_stack=#' для указания большего размера стека, если необходимо"
- serbian "Prepisivanje thread stack-a: Upotrebljeno: %ld od %ld stack memorije. Upotrebite 'mysqld --thread_stack=#' da navedete veći stack ako je potrebno"
- slo "Pretečenie zásobníku vlákna: použité: %ld z %ld. Použite 'mysqld --thread_stack=#' k zadaniu väčšieho zásobníka"
- spa "Sobrecarga de la pila de thread: Usada: %ld de una %ld pila. Use 'mysqld --thread_stack=#' para especificar una mayor pila si necesario"
- 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=#' аби зазначити більший стек, якщо необхідно"
+ cze "Přetečení zásobníku threadu: použito %ld z %ld. Použijte 'mariadbd --thread_stack=#' k zadání většího zásobníku"
+ dan "Thread stack brugt: Brugt: %ld af en %ld stak. Brug 'mariadbd --thread_stack=#' for at allokere en større stak om nødvendigt"
+ nla "Thread stapel overrun: Gebruikte: %ld van een %ld stack. Gebruik 'mariadbd --thread_stack=#' om een grotere stapel te definieren (indien noodzakelijk)"
+ eng "Thread stack overrun: Used: %ld of a %ld stack. Consider increasing the thread_stack system variable"
+ fre "Débordement de la pile des tâches (Thread stack). Utilisées: %ld pour une pile de %ld. Essayez 'mariadbd --thread_stack=#' pour indiquer une plus grande valeur"
+ ger "Thread-Stack-Überlauf. Benutzt: %ld von %ld Stack. 'mariadbd --thread_stack=#' verwenden, um bei Bedarf einen größeren Stack anzulegen"
+ greek "Stack overrun στο thread: Used: %ld of a %ld stack. Παρακαλώ χρησιμοποιείστε 'mariadbd --thread_stack=#' για να ορίσετε ένα μεγαλύτερο stack αν χρειάζεται"
+ hun "Thread verem tullepes: Used: %ld of a %ld stack. Hasznalja a 'mariadbd --thread_stack=#' nagyobb verem definialasahoz"
+ ita "Thread stack overrun: Usati: %ld di uno stack di %ld. Usa 'maridbd --thread_stack=#' per specificare uno stack piu` grande"
+ jpn "スレッドスタック不足です(使用: %ld ; サイズ: %ld)。必要に応じて、より大きい値で 'mariadbd --thread_stack=#' の指定をしてください。"
+ kor "쓰레드 스택이 넘쳤습니다. 사용: %ld개 스택: %ld개. 만약 필요시 더큰 스택을 원할때에는 'mariadbd --thread_stack=#' 를 정의하세요"
+ por "Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld. Use 'mariadbd --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 'mariadbd --thread_stack=#' ca sa specifici un stack mai mare"
+ rus "Стек потоков переполнен: использовано: %ld из %ld стека. Применяйте 'mariadbd --thread_stack=#' для указания большего размера стека, если необходимо"
+ serbian "Prepisivanje thread stack-a: Upotrebljeno: %ld od %ld stack memorije. Upotrebite 'mariadbd --thread_stack=#' da navedete veći stack ako je potrebno"
+ slo "Pretečenie zásobníku vlákna: použité: %ld z %ld. Použite 'mariadbd --thread_stack=#' k zadaniu väčšieho zásobníka"
+ spa "Sobrecarga de la pila de thread: Usada: %ld de una %ld pila. Use 'mariadbd --thread_stack=#' para especificar una mayor pila si necesario"
+ swe "Trådstacken tog slut: Har använt %ld av %ld bytes. Använd 'mariadbd --thread_stack=#' ifall du behöver en större stack"
+ ukr "Стек гілок переповнено: Використано: %ld з %ld. Використовуйте 'mariadbd --thread_stack=#' аби зазначити більший стек, якщо необхідно"
ER_WRONG_OUTER_JOIN 42000
cze "V OUTER JOIN byl nalezen křížový odkaz. Prověřte ON podmínky"
dan "Krydsreferencer fundet i OUTER JOIN; check dine ON conditions"
@@ -2982,26 +2982,26 @@ ER_FUNCTION_NOT_DEFINED
swe "Funktionen '%-.192s' är inte definierad"
ukr "Функцію '%-.192s' не визначено"
ER_HOST_IS_BLOCKED
- 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'"
- 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'"
- hindi "होस्ट '%-.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 "接続エラーが多いため、ホスト '%-.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'"
- rus "Хост '%-.64s' заблокирован из-за слишком большого количества ошибок соединения. Разблокировать его можно с помощью 'mysqladmin flush-hosts'"
- serbian "Host '%-.64s' je blokiran zbog previše grešaka u konekciji. Možete ga odblokirati pomoću komande 'mysqladmin flush-hosts'"
- spa "Servidor '%-.64s' está bloqueado por muchos errores de conexión. Desbloquear con 'mysqladmin flush-hosts'"
- 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'"
+ cze "Stroj '%-.64s' je zablokován kvůli mnoha chybám při připojování. Odblokujete použitím 'mariadb-admin flush-hosts'"
+ dan "Værten '%-.64s' er blokeret på grund af mange fejlforespørgsler. Lås op med 'mariadb-admin flush-hosts'"
+ nla "Host '%-.64s' is geblokkeeerd vanwege te veel verbindings fouten. Deblokkeer met 'mariadb-admin flush-hosts'"
+ eng "Host '%-.64s' is blocked because of many connection errors; unblock with 'mariadb-admin flush-hosts'"
+ est "Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mariadb-admin 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 'mariadb-admin flush-hosts'"
+ ger "Host '%-.64s' blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mariadb-admin flush-hosts'"
+ greek "Ο υπολογιστής '%-.64s' έχει αποκλεισθεί λόγω πολλαπλών λαθών σύνδεσης. Προσπαθήστε να διορώσετε με 'mariadb-admin flush-hosts'"
+ hindi "होस्ट '%-.64s' को कई कनेक्शन में त्रुटियों के कारण ब्लॉक कर दिया गया है; 'mariadb-admin flush-hosts' का इस्तेमाल कर अनब्लॉक करें"
+ hun "A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mariadb-admin flush-hosts' parancsot"
+ ita "Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mariadb-admin flush-hosts'"
+ jpn "接続エラーが多いため、ホスト '%-.64s' は拒否されました。'mariadb-admin flush-hosts' で解除できます。"
+ kor "너무 많은 연결오류로 인하여 호스트 '%-.64s'는 블락되었습니다. 'mariadb-admin flush-hosts'를 이용하여 블락을 해제하세요"
+ por "'Host' '%-.64s' está bloqueado devido a muitos erros de conexão. Desbloqueie com 'mariadb-admin flush-hosts'"
+ rum "Host-ul '%-.64s' e blocat din cauza multelor erori de conectie. Poti deploca folosind 'mariadb-admin flush-hosts'"
+ rus "Хост '%-.64s' заблокирован из-за слишком большого количества ошибок соединения. Разблокировать его можно с помощью 'mariadb-admin flush-hosts'"
+ serbian "Host '%-.64s' je blokiran zbog previše grešaka u konekciji. Možete ga odblokirati pomoću komande 'mariadb-admin flush-hosts'"
+ spa "Servidor '%-.64s' está bloqueado por muchos errores de conexión. Desbloquear con 'mariadb-admin flush-hosts'"
+ swe "Denna dator, '%-.64s', är blockerad pga många felaktig paket. Gör 'mariadb-admin flush-hosts' för att ta bort alla blockeringarna"
+ ukr "Хост '%-.64s' заблоковано з причини великої кількості помилок з'єднання. Для розблокування використовуйте 'mariadb-admin flush-hosts'"
ER_HOST_NOT_PRIVILEGED
cze "Stroj '%-.64s' nemá povoleno se k tomuto MariaDB serveru připojit"
dan "Værten '%-.64s' kan ikke tilkoble denne MariaDB-server"
@@ -3046,7 +3046,7 @@ ER_PASSWORD_ANONYMOUS_USER 42000
ukr "Ви використовуєте MariaDB як анонімний користувач, тому вам не дозволено змінювати паролі"
ER_PASSWORD_NOT_ALLOWED 42000
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"
+ dan "Du skal have tilladelse til at opdatere tabeller i mysql 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"
est "Teiste paroolide muutmiseks on nõutav tabelite muutmisõigus 'mysql' andmebaasis"
@@ -3189,13 +3189,13 @@ ER_INVALID_USE_OF_NULL 22004
swe "Felaktig använding av NULL"
ukr "Хибне використання значення NULL"
ER_REGEXP_ERROR 42000
- cze "Regulární výraz vrátil chybu '%-.64s'"
+ cze "Regulární výraz vrátil chybu: %s"
dan "Fik fejl '%-.64s' fra regexp"
nla "Fout '%-.64s' ontvangen van regexp"
- eng "Got error '%-.64s' from regexp"
- est "regexp tagastas vea '%-.64s'"
+ eng "Regex error '%s'"
+ est "regexp tagastas vea: %s"
fre "Erreur '%-.64s' provenant de regexp"
- ger "regexp lieferte Fehler '%-.64s'"
+ ger "Regexp Fehler %s"
hindi "regexp में '%-.64s' त्रुटि हुई"
hun "'%-.64s' hiba a regularis kifejezes hasznalata soran (regexp)"
ita "Errore '%-.64s' da regexp"
@@ -3203,11 +3203,11 @@ ER_REGEXP_ERROR 42000
kor "regexp에서 '%-.64s'가 났습니다."
por "Obteve erro '%-.64s' em regexp"
rum "Eroarea '%-.64s' obtinuta din expresia regulara (regexp)"
- rus "Получена ошибка '%-.64s' от регулярного выражения"
- serbian "Funkcija regexp je vratila grešku '%-.64s'"
+ rus "Ошибка регулярного выражения: %s"
+ serbian "Funkcija regexp je vratila grešku: %s"
spa "Obtenido error '%-.64s' de regexp"
swe "Fick fel '%-.64s' från REGEXP"
- ukr "Отримано помилку '%-.64s' від регулярного виразу"
+ ukr "Помилка регулярного виразу: %s"
ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000
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"
@@ -3896,7 +3896,7 @@ ER_NO_RAID_COMPILED
ger "Diese MariaDB-Version ist nicht mit RAID-Unterstützung kompiliert"
hindi "MariaDB का यह संस्करण RAID सपोर्ट के साथ कॉम्पाईल्ड नहीं है"
hun "Ezen leforditott MariaDB verzio nem tartalmaz RAID support-ot"
- ita "Questa versione di MYSQL non e` compilata con il supporto RAID"
+ ita "Questa versione di MariaDB non e` compilata con il supporto RAID"
jpn "このバージョンのMariaDBは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"
@@ -3922,7 +3922,7 @@ ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
spa "Tu estás usando modo de actualización segura y tentado actualizar una tabla sin un WHERE que usa una KEY columna"
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
+ER_KEY_DOES_NOT_EXISTS 42000 S1009
cze "Klíč '%-.192s' v tabulce '%-.192s' neexistuje"
dan "Nøglen '%-.192s' eksisterer ikke i tabellen '%-.192s'"
nla "Zoeksleutel '%-.192s' bestaat niet in tabel '%-.192s'"
@@ -4257,18 +4257,18 @@ ER_WARNING_NOT_COMPLETE_ROLLBACK
ukr "Застереження: Деякі нетранзакційні зміни таблиць не можна буде повернути"
ER_TRANS_CACHE_FULL
dan "Fler-udtryks transaktion krævede mere plads en 'max_binlog_cache_size' bytes. Forhøj værdien af denne variabel og prøv igen"
- nla "Multi-statement transactie vereist meer dan 'max_binlog_cache_size' bytes opslag. Verhoog deze mysqld variabele en probeer opnieuw"
- eng "Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage; increase this mysqld variable and try again"
+ nla "Multi-statement transactie vereist meer dan 'max_binlog_cache_size' bytes opslag. Verhoog deze mariadbd variabele en probeer opnieuw"
+ eng "Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage"
est "Mitme lausendiga transaktsioon nõudis rohkem ruumi kui lubatud 'max_binlog_cache_size' muutujaga. Suurenda muutuja väärtust ja proovi uuesti"
- fre "Cette transaction à commandes multiples nécessite plus de 'max_binlog_cache_size' octets de stockage, augmentez cette variable de mysqld et réessayez"
+ fre "Cette transaction à commandes multiples nécessite plus de 'max_binlog_cache_size' octets de stockage, augmentez cette variable de mariadbd 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"
+ ita "La transazione a comandi multipli (multi-statement) ha richiesto piu` di 'max_binlog_cache_size' bytes di disco: aumentare questa variabile di mariadbd 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 та спробуйте знову"
+ 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 mariadbd e tente novamente"
+ rus "Транзакции, включающей большое количество команд, потребовалось более чем 'max_binlog_cache_size' байт. Увеличьте эту переменную сервера mariadbd и попробуйте еще раз"
+ spa "Multipla transición necesita mas que 'max_binlog_cache_size' bytes de almacenamiento. Aumente esta variable mariadbd y tente de nuevo"
+ swe "Transaktionen krävde mera än 'max_binlog_cache_size' minne. Öka denna mariadbd-variabel och försök på nytt"
+ ukr "Транзакція з багатьма виразами вимагає більше ніж 'max_binlog_cache_size' байтів для зберігання. Збільште цю змінну mariadbd та спробуйте знову"
ER_SLAVE_MUST_STOP
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'"
@@ -4948,7 +4948,7 @@ ER_ZLIB_Z_DATA_ERROR
por "ZLIB: Dados de entrada está corrupto"
spa "ZLIB: Dato de entrada fué corrompido para zlib"
ER_CUT_VALUE_GROUP_CONCAT
- eng "Row %u was cut by GROUP_CONCAT()"
+ eng "Row %u was cut by %s)"
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"
@@ -5052,11 +5052,11 @@ ER_BAD_SLAVE_UNTIL_COND
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"
+ 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 mariadbd 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"
+ jpn "START SLAVE UNTIL で段階的にレプリケーションを行う際には、--skip-slave-start オプションを使うことを推奨します。使わない場合、スレーブのmariadbdが不慮の再起動をすると問題が発生します。"
+ 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 mariadbd 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 mariadbd 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"
@@ -5659,9 +5659,9 @@ ER_TRG_IN_WRONG_SCHEMA
eng "Trigger in wrong schema"
ger "Trigger im falschen 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=#' の指定をしてください。"
+ eng "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Consider increasing the thread_stack system variable."
+ ger "Thread-Stack-Überlauf: %ld Bytes eines %ld-Byte-Stacks in Verwendung, und %ld Bytes benötigt. Verwenden Sie 'mariadbd --thread_stack=#', um einen größeren Stack anzugeben"
+ jpn "スレッドスタック不足です(使用: %ld ; サイズ: %ld ; 要求: %ld)。より大きい値で 'mariadbd --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"
@@ -5898,10 +5898,8 @@ ER_PARTITION_MGMT_ON_NONPARTITIONED
eng "Partition management on a not partitioned table is not possible"
ger "Partitionsverwaltung einer nicht partitionierten Tabelle ist nicht möglich"
swe "Partitioneringskommando på en opartitionerad tabell är inte möjligt"
-ER_FOREIGN_KEY_ON_PARTITIONED
- eng "Foreign key clause is not yet supported in conjunction with partitioning"
- ger "Fremdschlüssel-Beschränkungen sind im Zusammenhang mit Partitionierung nicht zulässig"
- swe "Foreign key klausul är inte ännu implementerad i kombination med partitionering"
+ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING
+ eng "Partitioned tables do not support %s"
ER_DROP_PARTITION_NON_EXISTENT
eng "Error in list of partitions to %-.64s"
ger "Fehler in der Partitionsliste bei %-.64s"
@@ -6079,8 +6077,8 @@ ER_CANT_LOCK_LOG_TABLE
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"
+ eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MariaDB %d, now running %d. Please use mariadb-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 mariadb-upgrade, um den Fehler zu beheben"
ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR
eng "Cannot switch out of the row-based binary log format when the session has open temporary tables"
ger "Kann nicht aus dem zeilenbasierten Binärlog-Format herauswechseln, wenn die Sitzung offene temporäre Tabellen hat"
@@ -6574,13 +6572,13 @@ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX
eng "The requested value for the heartbeat period exceeds the value of `slave_net_timeout' seconds. A sensible value for the period should be less than the timeout"
ER_STMT_CACHE_FULL
- eng "Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again"
+ eng "Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage."
ER_MULTI_UPDATE_KEY_CONFLICT
eng "Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'"
# When translating this error message make sure to include "ALTER TABLE" in the
-# message as mysqlcheck parses the error message looking for ALTER TABLE.
+# message as mariadb-check parses the error message looking for ALTER TABLE.
ER_TABLE_NEEDS_REBUILD
eng "Table rebuild required. Please do \"ALTER TABLE %`s FORCE\" or dump/reload to fix it!"
@@ -7388,8 +7386,8 @@ ER_FK_DEPTH_EXCEEDED
eng "Foreign key cascade delete/update exceeds max depth of %d."
ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2
- eng "Column count of %s.%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 %s.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MariaDB %d, jetzt unter %d. Bitte benutzen Sie mysql_upgrade, um den Fehler zu beheben"
+ eng "Column count of %s.%s is wrong. Expected %d, found %d. Created with MariaDB %d, now running %d. Please use mariadb-upgrade to fix this error."
+ ger "Spaltenanzahl von %s.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MariaDB %d, jetzt unter %d. Bitte benutzen Sie mariadb-upgrade, um den Fehler zu beheben"
ER_WARN_TRIGGER_DOESNT_HAVE_CREATED
eng "Trigger %s.%s.%s does not have CREATED attribute."
@@ -7775,7 +7773,7 @@ ER_UNKNOWN_SEQUENCES 42S02
ER_UNKNOWN_VIEW 42S02
eng "Unknown VIEW: '%-.300s'"
ER_WRONG_INSERT_INTO_SEQUENCE
- eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a sequence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead."
+ eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a sequence object (like with mariadb-dump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead."
ER_SP_STACK_TRACE
eng "At line %u in %s"
ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY
@@ -7826,7 +7824,7 @@ ER_PARTITION_WRONG_TYPE
eng "Wrong partitioning type, expected type: %`s"
WARN_VERS_PART_FULL
- eng "Versioned table %`s.%`s: partition %`s is full, add more HISTORY partitions"
+ eng "Versioned table %`s.%`s: last HISTORY partition (%`s) is out of %s, need more HISTORY partitions"
WARN_VERS_PARAMETERS
eng "Maybe missing parameters: %s"
@@ -7942,3 +7940,37 @@ ER_PERIOD_CONSTRAINT_DROP
eng "Can't DROP CONSTRAINT `%s`. Use DROP PERIOD `%s` for this"
ER_TOO_LONG_KEYPART 42000 S1009
eng "Specified key part was too long; max key part length is %u bytes"
+ER_TOO_LONG_DATABASE_COMMENT
+ eng "Comment for database '%-.64s' is too long (max = %u)"
+ER_UNKNOWN_DATA_TYPE
+ eng "Unknown data type: '%-.64s'"
+ER_UNKNOWN_OPERATOR
+ eng "Operator does not exists: '%-.128s'"
+ER_WARN_HISTORY_ROW_START_TIME
+ eng "Table `%s.%s` history row start '%s' is later than row end '%s'"
+ER_PART_STARTS_BEYOND_INTERVAL
+ eng "%`s: STARTS is later than query time, first history partition may exceed INTERVAL value"
+ER_GALERA_REPLICATION_NOT_SUPPORTED
+ eng "DDL-statement is forbidden as table storage engine does not support Galera replication"
+ER_LOAD_INFILE_CAPABILITY_DISABLED
+ eng "The used command is not allowed because the MariaDB server or client has disabled the local infile capability"
+ rum "Comanda folosită nu este permisă deoarece clientul sau serverul MariaDB a dezactivat această capabilitate"
+ER_NO_SECURE_TRANSPORTS_CONFIGURED
+ eng "No secure transports are configured, unable to set --require_secure_transport=ON"
+ER_SLAVE_IGNORED_SHARED_TABLE
+ eng "Slave SQL thread ignored the '%s' because table is shared"
+ ger "Slave-SQL-Thread hat die Abfrage '%s' ignoriert"
+ nla "Slave SQL thread negeerde de query '%s'"
+ por "Slave SQL thread ignorado a consulta devido '%s'"
+ spa "Slave SQL thread ignorado el query '%s'"
+ swe "Slav SQL tråden ignorerade '%s' pga tabellen är delad"
+ER_NO_AUTOINCREMENT_WITH_UNIQUE
+ eng "AUTO_INCREMENT column %`s cannot be used in the UNIQUE index %`s"
+ER_KEY_CONTAINS_PERIOD_FIELDS
+ eng "Key %`s cannot explicitly include column %`s"
+ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS
+ eng "Key %`s cannot have WITHOUT OVERLAPS"
+ER_NOT_ALLOWED_IN_THIS_CONTEXT
+ eng "'%-.128s' is not allowed in this context"
+ER_DATA_WAS_COMMITED_UNDER_ROLLBACK
+ eng "Engine %s does not support rollback. Changes were committed during rollback call"
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index 5439f13b3f4..5e3f32eae4e 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -42,9 +42,6 @@
static volatile sig_atomic_t segfaulted= 0;
extern ulong max_used_connections;
extern volatile sig_atomic_t calling_initgroups;
-#ifdef HAVE_NPTL
-extern volatile sig_atomic_t ld_assume_kernel_is_set;
-#endif
extern const char *optimizer_switch_names[];
@@ -55,12 +52,17 @@ static inline void output_core_info()
char buff[PATH_MAX];
ssize_t len;
int fd;
- if ((len= readlink("/proc/self/cwd", buff, sizeof(buff))) >= 0)
+ if ((len= readlink("/proc/self/cwd", buff, sizeof(buff)-1)) >= 0)
{
+ buff[len]= 0;
my_safe_printf_stderr("Writing a core file...\nWorking directory at %.*s\n",
(int) len, buff);
}
+#ifdef __FreeBSD__
+ if ((fd= my_open("/proc/curproc/rlimit", O_RDONLY, MYF(0))) >= 0)
+#else
if ((fd= my_open("/proc/self/limits", O_RDONLY, MYF(0))) >= 0)
+#endif
{
my_safe_printf_stderr("Resource Limits:\n");
while ((len= my_read(fd, (uchar*)buff, sizeof(buff), MYF(0))) > 0)
@@ -296,21 +298,6 @@ extern "C" sig_handler handle_fatal_signal(int sig)
}
#endif
-#ifdef HAVE_NPTL
- if (thd_lib_detected == THD_LIB_LT && !ld_assume_kernel_is_set)
- {
- my_safe_printf_stderr("%s",
- "You are running a statically-linked LinuxThreads binary on an NPTL\n"
- "system. This can result in crashes on some distributions due to "
- "LT/NPTL conflicts.\n"
- "You should either build a dynamically-linked binary, "
- "or force LinuxThreads\n"
- "to be used with the LD_ASSUME_KERNEL environment variable.\n"
- "Please consult the documentation for your distribution "
- "on how to do that.\n");
- }
-#endif
-
if (locked_in_memory)
{
my_safe_printf_stderr("%s", "\n"
diff --git a/sql/slave.cc b/sql/slave.cc
index f2e38c02ab5..c302ba1de64 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -57,6 +57,9 @@
#include "wsrep_trans_observer.h"
#endif
+class Master_info_index;
+Master_info_index *master_info_index;
+
#ifdef HAVE_REPLICATION
#include "rpl_tblmap.h"
@@ -81,7 +84,6 @@ char slave_transaction_retry_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;
ulonglong opt_read_binlog_speed_limit = 0;
@@ -488,6 +490,7 @@ handle_slave_background(void *arg __attribute__((unused)))
#ifdef WITH_WSREP
thd->variables.wsrep_on= 0;
#endif
+ thd->set_psi(PSI_CALL_get_thread());
thd_proc_info(thd, "Loading slave GTID position from table");
if (rpl_load_gtid_slave_state(thd))
@@ -583,7 +586,7 @@ slave_background_kill_request(THD *to_kill)
if (to_kill->rgi_slave->killed_for_retry)
return; // Already deadlock killed.
slave_background_kill_t *p=
- (slave_background_kill_t *)my_malloc(sizeof(*p), MYF(MY_WME));
+ (slave_background_kill_t *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*p), MYF(MY_WME));
if (p)
{
p->to_kill= to_kill;
@@ -611,7 +614,7 @@ slave_background_gtid_pos_create_request(
if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE)
return;
- p= (slave_background_gtid_pos_create_t *)my_malloc(sizeof(*p), MYF(MY_WME));
+ p= (slave_background_gtid_pos_create_t *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*p), MYF(MY_WME));
if (!p)
return;
mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
@@ -934,7 +937,7 @@ bool init_slave_skip_errors(const char* arg)
use_slave_mask= 1;
for (;my_isspace(system_charset_info,*arg);++arg)
/* empty */;
- if (!my_strnncoll(system_charset_info,(uchar*)arg,4,(const uchar*)"all",4))
+ if (!system_charset_info->strnncoll((uchar*)arg,4,(const uchar*)"all",4))
{
bitmap_set_all(&slave_error_mask);
goto end;
@@ -1532,7 +1535,7 @@ static bool sql_slave_killed(rpl_group_info *rgi)
rli->is_in_group().
*/
- if ((thd->transaction.all.modified_non_trans_table ||
+ if ((thd->transaction->all.modified_non_trans_table ||
(thd->variables.option_bits & OPTION_KEEP_LOG)) &&
rli->is_in_group())
{
@@ -1546,7 +1549,7 @@ static bool sql_slave_killed(rpl_group_info *rgi)
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,
+ 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()));
@@ -1652,6 +1655,40 @@ const char *print_slave_db_safe(const char* db)
#endif /* HAVE_REPLICATION */
+bool Sql_cmd_show_slave_status::execute(THD *thd)
+{
+#ifndef HAVE_REPLICATION
+ my_ok(thd);
+ return false;
+#else
+ DBUG_ENTER("Sql_cmd_show_slave_status::execute");
+ bool res= true;
+
+ /* Accept one of two privileges */
+ if (check_global_access(thd, PRIV_STMT_SHOW_SLAVE_STATUS))
+ goto error;
+ if (is_show_all_slaves_stat())
+ {
+ mysql_mutex_lock(&LOCK_active_mi);
+ res= show_all_master_info(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ }
+ else
+ {
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ res= show_master_info(thd, mi, 0);
+ mi->release();
+ }
+ }
+error:
+ DBUG_RETURN(res);
+#endif
+}
+
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
const char *default_val)
{
@@ -1775,7 +1812,8 @@ int init_dynarray_intvar_from_file(DYNAMIC_ARRAY* arr, IO_CACHE* f)
(decimal size + space) - 1 + `\n' + '\0'
*/
size_t max_size= (1 + num_items) * (sizeof(long)*3 + 1) + 1;
- buf_act= (char*) my_malloc(max_size, MYF(MY_WME));
+ buf_act= (char*) my_malloc(key_memory_Rpl_info_file_buffer, max_size,
+ MYF(MY_WME));
memcpy(buf_act, buf, read_size);
snd_size= my_b_gets(f, buf_act + read_size, max_size - read_size);
if (snd_size == 0 ||
@@ -3440,7 +3478,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
protocol->store((ulonglong) mi->rli.max_relay_log_size);
protocol->store(mi->rli.executed_entries);
protocol->store((uint32) mi->received_heartbeats);
- protocol->store((double) mi->heartbeat_period, 3, &tmp);
+ protocol->store_double(mi->heartbeat_period, 3);
protocol->store(gtid_pos->ptr(), gtid_pos->length(), &my_charset_bin);
}
@@ -3544,20 +3582,19 @@ void set_slave_thread_options(THD* thd)
when max_join_size is 4G, OPTION_BIG_SELECTS is automatically set, but
only for client threads.
*/
- ulonglong options= thd->variables.option_bits | OPTION_BIG_SELECTS;
- if (opt_log_slave_updates)
- options|= OPTION_BIN_LOG;
- else
+ ulonglong options= (thd->variables.option_bits |
+ OPTION_BIG_SELECTS | OPTION_BIN_LOG);
+ if (!opt_log_slave_updates)
options&= ~OPTION_BIN_LOG;
- thd->variables.option_bits= options;
- thd->variables.completion_type= 0;
-
/* For easier test in LOGGER::log_command */
if (thd->variables.log_disabled_statements & LOG_DISABLE_SLAVE)
- thd->variables.option_bits|= OPTION_LOG_OFF;
+ options|= OPTION_LOG_OFF;
+ thd->variables.option_bits= options;
- thd->variables.sql_log_slow= !MY_TEST(thd->variables.log_slow_disabled_statements &
- LOG_SLOW_DISABLE_SLAVE);
+ thd->variables.completion_type= 0;
+ thd->variables.sql_log_slow=
+ !MY_TEST(thd->variables.log_slow_disabled_statements &
+ LOG_SLOW_DISABLE_SLAVE);
DBUG_VOID_RETURN;
}
@@ -3591,9 +3628,16 @@ static int init_slave_thread(THD* thd, Master_info *mi,
thd->system_thread = (thd_type == SLAVE_THD_SQL) ?
SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
+ if (init_thr_lock())
+ {
+ thd->cleanup();
+ DBUG_RETURN(-1);
+ }
+
/* We must call store_globals() before doing my_net_init() */
- if (init_thr_lock() || thd->store_globals() ||
- my_net_init(&thd->net, 0, thd, MYF(MY_THREAD_SPECIFIC)) ||
+ thd->store_globals();
+
+ if (my_net_init(&thd->net, 0, thd, MYF(MY_THREAD_SPECIFIC)) ||
IF_DBUG(simulate_error & (1<< thd_type), 0))
{
thd->cleanup();
@@ -4205,7 +4249,7 @@ inline void update_state_of_relay_log(Relay_log_info *rli, Log_event *ev)
rli->clear_flag(Relay_log_info::IN_TRANSACTION);
}
}
- if (typ == XID_EVENT)
+ if (typ == XID_EVENT || typ == XA_PREPARE_LOG_EVENT)
rli->clear_flag(Relay_log_info::IN_TRANSACTION);
if (typ == GTID_EVENT &&
!(((Gtid_log_event*) ev)->flags2 & Gtid_log_event::FL_STANDALONE))
@@ -4339,7 +4383,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
(LOG_EVENT_IS_QUERY(typ) &&
strcmp("COMMIT", ((Query_log_event *) ev)->query) == 0))
{
- DBUG_ASSERT(thd->transaction.all.modified_non_trans_table);
+ DBUG_ASSERT(thd->transaction->all.modified_non_trans_table);
rli->abort_slave= 1;
mysql_mutex_unlock(&rli->data_lock);
delete ev;
@@ -4698,6 +4742,8 @@ pthread_handler_t handle_slave_io(void *arg)
THD_CHECK_SENTRY(thd);
mi->io_thd = thd;
+ thd->set_psi(PSI_CALL_get_thread());
+
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd; // remember where our stack is
mi->clear_error();
@@ -5337,6 +5383,8 @@ pthread_handler_t handle_slave_sql(void *arg)
executing SQL queries too.
*/
serial_rgi->thd= rli->sql_driver_thd= thd;
+
+ thd->set_psi(PSI_CALL_get_thread());
/* Inform waiting threads that slave has started */
rli->slave_run_id++;
@@ -6030,7 +6078,8 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
*/
if ((uchar)buf[EVENT_TYPE_OFFSET] == LOAD_EVENT)
{
- if (unlikely(!(tmp_buf=(char*)my_malloc(event_len+1,MYF(MY_WME)))))
+ if (unlikely(!(tmp_buf=(char*)my_malloc(key_memory_binlog_ver_1_event,
+ event_len+1,MYF(MY_WME)))))
{
mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR), "Memory allocation failed");
@@ -7090,6 +7139,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
buf[EVENT_TYPE_OFFSET])) ||
(!mi->last_queued_gtid_standalone &&
((uchar)buf[EVENT_TYPE_OFFSET] == XID_EVENT ||
+ (uchar)buf[EVENT_TYPE_OFFSET] == XA_PREPARE_LOG_EVENT ||
((uchar)buf[EVENT_TYPE_OFFSET] == QUERY_EVENT && /* QUERY_COMPRESSED_EVENT would never be commmit or rollback */
Query_log_event::peek_is_commit_rollback(buf, event_len,
checksum_alg))))))
@@ -8174,7 +8224,7 @@ void Rows_event_tracker::reset()
/*
- Update log event tracking data.
+ Update log event tracking data.
The first- and last- seen event binlog position get memorized, as
well as the end-of-statement status of the last one.
@@ -8184,6 +8234,7 @@ void Rows_event_tracker::update(const char* file_name, my_off_t pos,
const char* buf,
const Format_description_log_event *fdle)
{
+ DBUG_ENTER("Rows_event_tracker::update");
if (!first_seen)
{
first_seen= pos;
@@ -8192,6 +8243,7 @@ void Rows_event_tracker::update(const char* file_name, my_off_t pos,
last_seen= pos;
DBUG_ASSERT(stmt_end_seen == 0); // We can only have one
stmt_end_seen= get_row_event_stmt_end(buf, fdle);
+ DBUG_VOID_RETURN;
};
diff --git a/sql/sp.cc b/sql/sp.cc
index 081edcd725e..3737bd11740 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -28,12 +28,13 @@
// mysql_change_db, check_db_dir_existence,
// load_db_opt_by_name
#include "sql_table.h" // write_bin_log
-#include "sql_acl.h" // SUPER_ACL
#include "sp_head.h"
#include "sp_cache.h"
+#include "transaction.h"
#include "lock.h" // lock_object_name
#include <my_user.h>
+#include "mysql/psi/mysql_sp.h"
sp_cache **Sp_handler_procedure::get_cache(THD *thd) const
{
@@ -470,27 +471,31 @@ static Proc_table_intact proc_table_intact;
currently open tables will be saved, and from which will be
restored when we will end work with mysql.proc.
+ NOTES
+ On must have a start_new_trans object active when calling this function
+
@retval
0 Error
@retval
\# Pointer to TABLE object of mysql.proc
*/
-TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup)
+TABLE *open_proc_table_for_read(THD *thd)
{
TABLE_LIST table;
-
DBUG_ENTER("open_proc_table_for_read");
+ DBUG_ASSERT(thd->internal_transaction());
+
table.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_READ);
- if (open_system_tables_for_read(thd, &table, backup))
+ if (open_system_tables_for_read(thd, &table))
DBUG_RETURN(NULL);
if (!proc_table_intact.check(table.table, &proc_table_def))
DBUG_RETURN(table.table);
- close_system_tables(thd, backup);
+ thd->commit_whole_transaction_and_close_tables();
DBUG_RETURN(NULL);
}
@@ -504,6 +509,10 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup)
@note
Table opened with this call should closed using close_thread_tables().
+ We don't need to use the start_new_transaction object when calling this
+ as there can't be any active transactions when we create or alter
+ stored procedures
+
@retval
0 Error
@retval
@@ -517,7 +526,10 @@ static TABLE *open_proc_table_for_update(THD *thd)
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_proc_table_for_update");
- table_list.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL, TL_WRITE);
+ DBUG_ASSERT(!thd->internal_transaction());
+
+ table_list.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROC_NAME, NULL,
+ TL_WRITE);
if (!(table= open_system_table_for_update(thd, &table_list)))
DBUG_RETURN(NULL);
@@ -525,7 +537,7 @@ static TABLE *open_proc_table_for_update(THD *thd)
if (!proc_table_intact.check(table, &proc_table_def))
DBUG_RETURN(table);
- close_thread_tables(thd);
+ thd->commit_whole_transaction_and_close_tables();
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
DBUG_RETURN(NULL);
@@ -683,22 +695,25 @@ Sp_handler::db_find_routine(THD *thd,
longlong modified;
Sp_chistics chistics;
bool saved_time_zone_used= thd->time_zone_used;
- sql_mode_t sql_mode, saved_mode= thd->variables.sql_mode;
- Open_tables_backup open_tables_state_backup;
+ bool trans_commited= 0;
+ sql_mode_t sql_mode;
Stored_program_creation_ctx *creation_ctx;
AUTHID definer;
-
DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %s name: %.*s",
type_str(),
(int) name->m_name.length, name->m_name.str));
*sphp= 0; // In case of errors
- if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
- DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- /* Reset sql_mode during data dictionary operations. */
- thd->variables.sql_mode= 0;
+ start_new_trans new_trans(thd);
+ Sql_mode_instant_set sms(thd, 0);
+
+ if (!(table= open_proc_table_for_read(thd)))
+ {
+ ret= SP_OPEN_TABLE_FAILED;
+ goto done;
+ }
if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK)
goto done;
@@ -718,7 +733,7 @@ Sp_handler::db_find_routine(THD *thd,
table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
&params);
- if (type() != TYPE_ENUM_FUNCTION)
+ if (type() != SP_TYPE_FUNCTION)
returns= empty_clex_str;
else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root,
&returns))
@@ -741,8 +756,9 @@ Sp_handler::db_find_routine(THD *thd,
creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table);
- close_system_tables(thd, &open_tables_state_backup);
- table= 0;
+ trans_commited= 1;
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
ret= db_load_routine(thd, name, sphp,
sql_mode, params, returns, body, chistics, definer,
@@ -753,9 +769,12 @@ Sp_handler::db_find_routine(THD *thd,
does not affect replication.
*/
thd->time_zone_used= saved_time_zone_used;
- if (table)
- close_system_tables(thd, &open_tables_state_backup);
- thd->variables.sql_mode= saved_mode;
+ if (!trans_commited)
+ {
+ if (table)
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
+ }
DBUG_RETURN(ret);
}
@@ -867,6 +886,8 @@ static sp_head *sp_compile(THD *thd, String *defstr, sql_mode_t sql_mode,
thd->spcont= old_spcont;
thd->variables.sql_mode= old_sql_mode;
thd->variables.select_limit= old_select_limit;
+ if (sp != NULL)
+ sp->init_psi_share();
return sp;
}
@@ -1002,7 +1023,7 @@ Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name,
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
- if (type() == TYPE_ENUM_PACKAGE_BODY)
+ if (type() == SP_TYPE_PACKAGE_BODY)
{
sp_package *package= (*sphp)->get_package();
List_iterator<LEX> it(package->m_routine_implementations);
@@ -1105,6 +1126,9 @@ Sp_handler::sp_drop_routine_internal(THD *thd,
DBUG_ASSERT(spc);
if ((sp= sp_cache_lookup(spc, name)))
sp_cache_flush_obsolete(spc, &sp);
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(type(), name->m_db.str, static_cast<uint>(name->m_db.length),
+ name->m_name.str, static_cast<uint>(name->m_name.length));
DBUG_RETURN(SP_OK);
}
@@ -1232,19 +1256,19 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
if (lex->create_info.or_replace())
{
switch (type()) {
- case TYPE_ENUM_PACKAGE:
+ case SP_TYPE_PACKAGE:
// Drop together with its PACKAGE BODY mysql.proc record
if (sp_handler_package_spec.sp_find_and_drop_routine(thd, table, sp))
goto done;
break;
- case TYPE_ENUM_PACKAGE_BODY:
- case TYPE_ENUM_FUNCTION:
- case TYPE_ENUM_PROCEDURE:
+ case SP_TYPE_PACKAGE_BODY:
+ case SP_TYPE_FUNCTION:
+ case SP_TYPE_PROCEDURE:
if (sp_drop_routine_internal(thd, sp, table))
goto done;
break;
- case TYPE_ENUM_TRIGGER:
- case TYPE_ENUM_PROXY:
+ case SP_TYPE_TRIGGER:
+ case SP_TYPE_EVENT:
DBUG_ASSERT(0);
ret= SP_OK;
}
@@ -1259,10 +1283,10 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
ret= FALSE;
// Setting retstr as it is used for logging.
- if (type() == TYPE_ENUM_FUNCTION)
+ if (type() == SP_TYPE_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
- returns= retstr.lex_cstring();
+ retstr.get_value(&returns);
}
goto log;
}
@@ -1284,9 +1308,8 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
goto done;
}
- if (system_charset_info->cset->numchars(system_charset_info,
- sp->m_name.str,
- sp->m_name.str+sp->m_name.length) >
+ if (system_charset_info->numchars(sp->m_name.str,
+ sp->m_name.str + sp->m_name.length) >
table->field[MYSQL_PROC_FIELD_NAME]->char_length())
{
my_error(ER_TOO_LONG_IDENT, MYF(0), sp->m_name.str);
@@ -1343,10 +1366,10 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
store(sp->m_params, system_charset_info);
- if (type() == TYPE_ENUM_FUNCTION)
+ if (type() == SP_TYPE_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
- returns= retstr.lex_cstring();
+ retstr.get_value(&returns);
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_RETURNS]->
@@ -1361,8 +1384,8 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
table->field[MYSQL_PROC_FIELD_DEFINER]->
store(definer, system_charset_info);
- ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
- ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
+ table->field[MYSQL_PROC_FIELD_CREATED]->set_time();
+ table->field[MYSQL_PROC_FIELD_MODIFIED]->set_time();
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_SQL_MODE]->
@@ -1375,7 +1398,7 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
store(sp->comment(), system_charset_info);
}
- if (type() == TYPE_ENUM_FUNCTION &&
+ if (type() == SP_TYPE_FUNCTION &&
!trust_function_creators && mysql_bin_log.is_open())
{
if (!sp->detistic())
@@ -1394,7 +1417,7 @@ Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const
goto done;
}
}
- if (!(thd->security_ctx->master_access & SUPER_ACL))
+ if (!(thd->security_ctx->master_access & PRIV_LOG_BIN_TRUSTED_SP_CREATOR))
{
my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER,MYF(0));
goto done;
@@ -1524,8 +1547,7 @@ Sp_handler_package::show_create_sp(THD *thd, String *buf,
const DDL_options_st ddl_options,
sql_mode_t sql_mode) const
{
- sql_mode_t old_sql_mode= thd->variables.sql_mode;
- thd->variables.sql_mode= sql_mode;
+ Sql_mode_instant_set sms(thd, sql_mode);
bool rc=
buf->append(STRING_WITH_LEN("CREATE ")) ||
(ddl_options.or_replace() &&
@@ -1542,7 +1564,6 @@ Sp_handler_package::show_create_sp(THD *thd, String *buf,
append_package_chistics(buf, chistics) ||
buf->append(" ", 1) ||
buf->append(body.str, body.length);
- thd->variables.sql_mode= old_sql_mode;
return rc;
}
@@ -1629,7 +1650,7 @@ Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name,
if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK)
{
- if (type() == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
+ if (type() == SP_TYPE_FUNCTION && ! trust_function_creators &&
mysql_bin_log.is_open() &&
(chistics->daccess == SP_CONTAINS_SQL ||
chistics->daccess == SP_MODIFIES_SQL_DATA))
@@ -1654,7 +1675,7 @@ Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name,
}
store_record(table,record[1]);
- ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
+ table->field[MYSQL_PROC_FIELD_MODIFIED]->set_time();
if (chistics->suid != SP_IS_DEFAULT_SUID)
table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
store((longlong)chistics->suid, TRUE);
@@ -1726,7 +1747,6 @@ bool lock_db_routines(THD *thd, const char *db)
{
TABLE *table;
uint key_len;
- Open_tables_backup open_tables_state_backup;
MDL_request_list mdl_requests;
Lock_db_routines_error_handler err_handler;
uchar keybuf[MAX_KEY_LENGTH];
@@ -1734,13 +1754,15 @@ bool lock_db_routines(THD *thd, const char *db)
DBUG_SLOW_ASSERT(ok_for_lower_case_names(db));
+ start_new_trans new_trans(thd);
+
/*
mysql.proc will be re-opened during deletion, so we can ignore
errors when opening the table here. The error handler is
used to avoid getting the same warning twice.
*/
thd->push_internal_handler(&err_handler);
- table= open_proc_table_for_read(thd, &open_tables_state_backup);
+ table= open_proc_table_for_read(thd);
thd->pop_internal_handler();
if (!table)
{
@@ -1749,6 +1771,7 @@ bool lock_db_routines(THD *thd, const char *db)
or is outdated. We therefore only abort mysql_rm_db() if we
have errors not handled by the error handler.
*/
+ new_trans.restore_old_transaction();
DBUG_RETURN(thd->is_error() || thd->killed);
}
@@ -1759,11 +1782,10 @@ bool lock_db_routines(THD *thd, const char *db)
if (nxtres)
{
table->file->print_error(nxtres, MYF(0));
- close_system_tables(thd, &open_tables_state_backup);
- DBUG_RETURN(true);
+ goto error;
}
- if (! table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
+ if (!table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
HA_READ_KEY_EXACT))
{
do
@@ -1775,11 +1797,11 @@ bool lock_db_routines(THD *thd, const char *db)
longlong sp_type= table->field[MYSQL_PROC_MYSQL_TYPE]->val_int();
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
- const Sp_handler *sph= Sp_handler::handler((stored_procedure_type)
+ const Sp_handler *sph= Sp_handler::handler((enum_sp_type)
sp_type);
if (!sph)
sph= &sp_handler_procedure;
- mdl_request->init(sph->get_mdl_type(), db, sp_name,
+ MDL_REQUEST_INIT(mdl_request, sph->get_mdl_type(), db, sp_name,
MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(mdl_request);
} while (! (nxtres= table->file->ha_index_next_same(table->record[0], keybuf, key_len)));
@@ -1788,10 +1810,10 @@ bool lock_db_routines(THD *thd, const char *db)
if (nxtres != 0 && nxtres != HA_ERR_END_OF_FILE)
{
table->file->print_error(nxtres, MYF(0));
- close_system_tables(thd, &open_tables_state_backup);
- DBUG_RETURN(true);
+ goto error;
}
- close_system_tables(thd, &open_tables_state_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
/* We should already hold a global IX lock and a schema X lock. */
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::BACKUP, "", "",
@@ -1800,6 +1822,10 @@ bool lock_db_routines(THD *thd, const char *db)
MDL_EXCLUSIVE));
DBUG_RETURN(thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout));
+error:
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
+ DBUG_RETURN(true);
}
@@ -1818,6 +1844,8 @@ sp_drop_db_routines(THD *thd, const char *db)
uint key_len;
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
uchar keybuf[MAX_KEY_LENGTH];
+ size_t db_length= strlen(db);
+ Sql_mode_instant_remove smir(thd, MODE_PAD_CHAR_TO_FULL_LENGTH); // see below
DBUG_ENTER("sp_drop_db_routines");
DBUG_PRINT("enter", ("db: %s", db));
@@ -1825,7 +1853,7 @@ sp_drop_db_routines(THD *thd, const char *db)
if (!(table= open_proc_table_for_update(thd)))
goto err;
- table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info);
+ table->field[MYSQL_PROC_FIELD_DB]->store(db, db_length, system_charset_info);
key_len= table->key_info->key_part[0].store_length;
table->field[MYSQL_PROC_FIELD_DB]->get_key_image(keybuf, key_len, Field::itRAW);
@@ -1844,7 +1872,18 @@ sp_drop_db_routines(THD *thd, const char *db)
do
{
if (! table->file->ha_delete_row(table->record[0]))
+ {
deleted= TRUE; /* We deleted something */
+#ifdef HAVE_PSI_SP_INTERFACE
+ String buf;
+ // the following assumes MODE_PAD_CHAR_TO_FULL_LENGTH being *unset*
+ String *name= table->field[MYSQL_PROC_FIELD_NAME]->val_str(&buf);
+
+ enum_sp_type sp_type= (enum_sp_type) table->field[MYSQL_PROC_MYSQL_TYPE]->ptr[0];
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(sp_type, db, static_cast<uint>(db_length), name->ptr(), name->length());
+#endif
+ }
else
{
ret= SP_DELETE_ROW_FAILED;
@@ -1865,6 +1904,7 @@ sp_drop_db_routines(THD *thd, const char *db)
table->file->ha_index_end();
err_idx_init:
+ trans_commit_stmt(thd);
close_thread_tables(thd);
/*
Make sure to only release the MDL lock on mysql.proc, not other
@@ -2018,10 +2058,10 @@ Sp_handler::sp_clone_and_link_routine(THD *thd,
DBUG_RETURN(0);
}
- if (type() == TYPE_ENUM_FUNCTION)
+ if (type() == SP_TYPE_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
- returns= retstr.lex_cstring();
+ retstr.get_value(&returns);
}
if (sp->m_parent)
@@ -2286,7 +2326,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
const Sp_handler *handler,
TABLE_LIST *belong_to_view)
{
- my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info,
+ my_hash_init_opt(PSI_INSTRUMENT_ME, &prelocking_ctx->sroutines, system_charset_info,
Query_tables_list::START_SROUTINES_HASH_SIZE,
0, 0, sp_sroutine_key, 0, 0);
@@ -2296,7 +2336,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry));
if (unlikely(!rn)) // OOM. Error will be reported using fatal_error().
return FALSE;
- rn->mdl_request.init(key, MDL_SHARED, MDL_TRANSACTION);
+ MDL_REQUEST_INIT_BY_KEY(&rn->mdl_request, key, MDL_SHARED, MDL_TRANSACTION);
if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn))
return FALSE;
prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next);
@@ -2366,7 +2406,7 @@ is_package_public_routine(THD *thd,
const LEX_CSTRING &db,
const LEX_CSTRING &package,
const LEX_CSTRING &routine,
- stored_procedure_type type)
+ enum_sp_type type)
{
sp_head *sp= NULL;
Database_qualified_name tmp(db, package);
@@ -2400,7 +2440,7 @@ is_package_public_routine_quick(THD *thd,
const LEX_CSTRING &db,
const LEX_CSTRING &pkgname,
const LEX_CSTRING &name,
- stored_procedure_type type)
+ enum_sp_type type)
{
Database_qualified_name tmp(db, pkgname);
sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, &tmp);
@@ -2419,7 +2459,7 @@ static bool
is_package_body_routine(THD *thd, sp_package *pkg,
const LEX_CSTRING &name1,
const LEX_CSTRING &name2,
- stored_procedure_type type)
+ enum_sp_type type)
{
return Sp_handler::eq_routine_name(pkg->m_name, name1) &&
(pkg->m_routine_declarations.find(name2, type) ||
@@ -2761,7 +2801,7 @@ int Sroutine_hash_entry::sp_cache_routine(THD *thd,
@param[out] sp Pointer to sp_head object for routine, NULL if routine was
not found.
- @retval 0 Either routine is found and was succesfully loaded into cache
+ @retval 0 Either routine is found and was successfully loaded into cache
or it does not exist.
@retval non-0 Error while loading routine from mysql,proc table.
*/
@@ -2847,7 +2887,7 @@ Sp_handler::sp_cache_package_routine(THD *thd,
bool lookup_only, sp_head **sp) const
{
DBUG_ENTER("sp_cache_package_routine");
- DBUG_ASSERT(type() == TYPE_ENUM_FUNCTION || type() == TYPE_ENUM_PROCEDURE);
+ DBUG_ASSERT(type() == SP_TYPE_FUNCTION || type() == SP_TYPE_PROCEDURE);
sp_name pkgname(&name->m_db, &pkgname_cstr, false);
sp_head *ph= NULL;
int ret= sp_handler_package_body.sp_cache_routine(thd, &pkgname,
@@ -2914,7 +2954,6 @@ Sp_handler::show_create_sp(THD *thd, String *buf,
const DDL_options_st ddl_options,
sql_mode_t sql_mode) const
{
- sql_mode_t old_sql_mode= thd->variables.sql_mode;
size_t agglen= (chistics.agg_type == GROUP_AGGREGATE)? 10 : 0;
LEX_CSTRING tmp;
@@ -2925,7 +2964,7 @@ Sp_handler::show_create_sp(THD *thd, String *buf,
agglen + USER_HOST_BUFF_SIZE))
return true;
- thd->variables.sql_mode= sql_mode;
+ Sql_mode_instant_set sms(thd, sql_mode);
buf->append(STRING_WITH_LEN("CREATE "));
if (ddl_options.or_replace())
buf->append(STRING_WITH_LEN("OR REPLACE "));
@@ -2947,7 +2986,7 @@ Sp_handler::show_create_sp(THD *thd, String *buf,
buf->append('(');
buf->append(&params);
buf->append(')');
- if (type() == TYPE_ENUM_FUNCTION)
+ if (type() == SP_TYPE_FUNCTION)
{
if (sql_mode & MODE_ORACLE)
buf->append(STRING_WITH_LEN(" RETURN "));
@@ -2976,7 +3015,6 @@ Sp_handler::show_create_sp(THD *thd, String *buf,
append_suid(buf, chistics.suid);
append_comment(buf, chistics.comment);
buf->append(body.str, body.length); // Not \0 terminated
- thd->variables.sql_mode= old_sql_mode;
return false;
}
diff --git a/sql/sp.h b/sql/sp.h
index 49bf1eb93cf..e92525e1930 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -1,5 +1,6 @@
/* -*- C++ -*- */
/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -46,18 +47,18 @@ template <typename T> class SQL_I_List;
/*
Values for the type enum. This reflects the order of the enum declaration
in the CREATE TABLE command.
+ See also storage/perfschema/my_thread.h
*/
-enum stored_procedure_type
+enum enum_sp_type
{
- TYPE_ENUM_FUNCTION=1,
- TYPE_ENUM_PROCEDURE=2,
- TYPE_ENUM_PACKAGE=3,
- TYPE_ENUM_PACKAGE_BODY=4,
- TYPE_ENUM_TRIGGER=5,
- TYPE_ENUM_PROXY=6
+ SP_TYPE_FUNCTION=1,
+ SP_TYPE_PROCEDURE=2,
+ SP_TYPE_PACKAGE=3,
+ SP_TYPE_PACKAGE_BODY=4,
+ SP_TYPE_TRIGGER=5,
+ SP_TYPE_EVENT=6,
};
-
class Sp_handler
{
bool sp_resolve_package_routine_explicit(THD *thd,
@@ -120,13 +121,13 @@ public: // TODO: make it private or protected
public:
virtual ~Sp_handler() {}
static const Sp_handler *handler(enum enum_sql_command cmd);
- static const Sp_handler *handler(stored_procedure_type type);
+ static const Sp_handler *handler(enum_sp_type type);
static const Sp_handler *handler(MDL_key::enum_mdl_namespace ns);
/*
Return a handler only those SP objects that store
definitions in the mysql.proc system table
*/
- static const Sp_handler *handler_mysql_proc(stored_procedure_type type)
+ static const Sp_handler *handler_mysql_proc(enum_sp_type type)
{
const Sp_handler *sph= handler(type);
return sph ? sph->sp_handler_mysql_proc() : NULL;
@@ -135,9 +136,8 @@ public:
static bool eq_routine_name(const LEX_CSTRING &name1,
const LEX_CSTRING &name2)
{
- return my_strnncoll(system_charset_info,
- (const uchar *) name1.str, name1.length,
- (const uchar *) name2.str, name2.length) == 0;
+ return system_charset_info->strnncoll(name1.str, name1.length,
+ name2.str, name2.length) == 0;
}
const char *type_str() const { return type_lex_cstring().str; }
virtual const char *show_create_routine_col1_caption() const
@@ -154,7 +154,7 @@ public:
{
return this;
}
- virtual stored_procedure_type type() const= 0;
+ virtual enum_sp_type type() const= 0;
virtual LEX_CSTRING type_lex_cstring() const= 0;
virtual LEX_CSTRING empty_body_lex_cstring(sql_mode_t mode) const
{
@@ -249,7 +249,7 @@ public:
class Sp_handler_procedure: public Sp_handler
{
public:
- stored_procedure_type type() const { return TYPE_ENUM_PROCEDURE; }
+ enum_sp_type type() const { return SP_TYPE_PROCEDURE; }
LEX_CSTRING type_lex_cstring() const
{
static LEX_CSTRING m_type_str= { STRING_WITH_LEN("PROCEDURE")};
@@ -299,7 +299,7 @@ public:
class Sp_handler_function: public Sp_handler
{
public:
- stored_procedure_type type() const { return TYPE_ENUM_FUNCTION; }
+ enum_sp_type type() const { return SP_TYPE_FUNCTION; }
LEX_CSTRING type_lex_cstring() const
{
static LEX_CSTRING m_type_str= { STRING_WITH_LEN("FUNCTION")};
@@ -368,7 +368,7 @@ public: // TODO: make it private or protected
const Database_qualified_name *name)
const;
public:
- stored_procedure_type type() const { return TYPE_ENUM_PACKAGE; }
+ enum_sp_type type() const { return SP_TYPE_PACKAGE; }
LEX_CSTRING type_lex_cstring() const
{
static LEX_CSTRING m_type_str= {STRING_WITH_LEN("PACKAGE")};
@@ -401,7 +401,7 @@ public:
class Sp_handler_package_body: public Sp_handler_package
{
public:
- stored_procedure_type type() const { return TYPE_ENUM_PACKAGE_BODY; }
+ enum_sp_type type() const { return SP_TYPE_PACKAGE_BODY; }
LEX_CSTRING type_lex_cstring() const
{
static LEX_CSTRING m_type_str= {STRING_WITH_LEN("PACKAGE BODY")};
@@ -434,7 +434,7 @@ public:
class Sp_handler_trigger: public Sp_handler
{
public:
- stored_procedure_type type() const { return TYPE_ENUM_TRIGGER; }
+ enum_sp_type type() const { return SP_TYPE_TRIGGER; }
LEX_CSTRING type_lex_cstring() const
{
static LEX_CSTRING m_type_str= { STRING_WITH_LEN("TRIGGER")};
@@ -493,20 +493,20 @@ inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd)
}
-inline const Sp_handler *Sp_handler::handler(stored_procedure_type type)
+inline const Sp_handler *Sp_handler::handler(enum_sp_type type)
{
switch (type) {
- case TYPE_ENUM_PROCEDURE:
+ case SP_TYPE_PROCEDURE:
return &sp_handler_procedure;
- case TYPE_ENUM_FUNCTION:
+ case SP_TYPE_FUNCTION:
return &sp_handler_function;
- case TYPE_ENUM_PACKAGE:
+ case SP_TYPE_PACKAGE:
return &sp_handler_package_spec;
- case TYPE_ENUM_PACKAGE_BODY:
+ case SP_TYPE_PACKAGE_BODY:
return &sp_handler_package_body;
- case TYPE_ENUM_TRIGGER:
+ case SP_TYPE_TRIGGER:
return &sp_handler_trigger;
- case TYPE_ENUM_PROXY:
+ case SP_TYPE_EVENT:
break;
}
return NULL;
@@ -655,7 +655,7 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
Routines which allow open/lock and close mysql.proc table even when
we already have some tables open and locked.
*/
-TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup);
+TABLE *open_proc_table_for_read(THD *thd);
bool load_charset(MEM_ROOT *mem_root,
Field *field,
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index e4ffbdcb155..b3949a94751 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -27,7 +27,7 @@ static ulong volatile Cversion= 1;
/*
- Cache of stored routines.
+ Cache of stored routines.
*/
class sp_cache
@@ -149,8 +149,8 @@ void sp_cache_end()
sp_cache_insert()
cp The cache to put routine into
sp Routine to insert.
-
- TODO: Perhaps it will be more straightforward if in case we returned an
+
+ TODO: Perhaps it will be more straightforward if in case we returned an
error from this function when we couldn't allocate sp_cache. (right
now failure to put routine into cache will cause a 'SP not found'
error to be reported at some later time)
@@ -173,18 +173,18 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp)
}
-/*
+/*
Look up a routine in the cache.
SYNOPSIS
sp_cache_lookup()
cp Cache to look into
name Name of rutine to find
-
+
NOTE
An obsolete (but not more obsolete then since last
sp_cache_flush_obsolete call) routine may be returned.
- RETURN
+ RETURN
The routine or
NULL if the routine not found.
*/
@@ -204,7 +204,7 @@ sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name)
SYNOPSIS
sp_cache_invalidate()
-
+
NOTE
This is called when a VIEW definition is created or modified (and in some
other contexts). We can't destroy sp_head objects here as one may modify
@@ -225,7 +225,7 @@ void sp_cache_invalidate()
@param[in] sp SP to remove.
@note This invalidates pointers to sp_head objects this thread
- uses. In practice that means 'dont call this function when
+ uses. In practice that means don't call this function when
inside SP'.
*/
@@ -264,7 +264,7 @@ sp_cache_enforce_limit(sp_cache *c, ulong upper_limit_for_elements)
}
/*************************************************************************
- Internal functions
+ Internal functions
*************************************************************************/
extern "C" uchar *hash_get_key_for_sp_head(const uchar *ptr, size_t *plen,
@@ -302,7 +302,7 @@ sp_cache::~sp_cache()
void
sp_cache::init()
{
- my_hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
+ my_hash_init(key_memory_sp_cache, &m_hashtable, system_charset_info, 0, 0, 0,
hash_get_key_for_sp_head, hash_free_sp_head, 0);
}
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index e91364b0c0a..9e5b5bee0f2 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -23,7 +23,6 @@
#include "probes_mysql.h"
#include "sql_show.h" // append_identifier
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
-#include "sql_acl.h" // *_ACL
#include "sql_array.h" // Dynamic_array
#include "log_event.h" // Query_log_event
#include "sql_derived.h" // mysql_handle_derived
@@ -56,8 +55,51 @@
#define SP_INSTR_UINT_MAXLEN 8
#define SP_STMT_PRINT_MAXLEN 40
-
#include <my_user.h>
+#include "mysql/psi/mysql_statement.h"
+#include "mysql/psi/mysql_sp.h"
+
+#ifdef HAVE_PSI_INTERFACE
+void init_sp_psi_keys()
+{
+ const char *category= "sp";
+ const int num __attribute__((unused)) = __LINE__ + 3;
+
+ PSI_server->register_statement(category, & sp_instr_stmt::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_set::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_set_trigger_field::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_jump::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_jump_if_not::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_freturn::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_preturn::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_hpush_jump::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_hpop::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_hreturn::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_cpush::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_cpop::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_copen::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_cclose::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_cfetch::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_agg_cfetch::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_cursor_copy_struct::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_error::psi_info, 1);
+ PSI_server->register_statement(category, & sp_instr_set_case_expr::psi_info, 1);
+
+ DBUG_ASSERT(SP_PSI_STATEMENT_INFO_COUNT == __LINE__ - num);
+}
+#endif
+
+#ifdef HAVE_PSI_SP_INTERFACE
+#define MYSQL_RUN_SP(SP,CODE) \
+ do { \
+ PSI_sp_locker_state psi_state; \
+ PSI_sp_locker *locker= MYSQL_START_SP(&psi_state, (SP)->m_sp_share); \
+ CODE; \
+ MYSQL_END_SP(locker); \
+ } while(0)
+#else
+#define MYSQL_RUN_SP(SP, CODE) do { CODE; } while(0)
+#endif
extern "C" uchar *sp_table_key(const uchar *ptr, size_t *plen, my_bool first);
@@ -188,7 +230,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_ENGINE_MUTEX:
case SQLCOM_SHOW_EVENTS:
case SQLCOM_SHOW_KEYS:
- case SQLCOM_SHOW_MASTER_STAT:
+ case SQLCOM_SHOW_BINLOG_STAT:
case SQLCOM_SHOW_OPEN_TABLES:
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_PROCESSLIST:
@@ -290,6 +332,10 @@ sp_get_flags_for_command(LEX *lex)
break;
case SQLCOM_DELETE:
case SQLCOM_DELETE_MULTI:
+ case SQLCOM_INSERT:
+ case SQLCOM_REPLACE:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_INSERT_SELECT:
{
/*
DELETE normally doesn't return resultset, but there are 3 exceptions:
@@ -297,8 +343,7 @@ sp_get_flags_for_command(LEX *lex)
- EXPLAIN DELETE ...
- ANALYZE DELETE ...
*/
- if (lex->first_select_lex()->item_list.is_empty() &&
- !lex->describe && !lex->analyze_stmt)
+ if (!lex->has_returning() && !lex->describe && !lex->analyze_stmt)
flags= 0;
else
flags= sp_head::MULTI_RESULTS;
@@ -306,10 +351,6 @@ sp_get_flags_for_command(LEX *lex)
}
case SQLCOM_UPDATE:
case SQLCOM_UPDATE_MULTI:
- case SQLCOM_INSERT:
- case SQLCOM_REPLACE:
- case SQLCOM_REPLACE_SELECT:
- case SQLCOM_INSERT_SELECT:
{
if (!lex->describe && !lex->analyze_stmt)
flags= 0;
@@ -456,8 +497,8 @@ sp_head *sp_head::create(sp_package *parent, const Sp_handler *handler,
enum_sp_aggregate_type agg_type)
{
MEM_ROOT own_root;
- init_sql_alloc(&own_root, "sp_head", MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC,
- MYF(0));
+ init_sql_alloc(key_memory_sp_head_main_root, &own_root, MEM_ROOT_BLOCK_SIZE,
+ MEM_ROOT_PREALLOC, MYF(0));
sp_head *sp;
if (!(sp= new (&own_root) sp_head(&own_root, parent, handler, agg_type)))
free_root(&own_root, MYF(0));
@@ -538,11 +579,12 @@ sp_head::sp_head(MEM_ROOT *mem_root_arg, sp_package *parent,
m_backpatch_goto.empty();
m_cont_backpatch.empty();
m_lex.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
- my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
- my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key,
- 0, 0);
- m_security_ctx.init();
+ my_init_dynamic_array(key_memory_sp_head_main_root, &m_instr,
+ sizeof(sp_instr *), 16, 8, MYF(0));
+ my_hash_init(key_memory_sp_head_main_root, &m_sptabs, system_charset_info, 0,
+ 0, 0, sp_table_key, 0, 0);
+ my_hash_init(key_memory_sp_head_main_root, &m_sroutines, system_charset_info,
+ 0, 0, 0, sp_sroutine_key, 0, 0);
DBUG_VOID_RETURN;
}
@@ -552,7 +594,7 @@ sp_package *sp_package::create(LEX *top_level_lex, const sp_name *name,
const Sp_handler *sph)
{
MEM_ROOT own_root;
- init_sql_alloc(&own_root, "sp_package", MEM_ROOT_BLOCK_SIZE,
+ init_sql_alloc(key_memory_sp_head_main_root, &own_root, MEM_ROOT_BLOCK_SIZE,
MEM_ROOT_PREALLOC, MYF(0));
sp_package *sp;
if (!(sp= new (&own_root) sp_package(&own_root, top_level_lex, name, sph)))
@@ -605,7 +647,7 @@ bool sp_head::eq_routine_spec(const sp_head *sp) const
bool sp_package::validate_after_parser(THD *thd)
{
- if (m_handler->type() != TYPE_ENUM_PACKAGE_BODY)
+ if (m_handler->type() != SP_TYPE_PACKAGE_BODY)
return false;
sp_head *sp= sp_cache_lookup(&thd->sp_package_spec_cache, this);
sp_package *spec= sp ? sp->get_package() : NULL;
@@ -684,7 +726,7 @@ bool sp_package::validate_private_routines(THD *thd)
LEX *sp_package::LexList::find(const LEX_CSTRING &name,
- stored_procedure_type type)
+ enum_sp_type type)
{
List_iterator<LEX> it(*this);
for (LEX *lex; (lex= it++); )
@@ -707,7 +749,7 @@ LEX *sp_package::LexList::find(const LEX_CSTRING &name,
LEX *sp_package::LexList::find_qualified(const LEX_CSTRING &name,
- stored_procedure_type type)
+ enum_sp_type type)
{
List_iterator<LEX> it(*this);
for (LEX *lex; (lex= it++); )
@@ -721,6 +763,17 @@ LEX *sp_package::LexList::find_qualified(const LEX_CSTRING &name,
}
+void sp_package::init_psi_share()
+{
+ List_iterator<LEX> it(m_routine_implementations);
+ for (LEX *lex; (lex= it++); )
+ {
+ DBUG_ASSERT(lex->sphead);
+ lex->sphead->init_psi_share();
+ }
+ sp_head::init_psi_share();
+}
+
void
sp_head::init(LEX *lex)
{
@@ -756,6 +809,13 @@ sp_head::init_sp_name(const sp_name *spname)
DBUG_VOID_RETURN;
}
+void
+sp_head::init_psi_share()
+{
+ m_sp_share= MYSQL_GET_SP_SHARE(m_handler->type(), m_db.str, static_cast<uint>(m_db.length),
+ m_name.str, static_cast<uint>(m_name.length));
+}
+
void
sp_head::set_body_start(THD *thd, const char *begin_ptr)
@@ -1039,7 +1099,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
{
DBUG_ENTER("subst_spvars");
- Dynamic_array<Rewritable_query_parameter*> rewritables;
+ Dynamic_array<Rewritable_query_parameter*> rewritables(PSI_INSTRUMENT_MEM);
char *pbuf;
StringBuffer<512> qbuf;
Copy_query_with_rewrite acc(thd, query_str->str, query_str->length, &qbuf);
@@ -1151,6 +1211,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
Query_arena execute_arena(&execute_mem_root, STMT_INITIALIZED_FOR_SP),
backup_arena;
query_id_t old_query_id;
+ CSET_STRING old_query;
TABLE *old_derived_tables;
TABLE *old_rec_tables;
LEX *old_lex;
@@ -1172,7 +1233,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
opt_trace_disable_if_no_security_context_access(thd);
/* init per-instruction memroot */
- init_sql_alloc(&execute_mem_root, "per_instruction_memroot",
+ init_sql_alloc(key_memory_sp_head_execute_root, &execute_mem_root,
MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
DBUG_ASSERT(!(m_flags & IS_INVOKED));
@@ -1231,6 +1292,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
be able properly do close_thread_tables() in instructions.
*/
old_query_id= thd->query_id;
+ old_query= thd->query_string;
old_derived_tables= thd->derived_tables;
thd->derived_tables= 0;
old_rec_tables= thd->rec_tables;
@@ -1361,8 +1423,24 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
WSREP_DEBUG("assigned new next trx ID for SP, trx id: %" PRIu64, thd->wsrep_next_trx_id());
}
#endif /* WITH_WSREP */
+
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_statement_locker_state state;
+ PSI_statement_locker *parent_locker;
+ PSI_statement_info *psi_info = i->get_psi_info();
+
+ parent_locker= thd->m_statement_psi;
+ thd->m_statement_psi= MYSQL_START_STATEMENT(& state, psi_info->m_key,
+ thd->db.str, thd->db.length, thd->charset(), m_sp_share);
+#endif
+
err_status= i->execute(thd, &ip);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= parent_locker;
+#endif
+
#ifdef WITH_WSREP
if (WSREP(thd))
{
@@ -1399,7 +1477,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/*
Reset the return code to zero if the transaction was
- replayed succesfully.
+ replayed successfully.
*/
if (must_replay && !wsrep_current_error(thd))
{
@@ -1491,6 +1569,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
old_change_list.move_elements_to(thd);
thd->lex= old_lex;
thd->set_query_id(old_query_id);
+ thd->set_query_inner(old_query);
DBUG_ASSERT(!thd->derived_tables);
thd->derived_tables= old_derived_tables;
thd->rec_tables= old_rec_tables;
@@ -1820,8 +1899,8 @@ 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, "execute_trigger", MEM_ROOT_BLOCK_SIZE, 0,
- MYF(0));
+ init_sql_alloc(key_memory_sp_head_call_root,
+ &call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
Row_definition_list defs;
@@ -1834,7 +1913,7 @@ sp_head::execute_trigger(THD *thd,
thd->spcont= nctx;
- err_status= execute(thd, FALSE);
+ MYSQL_RUN_SP(this, err_status= execute(thd, FALSE));
err_with_cleanup:
thd->restore_active_arena(&call_arena, &backup_arena);
@@ -2084,7 +2163,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
*/
thd->set_n_backup_active_arena(call_arena, &backup_arena);
- err_status= execute(thd, TRUE);
+ MYSQL_RUN_SP(this, err_status= execute(thd, TRUE));
thd->restore_active_arena(call_arena, &backup_arena);
@@ -2365,11 +2444,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#endif
opt_trace_disable_if_no_stored_proc_func_access(thd, this);
+
if (!err_status)
- {
- err_status= execute(thd, TRUE);
- DBUG_PRINT("info", ("execute returned %d", (int) err_status));
- }
+ MYSQL_RUN_SP(this, err_status= execute(thd, TRUE));
if (save_log_general)
thd->variables.option_bits &= ~OPTION_LOG_OFF;
@@ -2415,11 +2492,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
break;
}
- Send_field *out_param_info= new (thd->mem_root) Send_field();
- nctx->get_parameter(i)->make_send_field(thd, out_param_info);
- out_param_info->db_name= m_db.str;
- out_param_info->table_name= m_name.str;
- out_param_info->org_table_name= m_name.str;
+ Send_field *out_param_info= new (thd->mem_root) Send_field(thd, nctx->get_parameter(i));
+ out_param_info->db_name= m_db;
+ out_param_info->table_name= m_name;
+ out_param_info->org_table_name= m_name;
out_param_info->col_name= spvar->name;
out_param_info->org_col_name= spvar->name;
@@ -2858,7 +2934,7 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access)
*full_access= ((!check_table_access(thd, SELECT_ACL, &tables, FALSE,
1, TRUE) &&
- (tables.grant.privilege & SELECT_ACL) != 0) ||
+ (tables.grant.privilege & SELECT_ACL) != NO_ACL) ||
/* Check if user owns the routine. */
(!strcmp(sp->m_definer.user.str,
thd->security_ctx->priv_user) &&
@@ -3099,7 +3175,7 @@ bool sp_head::add_instr_freturn(THD *thd, sp_pcontext *spcont,
{
sp_instr_freturn *i= new (thd->mem_root)
sp_instr_freturn(instructions(), spcont, item,
- m_return_field_def.type_handler(), thd->lex);
+ m_return_field_def.type_handler(), lex);
if (i == NULL || add_instr(i))
return true;
m_flags|= sp_head::HAS_RETURN;
@@ -3357,8 +3433,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
It's reset further in the common code part.
It's merged with the saved parent's value at the exit of this func.
*/
- bool parent_modified_non_trans_table= thd->transaction.stmt.modified_non_trans_table;
- thd->transaction.stmt.modified_non_trans_table= FALSE;
+ bool parent_modified_non_trans_table= thd->transaction->stmt.modified_non_trans_table;
+ thd->transaction->stmt.modified_non_trans_table= FALSE;
DBUG_ASSERT(!thd->derived_tables);
DBUG_ASSERT(thd->Item_change_list::is_empty());
/*
@@ -3450,7 +3526,7 @@ 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?
+ //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)
@@ -3481,7 +3557,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
Merge here with the saved parent's values
what is needed from the substatement gained
*/
- thd->transaction.stmt.modified_non_trans_table |= parent_modified_non_trans_table;
+ thd->transaction->stmt.modified_non_trans_table |= parent_modified_non_trans_table;
TRANSACT_TRACKER(add_trx_state_from_thd(thd));
@@ -3559,6 +3635,9 @@ int sp_instr::exec_core(THD *thd, uint *nextp)
sp_instr_stmt class functions
*/
+PSI_statement_info sp_instr_stmt::psi_info=
+{ 0, "stmt", 0};
+
int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
@@ -3569,6 +3648,8 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, m_query.str, static_cast<uint>(m_query.length));
+
#if defined(ENABLED_PROFILING)
/* This s-p instr is profilable and will be captured. */
thd->profiling.set_query_source(m_query.str, m_query.length);
@@ -3695,6 +3776,9 @@ sp_instr_stmt::exec_core(THD *thd, uint *nextp)
sp_instr_set class functions
*/
+PSI_statement_info sp_instr_set::psi_info=
+{ 0, "set", 0};
+
int
sp_instr_set::execute(THD *thd, uint *nextp)
{
@@ -3845,6 +3929,9 @@ sp_instr_set_row_field_by_name::print(String *str)
sp_instr_set_trigger_field class functions
*/
+PSI_statement_info sp_instr_set_trigger_field::psi_info=
+{ 0, "set_trigger_field", 0};
+
int
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
{
@@ -3857,10 +3944,8 @@ sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
int
sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
{
- bool sav_abort_on_warning= thd->abort_on_warning;
- thd->abort_on_warning= thd->is_strict_mode() && !thd->lex->ignore;
+ Abort_on_warning_instant_set aws(thd, thd->is_strict_mode() && !thd->lex->ignore);
const int res= (trigger_field->set_value(thd, &value) ? -1 : 0);
- thd->abort_on_warning= sav_abort_on_warning;
*nextp = m_ip+1;
return res;
}
@@ -3890,6 +3975,9 @@ uint sp_instr_opt_meta::get_cont_dest() const
sp_instr_jump class functions
*/
+PSI_statement_info sp_instr_jump::psi_info=
+{ 0, "jump", 0};
+
int
sp_instr_jump::execute(THD *thd, uint *nextp)
{
@@ -3955,6 +4043,9 @@ sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
sp_instr_jump_if_not class functions
*/
+PSI_statement_info sp_instr_jump_if_not::psi_info=
+{ 0, "jump_if_not", 0};
+
int
sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
{
@@ -4050,6 +4141,9 @@ sp_instr_jump_if_not::opt_move(uint dst, List<sp_instr> *bp)
sp_instr_freturn class functions
*/
+PSI_statement_info sp_instr_freturn::psi_info=
+{ 0, "freturn", 0};
+
int
sp_instr_freturn::execute(THD *thd, uint *nextp)
{
@@ -4114,9 +4208,33 @@ sp_instr_freturn::print(String *str)
}
/*
+ sp_instr_preturn class functions
+*/
+
+PSI_statement_info sp_instr_preturn::psi_info=
+{ 0, "preturn", 0};
+
+int
+sp_instr_preturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_preturn::execute");
+ *nextp= UINT_MAX;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_preturn::print(String *str)
+{
+ str->append(STRING_WITH_LEN("preturn"));
+}
+
+/*
sp_instr_hpush_jump class functions
*/
+PSI_statement_info sp_instr_hpush_jump::psi_info=
+{ 0, "hpush_jump", 0};
+
int
sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
{
@@ -4193,6 +4311,9 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
sp_instr_hpop class functions
*/
+PSI_statement_info sp_instr_hpop::psi_info=
+{ 0, "hpop", 0};
+
int
sp_instr_hpop::execute(THD *thd, uint *nextp)
{
@@ -4217,6 +4338,9 @@ sp_instr_hpop::print(String *str)
sp_instr_hreturn class functions
*/
+PSI_statement_info sp_instr_hreturn::psi_info=
+{ 0, "hreturn", 0};
+
int
sp_instr_hreturn::execute(THD *thd, uint *nextp)
{
@@ -4276,6 +4400,9 @@ sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
sp_instr_cpush class functions
*/
+PSI_statement_info sp_instr_cpush::psi_info=
+{ 0, "cpush", 0};
+
int
sp_instr_cpush::execute(THD *thd, uint *nextp)
{
@@ -4317,6 +4444,9 @@ sp_instr_cpush::print(String *str)
sp_instr_cpop class functions
*/
+PSI_statement_info sp_instr_cpop::psi_info=
+{ 0, "cpop", 0};
+
int
sp_instr_cpop::execute(THD *thd, uint *nextp)
{
@@ -4347,6 +4477,9 @@ sp_instr_cpop::print(String *str)
Assert that we either have an error or a cursor
*/
+PSI_statement_info sp_instr_copen::psi_info=
+{ 0, "copen", 0};
+
int
sp_instr_copen::execute(THD *thd, uint *nextp)
{
@@ -4405,6 +4538,9 @@ sp_instr_copen::print(String *str)
sp_instr_cclose class functions
*/
+PSI_statement_info sp_instr_cclose::psi_info=
+{ 0, "cclose", 0};
+
int
sp_instr_cclose::execute(THD *thd, uint *nextp)
{
@@ -4447,6 +4583,9 @@ sp_instr_cclose::print(String *str)
sp_instr_cfetch class functions
*/
+PSI_statement_info sp_instr_cfetch::psi_info=
+{ 0, "cfetch", 0};
+
int
sp_instr_cfetch::execute(THD *thd, uint *nextp)
{
@@ -4494,6 +4633,13 @@ sp_instr_cfetch::print(String *str)
}
}
+/*
+ sp_instr_agg_cfetch class functions
+*/
+
+PSI_statement_info sp_instr_agg_cfetch::psi_info=
+{ 0, "agg_cfetch", 0};
+
int
sp_instr_agg_cfetch::execute(THD *thd, uint *nextp)
{
@@ -4546,6 +4692,9 @@ sp_instr_agg_cfetch::print(String *str)
- copies the cursor structure to the associated %ROWTYPE variable.
*/
+PSI_statement_info sp_instr_cursor_copy_struct::psi_info=
+{ 0, "cursor_copy_struct", 0};
+
int
sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp)
{
@@ -4618,6 +4767,9 @@ sp_instr_cursor_copy_struct::print(String *str)
sp_instr_error class functions
*/
+PSI_statement_info sp_instr_error::psi_info=
+{ 0, "error", 0};
+
int
sp_instr_error::execute(THD *thd, uint *nextp)
{
@@ -4644,6 +4796,9 @@ sp_instr_error::print(String *str)
sp_instr_set_case_expr class implementation
**************************************************************************/
+PSI_statement_info sp_instr_set_case_expr::psi_info=
+{ 0, "set_case_expr", 0};
+
int
sp_instr_set_case_expr::execute(THD *thd, uint *nextp)
{
@@ -4984,8 +5139,8 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->lock_type= locktype;
table->select_lex= lex->current_select;
table->cacheable_table= 1;
- table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
- mdl_type, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table->mdl_request, MDL_key::TABLE, table->db.str,
+ table->table_name.str, mdl_type, MDL_TRANSACTION);
lex->add_to_query_tables(table);
return table;
@@ -5016,6 +5171,9 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
if (!(val= adjust_assignment_source(thd, val, spv->default_value)))
return true;
+ if (val->walk(&Item::unknown_splocal_processor, false, NULL))
+ return true;
+
sp_instr_set *sp_set= new (thd->mem_root)
sp_instr_set(instructions(), spcont, rh,
spv->offset, val, lex,
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 6cf4610c466..913be1aace7 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -47,6 +47,14 @@ class sp_instr;
class sp_instr_opt_meta;
class sp_instr_jump_if_not;
+/**
+ Number of PSI_statement_info instruments
+ for internal stored programs statements.
+*/
+#ifdef HAVE_PSI_INTERFACE
+void init_sp_psi_keys(void);
+#endif
+
/*************************************************************************/
/**
@@ -174,6 +182,11 @@ public:
const Sp_handler *m_handler;
uint m_flags; // Boolean attributes of a stored routine
+ /**
+ Instrumentation interface for SP.
+ */
+ PSI_sp_share *m_sp_share;
+
Column_definition m_return_field_def; /**< This is used for FUNCTIONs only. */
const char *m_tmp_query; ///< Temporary pointer to sub query string
@@ -856,6 +869,8 @@ public:
return NULL;
}
+ virtual void init_psi_share();
+
protected:
MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root
@@ -931,9 +946,9 @@ public:
public:
LexList() { elements= 0; }
// Find a package routine by a non qualified name
- LEX *find(const LEX_CSTRING &name, stored_procedure_type type);
+ LEX *find(const LEX_CSTRING &name, enum_sp_type type);
// Find a package routine by a package-qualified name, e.g. 'pkg.proc'
- LEX *find_qualified(const LEX_CSTRING &name, stored_procedure_type type);
+ LEX *find_qualified(const LEX_CSTRING &name, enum_sp_type type);
// Check if a routine with the given qualified name already exists
bool check_dup_qualified(const LEX_CSTRING &name, const Sp_handler *sph)
{
@@ -990,6 +1005,7 @@ public:
m_routine_implementations.push_back(lex, &main_mem_root);
}
sp_package *get_package() { return this; }
+ void init_psi_share();
bool is_invoked() const
{
/*
@@ -1156,6 +1172,7 @@ public:
{
m_ip= dst;
}
+ virtual PSI_statement_info* get_psi_info() = 0;
}; // class sp_instr : public Sql_alloc
@@ -1282,6 +1299,10 @@ private:
sp_lex_keeper m_lex_keeper;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
+
}; // class sp_instr_stmt : public sp_instr
@@ -1316,6 +1337,10 @@ protected:
uint m_offset; ///< Frame offset
Item *m_value;
sp_lex_keeper m_lex_keeper;
+
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_set : public sp_instr
@@ -1424,6 +1449,10 @@ private:
Item_trigger_field *trigger_field;
Item *value;
sp_lex_keeper m_lex_keeper;
+
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_trigger_field : public sp_instr
@@ -1510,6 +1539,9 @@ public:
m_dest= new_dest;
}
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_jump : public sp_instr_opt_meta
@@ -1561,6 +1593,9 @@ private:
Item *m_expr; ///< The condition
sp_lex_keeper m_lex_keeper;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_jump_if_not : public sp_instr_jump
@@ -1578,17 +1613,9 @@ public:
virtual ~sp_instr_preturn()
{}
- virtual int execute(THD *thd, uint *nextp)
- {
- DBUG_ENTER("sp_instr_preturn::execute");
- *nextp= UINT_MAX;
- DBUG_RETURN(0);
- }
+ virtual int execute(THD *thd, uint *nextp);
- virtual void print(String *str)
- {
- str->append(STRING_WITH_LEN("preturn"));
- }
+ virtual void print(String *str);
virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
@@ -1596,6 +1623,9 @@ public:
return UINT_MAX;
}
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_preturn : public sp_instr
@@ -1633,6 +1663,9 @@ protected:
const Type_handler *m_type_handler;
sp_lex_keeper m_lex_keeper;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_freturn : public sp_instr
@@ -1698,6 +1731,9 @@ private:
// debug version only). It's used in print().
uint m_frame;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_hpush_jump : public sp_instr_jump
@@ -1728,6 +1764,9 @@ private:
uint m_count;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_hpop : public sp_instr
@@ -1762,12 +1801,14 @@ private:
uint m_frame;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_hreturn : public sp_instr_jump
/** This is DECLARE CURSOR */
-class sp_instr_cpush : public sp_instr,
- public sp_cursor
+class sp_instr_cpush : public sp_instr, public sp_cursor
{
sp_instr_cpush(const sp_instr_cpush &); /**< Prevent use of these */
void operator=(sp_instr_cpush &);
@@ -1796,6 +1837,9 @@ private:
sp_lex_keeper m_lex_keeper;
uint m_cursor; /**< Frame offset (for debugging) */
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_cpush : public sp_instr
@@ -1826,6 +1870,9 @@ private:
uint m_count;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_cpop : public sp_instr
@@ -1853,6 +1900,9 @@ private:
uint m_cursor; ///< Stack index
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_copen : public sp_instr_stmt
@@ -1880,6 +1930,10 @@ public:
virtual int execute(THD *thd, uint *nextp);
virtual int exec_core(THD *thd, uint *nextp);
virtual void print(String *str);
+
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
};
@@ -1905,6 +1959,9 @@ private:
uint m_cursor;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_cclose : public sp_instr
@@ -1939,6 +1996,9 @@ private:
List<sp_variable> m_varlist;
bool m_error_on_no_data;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_cfetch : public sp_instr
/*
@@ -1963,6 +2023,10 @@ public:
virtual int execute(THD *thd, uint *nextp);
virtual void print(String *str);
+
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_agg_cfetch : public sp_instr
@@ -1996,6 +2060,9 @@ private:
int m_errcode;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_error : public sp_instr
@@ -2035,6 +2102,9 @@ private:
Item *m_case_expr;
sp_lex_keeper m_lex_keeper;
+public:
+ virtual PSI_statement_info* get_psi_info() { return & psi_info; }
+ static PSI_statement_info psi_info;
}; // class sp_instr_set_case_expr : public sp_instr_opt_meta
bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access);
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 433efda479b..848d1f0c655 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -95,6 +96,9 @@ sp_pcontext::sp_pcontext()
: Sql_alloc(),
m_max_var_index(0), m_max_cursor_index(0),
m_parent(NULL), m_pboundary(0),
+ m_vars(PSI_INSTRUMENT_MEM), m_case_expr_ids(PSI_INSTRUMENT_MEM),
+ m_conditions(PSI_INSTRUMENT_MEM), m_cursors(PSI_INSTRUMENT_MEM),
+ m_handlers(PSI_INSTRUMENT_MEM), m_children(PSI_INSTRUMENT_MEM),
m_scope(REGULAR_SCOPE)
{
init(0, 0, 0);
@@ -105,6 +109,9 @@ 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_parent(prev), m_pboundary(0),
+ m_vars(PSI_INSTRUMENT_MEM), m_case_expr_ids(PSI_INSTRUMENT_MEM),
+ m_conditions(PSI_INSTRUMENT_MEM), m_cursors(PSI_INSTRUMENT_MEM),
+ m_handlers(PSI_INSTRUMENT_MEM), m_children(PSI_INSTRUMENT_MEM),
m_scope(scope)
{
init(prev->m_var_offset + prev->m_max_var_index,
@@ -208,9 +215,8 @@ sp_variable *sp_pcontext::find_variable(const LEX_CSTRING *name,
{
sp_variable *p= m_vars.at(i);
- if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)p->name.str, p->name.length) == 0)
+ if (system_charset_info->strnncoll(name->str, name->length,
+ p->name.str, p->name.length) == 0)
{
return p;
}
@@ -624,9 +630,8 @@ const sp_pcursor *sp_pcontext::find_cursor(const LEX_CSTRING *name,
{
LEX_CSTRING n= m_cursors.at(i);
- if (my_strnncoll(system_charset_info,
- (const uchar *) name->str, name->length,
- (const uchar *) n.str, n.length) == 0)
+ if (system_charset_info->strnncoll(name->str, name->length,
+ n.str, n.length) == 0)
{
*poff= m_cursor_offset + i;
return &m_cursors.at(i);
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index b1d77234f54..ffc9c0e19af 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -1,5 +1,6 @@
/* -*- C++ -*- */
/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -260,9 +261,8 @@ public:
}
bool eq_name(const LEX_CSTRING *str) const
{
- return my_strnncoll(system_charset_info,
- (const uchar *) name.str, name.length,
- (const uchar *) str->str, str->length) == 0;
+ return system_charset_info->strnncoll(name.str, name.length,
+ str->str, str->length) == 0;
}
};
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 17b4c83b7bc..c4c19dd39f6 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -74,6 +74,7 @@ sp_rcontext::sp_rcontext(const sp_head *owner,
m_return_value_fld(return_value_fld),
m_return_value_set(false),
m_in_sub_stmt(in_sub_stmt),
+ m_handlers(PSI_INSTRUMENT_MEM), m_handler_call_stack(PSI_INSTRUMENT_MEM),
m_ccount(0)
{
}
diff --git a/sql/spatial.cc b/sql/spatial.cc
index 2b36468e158..301d50f5d1e 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2002, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2013, Monty Program Ab.
+ Copyright (c) 2011, 2020, MariaDB Corporation.
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
@@ -241,9 +241,8 @@ Geometry::Class_info *Geometry::find_class(const char *name, size_t len)
{
if (*cur_rt &&
((*cur_rt)->m_name.length == len) &&
- (my_strnncoll(&my_charset_latin1,
- (const uchar*) (*cur_rt)->m_name.str, len,
- (const uchar*) name, len) == 0))
+ (my_charset_latin1.strnncoll((*cur_rt)->m_name.str, len,
+ name, len) == 0))
return *cur_rt;
}
return 0;
@@ -277,6 +276,33 @@ Geometry *Geometry::construct(Geometry_buffer *buffer,
}
+uint Geometry::get_key_image_itMBR(LEX_CSTRING &src, uchar *buff, uint length)
+{
+ const char *dummy;
+ MBR mbr;
+ Geometry_buffer buffer;
+ Geometry *gobj;
+ const uint image_length= SIZEOF_STORED_DOUBLE*4;
+
+ if (src.length < SRID_SIZE)
+ {
+ bzero(buff, image_length);
+ return image_length;
+ }
+ gobj= Geometry::construct(&buffer, (char*) src.str, (uint32) src.length);
+ if (!gobj || gobj->get_mbr(&mbr, &dummy))
+ bzero(buff, image_length);
+ else
+ {
+ float8store(buff, mbr.xmin);
+ float8store(buff+8, mbr.xmax);
+ float8store(buff+16, mbr.ymin);
+ float8store(buff+24, mbr.ymax);
+ }
+ return image_length;
+}
+
+
Geometry *Geometry::create_from_wkt(Geometry_buffer *buffer,
Gis_read_stream *trs, String *wkt,
bool init_stream)
@@ -520,8 +546,8 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
goto create_geom;
}
else if (je->value_len == feature_coll_type_len &&
- my_strnncoll(&my_charset_latin1, je->value, je->value_len,
- feature_coll_type, feature_coll_type_len) == 0)
+ my_charset_latin1.strnncoll(je->value, je->value_len,
+ feature_coll_type, feature_coll_type_len) == 0)
{
/*
'FeatureCollection' type found. Handle the 'Featurecollection'/'features'
@@ -532,8 +558,8 @@ Geometry *Geometry::create_from_json(Geometry_buffer *buffer,
fcoll_type_found= 1;
}
else if (je->value_len == feature_type_len &&
- my_strnncoll(&my_charset_latin1, je->value, je->value_len,
- feature_type, feature_type_len) == 0)
+ my_charset_latin1.strnncoll(je->value, je->value_len,
+ feature_type, feature_type_len) == 0)
{
if (geometry_start)
goto handle_geometry_key;
@@ -925,8 +951,7 @@ static int read_point_from_json(json_engine_t *je, bool er_on_3D,
goto bad_coordinates;
d= (n_coord == 0) ? x : ((n_coord == 1) ? y : &tmp);
- *d= my_strntod(je->s.cs, (char *) je->value,
- je->value_len, &endptr, &err);
+ *d= je->s.cs->strntod((char *) je->value, je->value_len, &endptr, &err);
if (err)
goto bad_coordinates;
n_coord++;
@@ -3054,9 +3079,7 @@ bool Gis_geometry_collection::init_from_wkt(Gis_read_stream *trs, String *wkb)
return 1;
if (next_word.length != 5 ||
- (my_strnncoll(&my_charset_latin1,
- (const uchar*) "empty", 5,
- (const uchar*) next_word.str, 5) != 0))
+ (my_charset_latin1.strnncoll("empty", 5, next_word.str, 5) != 0))
{
for (;;)
{
diff --git a/sql/spatial.h b/sql/spatial.h
index 55f450b1b1b..0b998e2e55c 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -317,6 +317,7 @@ public:
bool er_on_3D, String *res);
static Geometry *create_from_opresult(Geometry_buffer *g_buf,
String *res, Gcalc_result_receiver &rr);
+ static uint get_key_image_itMBR(LEX_CSTRING &src, uchar *buff, uint length);
int as_wkt(String *wkt, const char **end);
int as_json(String *wkt, uint max_dec_digits, const char **end);
int bbox_as_json(String *wkt);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 42bf1782bdb..2c96e0e9ff2 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -62,13 +62,22 @@
bool mysql_user_table_is_in_short_password_format= false;
bool using_global_priv_table= true;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
// set that from field length in acl_load?
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
const uint max_hostname_length= 60;
const uint max_dbname_length= 64;
+#endif
-#include "sql_acl_getsort.ic"
+const char *safe_vio_type_name(Vio *vio)
+{
+ size_t unused;
+#ifdef EMBEDDED_LIBRARY
+ if (!vio) return "Internal";
#endif
+ return vio_type_name(vio_type(vio), &unused);
+}
+
+#include "sql_acl_getsort.ic"
static LEX_CSTRING native_password_plugin_name= {
STRING_WITH_LEN("mysql_native_password")
@@ -126,9 +135,9 @@ static bool compare_hostname(const acl_host_and_ip *, const char *, const char *
class ACL_ACCESS {
public:
ulonglong sort;
- ulong access;
+ privilege_t access;
ACL_ACCESS()
- :sort(0), access(0)
+ :sort(0), access(NO_ACL)
{ }
};
@@ -193,7 +202,7 @@ public:
ACL_USER() { }
ACL_USER(THD *thd, const LEX_USER &combo,
const Account_options &options,
- const ulong privileges);
+ const privilege_t privileges);
ACL_USER *copy(MEM_ROOT *root)
{
@@ -259,7 +268,7 @@ public:
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;
+ privilege_t 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
@@ -270,16 +279,17 @@ public:
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);
+ ACL_ROLE(const char * rolename, privilege_t privileges, MEM_ROOT *mem);
};
class ACL_DB :public ACL_ACCESS
{
public:
+ ACL_DB() :initial_access(NO_ACL) { }
acl_host_and_ip host;
const char *user,*db;
- ulong initial_access; /* access bits present in the table */
+ privilege_t initial_access; /* access bits present in the table */
const char *get_username() { return user; }
};
@@ -521,7 +531,7 @@ public:
class acl_entry :public hash_filo_element
{
public:
- ulong access;
+ privilege_t access;
uint16 length;
char key[1]; // Key will be stored here
};
@@ -645,7 +655,7 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username,
#define ROLE_OPENED (1L << 3)
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_proxy_users;
-static Dynamic_array<ACL_DB> acl_dbs(0U,50U);
+static Dynamic_array<ACL_DB> acl_dbs(PSI_INSTRUMENT_MEM, 0U, 50U);
typedef Dynamic_array<ACL_DB>::CMP_FUNC acl_dbs_cmp;
static HASH acl_roles;
/*
@@ -663,7 +673,7 @@ static HASH package_spec_priv_hash, package_body_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
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 privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field=0);
static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b);
static int acl_user_compare(const ACL_USER *a, const ACL_USER *b);
static void rebuild_acl_users();
@@ -810,15 +820,15 @@ class Grant_table_base
/* Return the underlying TABLE handle. */
TABLE* table() const { return m_table; }
- ulong get_access() const
+ privilege_t get_access() const
{
- ulong access_bits= 0, bit= 1;
+ ulonglong access_bits= 0, bit= 1;
for (uint i = start_priv_columns; i < end_priv_columns; i++, bit<<=1)
{
if (get_YN_as_bool(m_table->field[i]))
access_bits|= bit;
}
- return access_bits;
+ return ALL_KNOWN_ACL & access_bits;
}
protected:
@@ -875,8 +885,8 @@ class User_table: public Grant_table_base
virtual LEX_CSTRING& name() const = 0;
virtual int get_auth(THD *, MEM_ROOT *, ACL_USER *u) const= 0;
virtual bool set_auth(const ACL_USER &u) const = 0;
- virtual ulong get_access() const = 0;
- virtual void set_access(ulong rights, bool revoke) const = 0;
+ virtual privilege_t get_access() const = 0;
+ virtual void set_access(const privilege_t rights, bool revoke) const = 0;
char *get_host(MEM_ROOT *root) const
{ return ::get_field(root, m_table->field[0]); }
@@ -994,9 +1004,9 @@ class User_table_tabular: public User_table
return 0;
}
- ulong get_access() const
+ privilege_t get_access() const
{
- ulong access= Grant_table_base::get_access();
+ privilege_t access(Grant_table_base::get_access());
if ((num_fields() <= 13) && (access & CREATE_ACL))
access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
@@ -1004,7 +1014,8 @@ class User_table_tabular: public User_table
{
access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
if (access & FILE_ACL)
- access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
+ access|= BINLOG_MONITOR_ACL | REPL_SLAVE_ACL | BINLOG_ADMIN_ACL |
+ BINLOG_REPLAY_ACL;
if (access & PROCESS_ACL)
access|= SUPER_ACL | EXECUTE_ACL;
}
@@ -1032,12 +1043,26 @@ class User_table_tabular: public User_table
if (num_fields() <= 46 && (access & DELETE_ACL))
access|= DELETE_HISTORY_ACL;
+ if (access & SUPER_ACL)
+ access|= GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS;
+
+ /*
+ The SHOW SLAVE HOSTS statement :
+ - required REPLICATION SLAVE privilege prior to 10.5.2
+ - requires REPLICATION MASTER ADMIN privilege since 10.5.2
+ There is no a way to GRANT MASTER ADMIN with User_table_tabular.
+ So let's automatically add REPLICATION MASTER ADMIN for all users
+ that had REPLICATION SLAVE. This will allow to do SHOW SLAVE HOSTS.
+ */
+ if (access & REPL_SLAVE_ACL)
+ access|= REPL_MASTER_ADMIN_ACL;
+
return access & GLOBAL_ACLS;
}
- void set_access(ulong rights, bool revoke) const
+ void set_access(const privilege_t rights, bool revoke) const
{
- ulong priv= SELECT_ACL;
+ ulonglong priv(SELECT_ACL);
for (uint i= start_priv_columns; i < end_priv_columns; i++, priv <<= 1)
{
if (priv & rights)
@@ -1478,23 +1503,103 @@ class User_table_json: public User_table
set_str_value("authentication_string",
u.auth[i].auth_string.str, u.auth[i].auth_string.length);
}
- ulong get_access() const
+
+ void print_warning_bad_version_id(ulonglong version_id) const
+ {
+ sql_print_warning("'user' entry '%s@%s' has a wrong 'version_id' value %lld",
+ safe_str(get_user(current_thd->mem_root)),
+ safe_str(get_host(current_thd->mem_root)),
+ version_id);
+ }
+
+ void print_warning_bad_access(ulonglong version_id,
+ privilege_t mask,
+ ulonglong access) const
{
+ sql_print_warning("'user' entry '%s@%s' "
+ "has a wrong 'access' value 0x%llx "
+ "(allowed mask is 0x%llx, version_id=%lld)",
+ safe_str(get_user(current_thd->mem_root)),
+ safe_str(get_host(current_thd->mem_root)),
+ access, mask, version_id);
+ }
+
+ privilege_t adjust_access(ulonglong version_id, ulonglong access) const
+ {
+ privilege_t mask= ALL_KNOWN_ACL_100304;
+ ulonglong orig_access= access;
+ if (version_id >= 100502)
+ {
+ mask= ALL_KNOWN_ACL_100502;
+ }
+ else // 100501 or earlier
+ {
+ /*
+ Address changes in SUPER and REPLICATION SLAVE made in 10.5.2.
+ This also covers a special case: if the user had ALL PRIVILEGES before
+ the upgrade, it gets ALL PRIVILEGES after the upgrade.
+ */
+ if (access & SUPER_ACL)
+ {
+ if (access & REPL_SLAVE_ACL)
+ {
+ /*
+ The user could do both before the upgrade:
+ - set global variables (because of SUPER_ACL)
+ - execute "SHOW SLAVE HOSTS" (because of REPL_SLAVE_ACL)
+ Grant all new privileges that were splitted from SUPER (in 10.5.2),
+ and REPLICATION MASTER ADMIN, so it still can do "SHOW SLAVE HOSTS".
+ */
+ access|= REPL_MASTER_ADMIN_ACL;
+ }
+ access|= GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS;
+ }
+ }
+
+ if (orig_access & ~mask)
+ {
+ print_warning_bad_access(version_id, mask, orig_access);
+ return NO_ACL;
+ }
+ return access & ALL_KNOWN_ACL;
+ }
+
+ privilege_t get_access() const
+ {
+ ulonglong version_id= (ulonglong) get_int_value("version_id");
+ ulonglong access= (ulonglong) get_int_value("access");
+
+ /*
+ Special case:
+ mysql_system_tables_data.sql populates "ALL PRIVILEGES"
+ for the super user this way:
+ {"access":18446744073709551615}
+ */
+ if (access == (ulonglong) ~0)
+ return GLOBAL_ACLS;
+
/*
- when new privileges will be added, we'll start storing GLOBAL_ACLS
- (or, for example, my_count_bits(GLOBAL_ACLS))
- in the json too, and it'll allow us to do privilege upgrades
+ Reject obviously bad (negative and too large) version_id values.
+ Also reject versions before 10.4.0 (when JSON table was added).
*/
- return get_int_value("access") & GLOBAL_ACLS;
+ if ((longlong) version_id < 0 || version_id > 999999 ||
+ (version_id > 0 && version_id < 100400))
+ {
+ print_warning_bad_version_id(version_id);
+ return NO_ACL;
+ }
+ return adjust_access(version_id, access) & GLOBAL_ACLS;
}
- void set_access(ulong rights, bool revoke) const
+
+ void set_access(const privilege_t rights, bool revoke) const
{
- ulong access= get_access();
+ privilege_t access= get_access();
if (revoke)
access&= ~rights;
else
access|= rights;
- set_int_value("access", access & GLOBAL_ACLS);
+ set_int_value("access", (longlong) (access & GLOBAL_ACLS));
+ set_int_value("version_id", (longlong) MYSQL_VERSION_ID);
}
const char *unsafe_str(const char *s) const
{ return s[0] ? s : NULL; }
@@ -1878,7 +1983,9 @@ class Grant_tables
if (res)
DBUG_RETURN(res);
- if (lock_tables(thd, first, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
+ if (lock_tables(thd, first, counter,
+ MYSQL_LOCK_IGNORE_TIMEOUT |
+ MYSQL_OPEN_IGNORE_LOGGING_FORMAT))
DBUG_RETURN(-1);
p_user_table->set_table(tables[USER_TABLE].table);
@@ -1987,17 +2094,20 @@ enum enum_acl_lists
ROLES_MAPPINGS_HASH
};
-ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
+ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root)
+ :
+ /* set initial role access the same as the table row privileges */
+ initial_role_access(user->access),
+ counter(0)
{
access= user->access;
- /* set initial role access the same as the table row privileges */
- initial_role_access= user->access;
this->user= user->user;
bzero(&parent_grantee, sizeof(parent_grantee));
flags= IS_ROLE;
}
-ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
+ACL_ROLE::ACL_ROLE(const char * rolename, privilege_t privileges,
+ MEM_ROOT *root) :
initial_role_access(privileges), counter(0)
{
this->access= initial_role_access;
@@ -2261,10 +2371,10 @@ bool acl_init(bool dont_read_acl_tables)
bool return_val;
DBUG_ENTER("acl_init");
- acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0,
+ acl_cache= new Hash_filo<acl_entry>(key_memory_acl_cache, ACL_CACHE_SIZE, 0, 0,
(my_hash_get_key) acl_entry_get_key,
- (my_hash_free_key) free,
- &my_charset_utf8_bin);
+ (my_hash_free_key) my_free,
+ &my_charset_utf8mb3_bin);
/*
cache built-in native authentication plugins,
@@ -2338,7 +2448,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
grant_version++; /* Privileges updated */
const Host_table& host_table= tables.host_table();
- init_sql_alloc(&acl_memroot, "ACL", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(key_memory_acl_mem, &acl_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
{
if (host_table.init_read_record(&read_record_info))
@@ -2415,7 +2525,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
user.sort= get_magic_sort("hu", user.host.hostname, user.user.str);
user.hostname_length= safe_strlen(user.host.hostname);
- my_init_dynamic_array(&user.role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0));
+ my_init_dynamic_array(key_memory_acl_mem, &user.role_grants,
+ sizeof(ACL_ROLE *), 0, 8, MYF(0));
user.account_locked= user_table.get_account_locked();
@@ -2433,7 +2544,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
entry->role_grants = user.role_grants;
- my_init_dynamic_array(&entry->parent_grantee,
+ my_init_dynamic_array(key_memory_acl_mem, &entry->parent_grantee,
sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
my_hash_insert(&acl_roles, (uchar *)entry);
@@ -2578,7 +2689,7 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
DBUG_RETURN(TRUE);
MEM_ROOT temp_root;
- init_alloc_root(&temp_root, "ACL_tmp", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_alloc_root(key_memory_acl_mem, &temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
while (!(read_record_info.read_record()))
{
char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host()));
@@ -2697,15 +2808,16 @@ bool acl_reload(THD *thd)
old_acl_roles_mappings= acl_roles_mappings;
old_acl_proxy_users= acl_proxy_users;
old_acl_dbs= acl_dbs;
- my_init_dynamic_array(&acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
- my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
- acl_dbs.init(50, 100);
- my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
- my_hash_init2(&acl_roles,50, &my_charset_utf8_bin,
+ my_init_dynamic_array(key_memory_acl_mem, &acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
+ my_init_dynamic_array(key_memory_acl_mem, &acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
+ acl_dbs.init(key_memory_acl_mem, 50, 100);
+ my_init_dynamic_array(key_memory_acl_mem, &acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
+ my_hash_init2(key_memory_acl_mem, &acl_roles,50, &my_charset_utf8mb3_bin,
0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
(void (*)(void *))free_acl_role, 0);
- my_hash_init2(&acl_roles_mappings, 50, &my_charset_utf8_bin, 0, 0, 0,
- (my_hash_get_key) acl_role_map_get_key, 0, 0, 0);
+ my_hash_init2(key_memory_acl_mem, &acl_roles_mappings, 50,
+ &my_charset_utf8mb3_bin, 0, 0, 0, (my_hash_get_key)
+ acl_role_map_get_key, 0, 0, 0);
old_mem= acl_memroot;
delete_dynamic(&acl_wild_hosts);
my_hash_free(&acl_check_hosts);
@@ -2758,9 +2870,9 @@ end:
privilege mask
*/
-static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
+static privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field)
{
- ulong access_bits=0,bit;
+ ulonglong access_bits=0,bit;
char buff[2];
String res(buff,sizeof(buff),&my_charset_latin1);
Field **pos;
@@ -2775,7 +2887,7 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
}
if (next_field)
*next_field=fieldnr;
- return access_bits;
+ return ALL_KNOWN_ACL & access_bits;
}
@@ -2979,7 +3091,7 @@ bool acl_getroot(Security_context *sctx, const char *user, const char *host,
mysql_mutex_lock(&acl_cache->lock);
- sctx->db_access= 0;
+ sctx->db_access= NO_ACL;
if (host[0]) // User, not Role
{
@@ -3041,9 +3153,10 @@ static ACL_USER *find_user_or_anon(const char *host, const char *user, const cha
user, host, ip, NULL, FALSE, NULL);
}
-static int check_user_can_set_role(THD *thd, const char *user, const char *host,
- const char *ip, const char *rolename,
- ulonglong *access)
+
+static int check_user_can_set_role(THD *thd, const char *user,
+ const char *host, const char *ip,
+ const char *rolename, privilege_t *access)
{
ACL_ROLE *role;
ACL_USER_BASE *acl_user_base;
@@ -3157,18 +3270,18 @@ end:
}
-int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access)
+int acl_check_setrole(THD *thd, const char *rolename, privilege_t *access)
{
return check_user_can_set_role(thd, thd->security_ctx->priv_user,
thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
}
-int acl_setrole(THD *thd, const char *rolename, ulonglong access)
+int acl_setrole(THD *thd, const char *rolename, privilege_t access)
{
/* merge the privileges */
Security_context *sctx= thd->security_ctx;
- sctx->master_access= static_cast<ulong>(access);
+ sctx->master_access= access;
if (thd->db.str)
sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
@@ -3193,7 +3306,8 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length,
return (uchar*) buff->host.hostname;
}
-static void acl_update_role(const char *rolename, ulong privileges)
+
+static void acl_update_role(const char *rolename, const privilege_t privileges)
{
ACL_ROLE *role= find_acl_role(rolename);
if (role)
@@ -3203,7 +3317,7 @@ static void acl_update_role(const char *rolename, ulong privileges)
ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo,
const Account_options &options,
- const ulong privileges)
+ const privilege_t privileges)
{
user= safe_lexcstrdup_root(&acl_memroot, combo.user);
update_hostname(&host, safe_strdup_root(&acl_memroot, combo.host.str));
@@ -3211,14 +3325,14 @@ ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo,
sort= get_magic_sort("hu", host.hostname, user.str);
password_last_changed= thd->query_start();
password_lifetime= -1;
- my_init_dynamic_array(&role_grants, sizeof(ACL_USER *), 0, 8, MYF(0));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &role_grants, sizeof(ACL_USER *), 0, 8, MYF(0));
}
static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
const LEX_USER &combo,
const Account_options &options,
- const ulong privileges)
+ const privilege_t privileges)
{
if (nauth)
{
@@ -3290,22 +3404,23 @@ static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
}
-static void acl_insert_role(const char *rolename, ulong privileges)
+static void acl_insert_role(const char *rolename, privilege_t privileges)
{
ACL_ROLE *entry;
mysql_mutex_assert_owner(&acl_cache->lock);
entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
- my_init_dynamic_array(&entry->parent_grantee,
+ my_init_dynamic_array(key_memory_acl_mem, &entry->parent_grantee,
sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
- my_init_dynamic_array(&entry->role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0));
+ my_init_dynamic_array(key_memory_acl_mem, &entry->role_grants,
+ sizeof(ACL_ROLE *), 0, 8, MYF(0));
my_hash_insert(&acl_roles, (uchar *)entry);
}
static bool acl_update_db(const char *user, const char *host, const char *db,
- ulong privileges)
+ privilege_t privileges)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -3357,7 +3472,7 @@ static bool 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)
+ const privilege_t privileges)
{
ACL_DB acl_db;
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -3378,10 +3493,10 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
acl_cache is not used if db_is_pattern is set.
*/
-ulong acl_get(const char *host, const char *ip,
- const char *user, const char *db, my_bool db_is_pattern)
+privilege_t acl_get(const char *host, const char *ip,
+ const char *user, const char *db, my_bool db_is_pattern)
{
- ulong host_access= ~(ulong)0, db_access= 0;
+ privilege_t host_access(ALL_KNOWN_ACL), db_access(NO_ACL);
uint i;
size_t key_length;
char key[ACL_KEY_LENGTH],*tmp_db,*end;
@@ -3392,7 +3507,7 @@ ulong acl_get(const char *host, const char *ip,
end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
if (end >= key + sizeof(key)) // db name was truncated
- DBUG_RETURN(0); // no privileges for an invalid db name
+ DBUG_RETURN(NO_ACL); // no privileges for an invalid db name
if (lower_case_table_names)
{
@@ -3406,7 +3521,7 @@ ulong acl_get(const char *host, const char *ip,
{
db_access=entry->access;
mysql_mutex_unlock(&acl_cache->lock);
- DBUG_PRINT("exit", ("access: 0x%lx", db_access));
+ DBUG_PRINT("exit", ("access: 0x%llx", (longlong) db_access));
DBUG_RETURN(db_access);
}
@@ -3429,7 +3544,7 @@ ulong acl_get(const char *host, const char *ip,
/*
No host specified for user. Get hostdata from host table
*/
- host_access=0; // Host must be found
+ host_access= NO_ACL; // Host must be found
for (i=0 ; i < acl_hosts.elements ; i++)
{
ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
@@ -3445,7 +3560,8 @@ ulong acl_get(const char *host, const char *ip,
exit:
/* Save entry in cache for quick retrieval */
if (!db_is_pattern &&
- (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
+ (entry= (acl_entry*) my_malloc(key_memory_acl_cache,
+ sizeof(acl_entry)+key_length, MYF(MY_WME))))
{
entry->access=(db_access & host_access);
DBUG_ASSERT(key_length < 0xffff);
@@ -3454,7 +3570,7 @@ exit:
acl_cache->add(entry);
}
mysql_mutex_unlock(&acl_cache->lock);
- DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
+ DBUG_PRINT("exit", ("access: 0x%llx", (longlong) (db_access & host_access)));
DBUG_RETURN(db_access & host_access);
}
@@ -3469,9 +3585,10 @@ exit:
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),
+ (void) my_init_dynamic_array(key_memory_acl_mem, &acl_wild_hosts,
+ sizeof(struct acl_host_and_ip),
acl_users.elements, 1, MYF(0));
- (void) my_hash_init(&acl_check_hosts,system_charset_info,
+ (void) my_hash_init(key_memory_acl_mem, &acl_check_hosts,system_charset_info,
acl_users.elements, 0, 0,
(my_hash_get_key) check_get_key, 0, 0);
if (!allow_all_hosts)
@@ -4236,7 +4353,7 @@ bool hostname_requires_resolving(const char *hostname)
if (hostname == my_localhost ||
(hostname_len == localhost_len &&
- !my_strnncoll(system_charset_info,
+ !system_charset_info->strnncoll(
(const uchar *) hostname, hostname_len,
(const uchar *) my_localhost, strlen(my_localhost))))
{
@@ -4347,7 +4464,7 @@ static bool test_if_create_new_users(THD *thd)
if (!create_new_users)
{
TABLE_LIST tl;
- ulong db_access;
+ privilege_t db_access(NO_ACL);
tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[USER_TABLE],
NULL, TL_WRITE);
create_new_users= 1;
@@ -4372,8 +4489,9 @@ static bool test_if_create_new_users(THD *thd)
static USER_AUTH auth_no_password;
static int replace_user_table(THD *thd, const User_table &user_table,
- LEX_USER * const combo, ulong rights,
- const bool revoke_grant, const bool can_create_user,
+ LEX_USER * const combo, privilege_t rights,
+ const bool revoke_grant,
+ const bool can_create_user,
const bool no_auto_create)
{
int error = -1;
@@ -4631,10 +4749,11 @@ end:
static int replace_db_table(TABLE *table, const char *db,
const LEX_USER &combo,
- ulong rights, const bool revoke_grant)
+ privilege_t rights, const bool revoke_grant)
{
uint i;
- ulong priv,store_rights;
+ ulonglong priv;
+ privilege_t store_rights(NO_ACL);
bool old_row_exists=0;
int error;
char what= revoke_grant ? 'N' : 'Y';
@@ -5040,17 +5159,17 @@ class GRANT_COLUMN :public Sql_alloc
{
public:
char *column;
- ulong rights;
- ulong init_rights;
+ privilege_t rights;
+ privilege_t init_rights;
uint key_length;
- GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y)
+ GRANT_COLUMN(String &c, privilege_t y) :rights (y), init_rights(y)
{
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) { }
+ rights (source->rights), init_rights(NO_ACL), key_length(source->key_length) { }
};
@@ -5066,37 +5185,43 @@ 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 */
+ privilege_t privs;
+ privilege_t init_privs; /* privileges found in physical table */
ulonglong sort;
size_t key_length;
GRANT_NAME(const char *h, const char *d,const char *u,
- const char *t, ulong p, bool is_routine);
+ const char *t, privilege_t p, bool is_routine);
GRANT_NAME (TABLE *form, bool is_routine);
virtual ~GRANT_NAME() {};
- virtual bool ok() { return privs != 0; }
+ virtual bool ok() { return privs != NO_ACL; }
void set_user_details(const char *h, const char *d,
const char *u, const char *t,
bool is_routine);
};
+static privilege_t get_access_value_from_val_int(Field *field)
+{
+ return privilege_t(ALL_KNOWN_ACL & (ulonglong) field->val_int());
+}
+
+
class GRANT_TABLE :public GRANT_NAME
{
public:
- ulong cols;
- ulong init_cols; /* privileges found in physical table */
+ privilege_t cols;
+ privilege_t init_cols; /* privileges found in physical table */
HASH hash_columns;
GRANT_TABLE(const char *h, const char *d,const char *u,
- const char *t, ulong p, ulong c);
+ const char *t, privilege_t p, privilege_t c);
GRANT_TABLE (TABLE *form, TABLE *col_privs);
~GRANT_TABLE();
- bool ok() { return privs != 0 || cols != 0; }
+ bool ok() { return privs != NO_ACL || cols != NO_ACL; }
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);
+ my_hash_init2(key_memory_acl_memex, &hash_columns, 4, system_charset_info,
+ 0, 0, 0, (my_hash_get_key) get_key_column, 0, 0, 0);
}
};
@@ -5127,15 +5252,15 @@ void GRANT_NAME::set_user_details(const char *h, const char *d,
}
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
- const char *t, ulong p, bool is_routine)
+ const char *t, privilege_t p, bool is_routine)
:db(0), tname(0), privs(p), init_privs(p)
{
set_user_details(h, d, u, t, is_routine);
}
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)
+ const char *t, privilege_t p, privilege_t c)
+ :GRANT_NAME(h,d,u,t,p, FALSE), cols(c), init_cols(NO_ACL)
{
init_hash();
}
@@ -5145,6 +5270,7 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
to 0
*/
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
+ :privs(NO_ACL), init_privs(NO_ACL)
{
user= safe_str(get_field(&grant_memroot,form->field[2]));
@@ -5160,7 +5286,6 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
if (!db || !tname)
{
/* Wrong table row; Ignore it */
- privs= 0;
return; /* purecov: inspected */
}
sort= get_magic_sort("hdu", host.hostname, db, user);
@@ -5175,14 +5300,14 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
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 = get_access_value_from_val_int(form->field[6]);
privs = fix_rights_for_table(privs);
init_privs= privs;
}
GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
- :GRANT_NAME(form, FALSE)
+ :GRANT_NAME(form, FALSE), cols(NO_ACL), init_cols(NO_ACL)
{
uchar key[MAX_KEY_LENGTH];
@@ -5190,10 +5315,10 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
{
/* Wrong table row; Ignore it */
my_hash_clear(&hash_columns); /* allow for destruction */
- cols= 0;
+ cols= NO_ACL;
return;
}
- cols= (ulong) form->field[7]->val_int();
+ cols= get_access_value_from_val_int(form->field[7]);
cols= fix_rights_for_column(cols);
/*
Initial columns privileges are the same as column privileges on creation.
@@ -5225,8 +5350,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
if (col_privs->file->ha_index_init(0, 1))
{
- cols= 0;
- init_cols= 0;
+ cols= NO_ACL;
+ init_cols= NO_ACL;
return;
}
@@ -5234,8 +5359,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
(key_part_map)15,
HA_READ_KEY_EXACT))
{
- cols= 0; /* purecov: deadcode */
- init_cols= 0;
+ cols= NO_ACL; /* purecov: deadcode */
+ init_cols= NO_ACL;
col_privs->file->ha_index_end();
return;
}
@@ -5245,18 +5370,18 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
GRANT_COLUMN *mem_check;
/* As column name is a string, we don't have to supply a buffer */
res=col_privs->field[4]->val_str(&column_name);
- ulong priv= (ulong) col_privs->field[6]->val_int();
+ privilege_t priv= get_access_value_from_val_int(col_privs->field[6]);
if (!(mem_check = new GRANT_COLUMN(*res,
fix_rights_for_column(priv))))
{
/* Don't use this entry */
- privs= cols= init_privs= init_cols=0; /* purecov: deadcode */
+ privs= cols= init_privs= init_cols= NO_ACL; /* purecov: deadcode */
return; /* purecov: deadcode */
}
if (my_hash_insert(&hash_columns, (uchar *) mem_check))
{
/* Invalidate this entry */
- privs= cols= init_privs= init_cols=0;
+ privs= cols= init_privs= init_cols= NO_ACL;
return;
}
} while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
@@ -5362,8 +5487,7 @@ column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
{
if (!my_hash_inited(&t->hash_columns))
return (GRANT_COLUMN*) 0;
- return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
- (uchar*) cname, length);
+ return (GRANT_COLUMN*)my_hash_search(&t->hash_columns, (uchar*)cname, length);
}
@@ -5371,7 +5495,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
TABLE *table, const LEX_USER &combo,
List <LEX_COLUMN> &columns,
const char *db, const char *table_name,
- ulong rights, bool revoke_grant)
+ privilege_t rights, bool revoke_grant)
{
int result=0;
uchar key[MAX_KEY_LENGTH];
@@ -5400,6 +5524,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
List_iterator <LEX_COLUMN> iter(columns);
class LEX_COLUMN *column;
+
int error= table->file->ha_index_init(0, 1);
if (unlikely(error))
{
@@ -5409,7 +5534,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
while ((column= iter++))
{
- ulong privileges= column->rights;
+ privilege_t privileges= column->rights;
bool old_row_exists=0;
uchar user_key[MAX_KEY_LENGTH];
@@ -5441,7 +5566,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
}
else
{
- ulong tmp= (ulong) table->field[6]->val_int();
+ privilege_t tmp= get_access_value_from_val_int(table->field[6]);
tmp=fix_rights_for_column(tmp);
if (revoke_grant)
@@ -5511,7 +5636,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
/* Scan through all rows with the same host,db,user and table */
do
{
- ulong privileges = (ulong) table->field[6]->val_int();
+ privilege_t privileges = get_access_value_from_val_int(table->field[6]);
privileges=fix_rights_for_column(privileges);
store_record(table,record[1]);
@@ -5524,7 +5649,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
privileges&= ~rights;
table->field[6]->store((longlong)
- get_rights_for_column(privileges), TRUE);
+ get_rights_for_column(privileges), TRUE);
table->field[4]->val_str(&column_name);
grant_column = column_hash_search(g_t,
column_name.ptr(),
@@ -5588,13 +5713,13 @@ static inline void get_grantor(THD *thd, char *grantor)
static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
TABLE *table, const LEX_USER &combo,
const char *db, const char *table_name,
- ulong rights, ulong col_rights,
+ privilege_t rights, privilege_t col_rights,
bool revoke_grant)
{
char grantor[USER_HOST_BUFF_SIZE];
int old_row_exists = 1;
int error=0;
- ulong store_table_rights, store_col_rights;
+ privilege_t store_table_rights(NO_ACL), store_col_rights(NO_ACL);
uchar user_key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_table_table");
@@ -5650,10 +5775,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
store_col_rights= get_rights_for_column(col_rights);
if (old_row_exists)
{
- ulong j,k;
store_record(table,record[1]);
- j = (ulong) table->field[6]->val_int();
- k = (ulong) table->field[7]->val_int();
+ privilege_t j= get_access_value_from_val_int(table->field[6]);
+ privilege_t k= get_access_value_from_val_int(table->field[7]);
if (revoke_grant)
{
@@ -5721,12 +5845,11 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
TABLE *table, const LEX_USER &combo,
const char *db, const char *routine_name,
const Sp_handler *sph,
- ulong rights, bool revoke_grant)
+ privilege_t rights, bool revoke_grant)
{
char grantor[USER_HOST_BUFF_SIZE];
int old_row_exists= 1;
int error=0;
- ulong store_proc_rights;
HASH *hash= sph->get_priv_hash();
DBUG_ENTER("replace_routine_table");
@@ -5752,7 +5875,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
*/
table->use_all_columns();
- restore_record(table, s->default_values); // Get empty record
+ restore_record(table, s->default_values); // Get empty record
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
@@ -5784,12 +5907,11 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
restore_record(table,record[1]); // Get saved record
}
- store_proc_rights= get_rights_for_procedure(rights);
+ privilege_t store_proc_rights= get_rights_for_procedure(rights);
if (old_row_exists)
{
- ulong j;
store_record(table,record[1]);
- j= (ulong) table->field[6]->val_int();
+ privilege_t j= get_access_value_from_val_int(table->field[6]);
if (revoke_grant)
{
@@ -5882,19 +6004,19 @@ struct PRIVS_TO_MERGE
};
-static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
+static enum PRIVS_TO_MERGE::what sp_privs_to_merge(enum_sp_type type)
{
switch (type) {
- case TYPE_ENUM_FUNCTION:
+ case SP_TYPE_FUNCTION:
return PRIVS_TO_MERGE::FUNC;
- case TYPE_ENUM_PROCEDURE:
+ case SP_TYPE_PROCEDURE:
return PRIVS_TO_MERGE::PROC;
- case TYPE_ENUM_PACKAGE:
+ case SP_TYPE_PACKAGE:
return PRIVS_TO_MERGE::PACKAGE_SPEC;
- case TYPE_ENUM_PACKAGE_BODY:
+ case SP_TYPE_PACKAGE_BODY:
return PRIVS_TO_MERGE::PACKAGE_BODY;
- case TYPE_ENUM_TRIGGER:
- case TYPE_ENUM_PROXY:
+ case SP_TYPE_EVENT:
+ case SP_TYPE_TRIGGER:
break;
}
DBUG_ASSERT(0);
@@ -6194,7 +6316,7 @@ typedef Hash_set<ACL_ROLE> role_hash_t;
static bool merge_role_global_privileges(ACL_ROLE *grantee)
{
- ulong old= grantee->access;
+ privilege_t old= grantee->access;
grantee->access= grantee->initial_role_access;
DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
@@ -6225,7 +6347,7 @@ static int db_name_sort(const int *db1, const int *db2)
2 - ACL_DB was added
4 - ACL_DB was deleted
*/
-static int update_role_db(int merged, int first, ulong access,
+static int update_role_db(int merged, int first, privilege_t access,
const char *role)
{
if (first < 0)
@@ -6250,12 +6372,12 @@ static int update_role_db(int merged, int first, ulong access,
acl_db.host.ip= acl_db.host.ip_mask= 0;
acl_db.db= acl_dbs.at(first).db;
acl_db.access= access;
- acl_db.initial_access= 0;
+ acl_db.initial_access= NO_ACL;
acl_db.sort= get_magic_sort("hdu", "", acl_db.db, role);
acl_dbs.push(acl_db);
return 2;
}
- else if (access == 0)
+ else if (access == NO_ACL)
{
/*
there is ACL_DB but the role has no db privileges granted
@@ -6289,7 +6411,7 @@ static int update_role_db(int merged, int first, ulong access,
static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
role_hash_t *rhash)
{
- Dynamic_array<int> dbs;
+ Dynamic_array<int> dbs(PSI_INSTRUMENT_MEM);
/*
Supposedly acl_dbs can be huge, but only a handful of db grants
@@ -6317,14 +6439,15 @@ static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
is not necessarily the first and may be not present at all.
*/
int first= -1, merged= -1;
- ulong access= 0, update_flags= 0;
+ privilege_t access(NO_ACL);
+ ulong update_flags= 0;
for (int *p= dbs.front(); p <= dbs.back(); p++)
{
if (first<0 || (!dbname && strcmp(acl_dbs.at(p[0]).db, acl_dbs.at(p[-1]).db)))
{ // new db name series
update_flags|= update_role_db(merged, first, access, grantee->user.str);
merged= -1;
- access= 0;
+ access= NO_ACL;
first= *p;
}
if (strcmp(acl_dbs.at(*p).user, grantee->user.str) == 0)
@@ -6382,7 +6505,7 @@ static int update_role_columns(GRANT_TABLE *merged,
GRANT_TABLE **cur, GRANT_TABLE **last)
{
- ulong rights __attribute__((unused))= 0;
+ privilege_t rights __attribute__((unused)) (NO_ACL);
int changed= 0;
if (!merged->cols)
{
@@ -6452,7 +6575,8 @@ static int update_role_columns(GRANT_TABLE *merged,
*/
static int update_role_table_columns(GRANT_TABLE *merged,
GRANT_TABLE **first, GRANT_TABLE **last,
- ulong privs, ulong cols, const char *role)
+ privilege_t privs, privilege_t cols,
+ const char *role)
{
if (!first)
return 0;
@@ -6468,12 +6592,12 @@ static int update_role_table_columns(GRANT_TABLE *merged,
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;
+ merged->init_privs= merged->init_cols= NO_ACL;
update_role_columns(merged, first, last);
my_hash_insert(&column_priv_hash,(uchar*) merged);
return 2;
}
- else if ((privs | cols) == 0)
+ else if ((privs | cols) == NO_ACL)
{
/*
there is GRANT_TABLE object but the role has no table or column
@@ -6506,7 +6630,7 @@ static int update_role_table_columns(GRANT_TABLE *merged,
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;
+ Dynamic_array<GRANT_TABLE *> grants(PSI_INSTRUMENT_MEM);
DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
/*
@@ -6528,7 +6652,8 @@ static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
grants.sort(table_name_sort);
GRANT_TABLE **first= NULL, *merged= NULL, **cur;
- ulong privs= 0, cols= 0, update_flags= 0;
+ privilege_t privs(NO_ACL), cols(NO_ACL);
+ ulong update_flags= 0;
for (cur= grants.front(); cur <= grants.back(); cur++)
{
if (!first ||
@@ -6538,7 +6663,7 @@ static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
update_flags|= update_role_table_columns(merged, first, cur,
privs, cols, grantee->user.str);
merged= NULL;
- privs= cols= 0;
+ privs= cols= NO_ACL;
first= cur;
}
if (strcmp(cur[0]->user, grantee->user.str) == 0)
@@ -6581,7 +6706,7 @@ static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
4 - GRANT_NAME was deleted
*/
static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
- ulong privs, const char *role, HASH *hash)
+ privilege_t privs, const char *role, HASH *hash)
{
if (!first)
return 0;
@@ -6597,11 +6722,11 @@ static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
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
+ merged->init_privs= NO_ACL; // all privs are inherited
my_hash_insert(hash, (uchar *)merged);
return 2;
}
- else if (privs == 0)
+ else if (privs == NO_ACL)
{
/*
there is GRANT_NAME but the role has no privileges granted
@@ -6634,7 +6759,7 @@ static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
- Dynamic_array<GRANT_NAME *> grants;
+ Dynamic_array<GRANT_NAME *> grants(PSI_INSTRUMENT_MEM);
/* first, collect routine privileges granted to roles in question */
for (uint i=0 ; i < hash->records ; i++)
@@ -6652,7 +6777,7 @@ static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
grants.sort(routine_name_sort);
GRANT_NAME **first= NULL, *merged= NULL;
- ulong privs= 0 ;
+ privilege_t privs(NO_ACL);
for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
{
if (!first ||
@@ -6662,7 +6787,7 @@ static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
update_flags|= update_role_routines(merged, first, privs,
grantee->user.str, hash);
merged= NULL;
- privs= 0;
+ privs= NO_ACL;
first= cur;
}
if (strcmp(cur[0]->user, grantee->user.str) == 0)
@@ -6695,7 +6820,7 @@ static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
grantee->counter= 1; // Mark the grantee as merged.
/* if we'll do db/table/routine privileges, create a hash of role names */
- role_hash_t role_hash(role_key);
+ role_hash_t role_hash(PSI_INSTRUMENT_MEM, role_key);
if (data->what != PRIVS_TO_MERGE::GLOBAL)
{
role_hash.insert(grantee);
@@ -6786,10 +6911,10 @@ static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
List <LEX_USER> &user_list,
- List <LEX_COLUMN> &columns, ulong rights,
+ List <LEX_COLUMN> &columns, privilege_t rights,
bool revoke_grant)
{
- ulong column_priv= 0;
+ privilege_t column_priv(NO_ACL);
int result;
List_iterator <LEX_USER> str_list (user_list);
LEX_USER *Str, *tmp_Str;
@@ -6909,7 +7034,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* Create user if needed */
error= copy_and_check_auth(Str, tmp_Str, thd) ||
replace_user_table(thd, tables.user_table(), Str,
- 0, revoke_grant, create_new_users,
+ NO_ACL, revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER));
if (unlikely(error))
@@ -6962,7 +7087,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
grant_column->rights&= ~(column->rights | rights);
}
/* scan trough all columns to get new column grant */
- column_priv= 0;
+ column_priv= NO_ACL;
for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
{
grant_column= (GRANT_COLUMN*)
@@ -7039,7 +7164,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
const Sp_handler *sph,
- List <LEX_USER> &user_list, ulong rights,
+ List <LEX_USER> &user_list, privilege_t rights,
bool revoke_grant, bool write_to_binlog)
{
List_iterator <LEX_USER> str_list (user_list);
@@ -7089,7 +7214,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
/* Create user if needed */
if (copy_and_check_auth(Str, tmp_Str, thd) ||
replace_user_table(thd, tables.user_table(), Str,
- 0, revoke_grant, create_new_users,
+ NO_ACL, revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER)))
{
@@ -7360,7 +7485,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
user_combo.user = username;
if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
- replace_user_table(thd, tables.user_table(), &user_combo, 0,
+ replace_user_table(thd, tables.user_table(), &user_combo, NO_ACL,
false, create_new_user,
no_auto_create_user))
{
@@ -7473,7 +7598,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
- ulong rights, bool revoke_grant, bool is_proxy)
+ privilege_t rights, bool revoke_grant, bool is_proxy)
{
List_iterator <LEX_USER> str_list (list);
LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
@@ -7532,13 +7657,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (copy_and_check_auth(Str, tmp_Str, thd) ||
replace_user_table(thd, tables.user_table(), Str,
- (!db ? rights : 0), revoke_grant, create_new_users,
+ (!db ? rights : NO_ACL),
+ revoke_grant, create_new_users,
MY_TEST(thd->variables.sql_mode &
MODE_NO_AUTO_CREATE_USER)))
result= true;
else if (db)
{
- ulong db_rights= rights & DB_ACLS;
+ privilege_t db_rights(rights & DB_ACLS);
if (db_rights == rights)
{
if (replace_db_table(tables.db_table().table(), db, *Str, db_rights,
@@ -7642,23 +7768,26 @@ static bool grant_load(THD *thd,
TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT *save_mem_root= thd->mem_root;
- sql_mode_t old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("grant_load");
- thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
-
- (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);
- (void) my_hash_init(&package_spec_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
- (void) my_hash_init(&package_body_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
- init_sql_alloc(&grant_memroot, "GRANT", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
+
+ (void) my_hash_init(key_memory_acl_memex, &column_priv_hash,
+ &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
+ get_grant_table, (my_hash_free_key) free_grant_table, 0);
+ (void) my_hash_init(key_memory_acl_memex, &proc_priv_hash,
+ &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
+ get_grant_table, 0,0);
+ (void) my_hash_init(key_memory_acl_memex, &func_priv_hash,
+ &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
+ get_grant_table, 0,0);
+ (void) my_hash_init(key_memory_acl_memex, &package_spec_priv_hash,
+ &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
+ get_grant_table, 0,0);
+ (void) my_hash_init(key_memory_acl_memex, &package_body_priv_hash,
+ &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
+ get_grant_table, 0,0);
+ init_sql_alloc(key_memory_acl_mem, &grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
t_table= tables_priv.table();
c_table= columns_priv.table();
@@ -7739,7 +7868,7 @@ static bool grant_load(THD *thd,
continue;
}
}
- stored_procedure_type type= (stored_procedure_type)procs_priv.routine_type()->val_int();
+ enum_sp_type type= (enum_sp_type)procs_priv.routine_type()->val_int();
const Sp_handler *sph= Sp_handler::handler(type);
if (!sph || !(hash= sph->get_priv_hash()))
{
@@ -7770,7 +7899,6 @@ end_unlock:
t_table->file->ha_index_end();
thd->mem_root= save_mem_root;
end_index_init:
- thd->variables.sql_mode= old_sql_mode;
DBUG_RETURN(return_val);
}
@@ -7914,14 +8042,14 @@ bool grant_reload(THD *thd)
*/
-bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
+bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
bool any_combination_will_do, uint number, bool no_errors)
{
TABLE_LIST *tl;
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
Security_context *sctx= thd->security_ctx;
uint i;
- ulong original_want_access= want_access;
+ privilege_t original_want_access(want_access);
bool locked= 0;
GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL;
@@ -7955,7 +8083,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
TABLE_LIST *const t_ref=
tl->correspondent_table ? tl->correspondent_table : tl;
sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
- ulong orig_want_access= original_want_access;
+ privilege_t orig_want_access(original_want_access);
/*
If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
@@ -7988,15 +8116,9 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
switch(access->check(orig_want_access, &t_ref->grant.privilege))
{
case ACL_INTERNAL_ACCESS_GRANTED:
- /*
- Currently,
- - the information_schema does not subclass ACL_internal_table_access,
- there are no per table privilege checks for I_S,
- - the performance schema does use per tables checks, but at most
- returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
- so this branch is not used.
- */
- DBUG_ASSERT(0);
+ t_ref->grant.privilege|= orig_want_access;
+ t_ref->grant.want_privilege= NO_ACL;
+ continue;
case ACL_INTERNAL_ACCESS_DENIED:
goto err;
case ACL_INTERNAL_ACCESS_CHECK_GRANT:
@@ -8026,7 +8148,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
clause, or an INFORMATION_SCHEMA table, drop the request for
a privilege.
*/
- t_ref->grant.want_privilege= 0;
+ t_ref->grant.want_privilege= NO_ACL;
}
continue;
}
@@ -8040,7 +8162,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if user has CREATE_TMP_ACL.
*/
t_ref->grant.privilege|= TMP_TABLE_ACLS;
- t_ref->grant.want_privilege= 0;
+ t_ref->grant.want_privilege= NO_ACL;
continue;
}
@@ -8077,15 +8199,15 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
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 ? grant_table->privs : 0;
- t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
+ t_ref->grant.privilege|= grant_table ? grant_table->privs : NO_ACL;
+ t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : NO_ACL;
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 ? grant_table->cols : 0) |
- (grant_table_role ? grant_table_role->cols : 0) |
+ if ((want_access&= ~((grant_table ? grant_table->cols : NO_ACL) |
+ (grant_table_role ? grant_table_role->cols : NO_ACL) |
t_ref->grant.privilege)))
{
goto err; // impossible
@@ -8115,7 +8237,7 @@ err:
static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
- uint length, ulong *want_access)
+ uint length, privilege_t *want_access)
{
if (grant_table)
{
@@ -8151,9 +8273,10 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
const char *name, size_t length, Security_context *sctx)
{
- ulong want_access= grant->want_privilege & ~grant->privilege;
+ privilege_t want_access(grant->want_privilege & ~grant->privilege);
DBUG_ENTER("check_grant_column");
- DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access));
+ DBUG_PRINT("enter", ("table: %s want_access: %llx",
+ table_name, (longlong) want_access));
if (!want_access)
DBUG_RETURN(0); // Already checked
@@ -8233,7 +8356,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
if (table_ref->view || table_ref->field_translation)
{
/* View or derived information schema table. */
- ulong view_privs;
+ privilege_t view_privs(NO_ACL);
grant= &(table_ref->grant);
db_name= table_ref->view_db.str;
table_name= table_ref->view_name.str;
@@ -8284,11 +8407,11 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
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, privilege_t want_access_arg,
Field_iterator_table_ref *fields)
{
Security_context *sctx= thd->security_ctx;
- ulong UNINIT_VAR(want_access);
+ privilege_t want_access(NO_ACL);
const char *table_name= NULL;
const char* db_name;
GRANT_INFO *grant;
@@ -8341,7 +8464,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
if (want_access)
{
- ulong have_access= 0;
+ privilege_t have_access(NO_ACL);
if (grant_table)
{
GRANT_COLUMN *grant_column=
@@ -8508,7 +8631,7 @@ bool check_grant_db(THD *thd, const char *db)
1 Error: User did not have the requested privielges
****************************************************************************/
-bool check_grant_routine(THD *thd, ulong want_access,
+bool check_grant_routine(THD *thd, privilege_t want_access,
TABLE_LIST *procs, const Sp_handler *sph,
bool no_errors)
{
@@ -8611,9 +8734,8 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
Functions to retrieve the grant for a table/column (for SHOW functions)
*****************************************************************************/
-ulong get_table_grant(THD *thd, TABLE_LIST *table)
+privilege_t get_table_grant(THD *thd, TABLE_LIST *table)
{
- ulong privilege;
Security_context *sctx= thd->security_ctx;
const char *db = table->db.str ? table->db.str : thd->db.str;
GRANT_TABLE *grant_table;
@@ -8637,7 +8759,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
table->grant.privilege|= grant_table->privs;
if (grant_table_role)
table->grant.privilege|= grant_table_role->privs;
- privilege= table->grant.privilege;
+ privilege_t privilege(table->grant.privilege);
mysql_rwlock_unlock(&LOCK_grant);
return privilege;
}
@@ -8661,14 +8783,14 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
The access priviliges for the field db_name.table_name.field_name
*/
-ulong get_column_grant(THD *thd, GRANT_INFO *grant,
- const char *db_name, const char *table_name,
- const char *field_name)
+privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *field_name)
{
GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column;
- ulong priv= 0;
+ privilege_t priv(NO_ACL);
mysql_rwlock_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
@@ -8847,19 +8969,34 @@ static const char *command_array[]=
"SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
"SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
"ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
- "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
+ "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "BINLOG MONITOR",
"CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
- "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE",
- "DELETE HISTORY"
+ "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", "DELETE HISTORY",
+ "SET USER", "FEDERATED ADMIN", "CONNECTION ADMIN", "READ_ONLY ADMIN",
+ "REPLICATION SLAVE ADMIN", "REPLICATION MASTER ADMIN", "BINLOG ADMIN",
+ "BINLOG REPLAY"
};
static uint command_lengths[]=
{
- 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
- 14, 13, 11, 5, 7, 17, 14,
+ 6, 6, 6, 6, 6, 4, 6,
+ 8, 7, 4, 5, 10, 5,
+ 5, 14, 5, 23,
+ 11, 7, 17, 14,
+ 11, 9, 14, 13,
+ 11, 5, 7, 17, 14,
+ 8, 15, 16, 15,
+ 23, 24, 12,
+ 13
};
+static_assert(array_elements(command_array) == PRIVILEGE_T_MAX_BIT + 1,
+ "The definition of command_array does not match privilege_t");
+static_assert(array_elements(command_lengths) == PRIVILEGE_T_MAX_BIT + 1,
+ "The definition of command_lengths does not match privilege_t");
+
+
static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
{
char buff[1024];
@@ -9275,7 +9412,7 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
char *buff, size_t buffsize)
{
uint counter;
- ulong want_access;
+ privilege_t want_access(NO_ACL);
Protocol *protocol= thd->protocol;
String global(buff, buffsize, system_charset_info);
@@ -9293,7 +9430,8 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
else
{
bool found=0;
- ulong j,test_access= want_access & ~GRANT_ACL;
+ ulonglong j;
+ privilege_t test_access(want_access & ~GRANT_ACL);
for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
{
if (test_access & j)
@@ -9340,7 +9478,7 @@ static bool show_database_privileges(THD *thd, const char *username,
const char *hostname,
char *buff, size_t buffsize)
{
- ulong want_access;
+ privilege_t want_access(NO_ACL);
Protocol *protocol= thd->protocol;
for (uint i=0 ; i < acl_dbs.elements() ; i++)
@@ -9382,7 +9520,8 @@ static bool show_database_privileges(THD *thd, const char *username,
else
{
int found=0, cnt;
- ulong j,test_access= want_access & ~GRANT_ACL;
+ ulonglong j;
+ privilege_t test_access(want_access & ~GRANT_ACL);
for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
{
if (test_access & j)
@@ -9439,8 +9578,8 @@ static bool show_table_and_column_privileges(THD *thd, const char *username,
if (!strcmp(username,user) &&
!my_strcasecmp(system_charset_info, hostname, host))
{
- ulong table_access;
- ulong cols_access;
+ privilege_t table_access(NO_ACL);
+ privilege_t cols_access(NO_ACL);
if (*hostname) // User
{
table_access= grant_table->privs;
@@ -9452,10 +9591,10 @@ static bool show_table_and_column_privileges(THD *thd, const char *username,
cols_access= grant_table->init_cols;
}
- if ((table_access | cols_access) != 0)
+ if ((table_access | cols_access) != NO_ACL)
{
String global(buff, sizeof(buff), system_charset_info);
- ulong test_access= (table_access | cols_access) & ~GRANT_ACL;
+ privilege_t test_access= (table_access | cols_access) & ~GRANT_ACL;
global.length(0);
global.append(STRING_WITH_LEN("GRANT "));
@@ -9468,7 +9607,7 @@ static bool show_table_and_column_privileges(THD *thd, const char *username,
{
/* Add specific column access */
int found= 0;
- ulong j;
+ ulonglong j;
for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
{
@@ -9572,16 +9711,16 @@ static int show_routine_grants(THD* thd,
if (!strcmp(username, user) &&
!my_strcasecmp(system_charset_info, hostname, host))
{
- ulong proc_access;
+ privilege_t proc_access(NO_ACL);
if (*hostname) // User
proc_access= grant_proc->privs;
else // Role
proc_access= grant_proc->init_privs;
- if (proc_access != 0)
+ if (proc_access != NO_ACL)
{
String global(buff, buffsize, system_charset_info);
- ulong test_access= proc_access & ~GRANT_ACL;
+ privilege_t test_access(proc_access & ~GRANT_ACL);
global.length(0);
global.append(STRING_WITH_LEN("GRANT "));
@@ -9592,7 +9731,7 @@ static int show_routine_grants(THD* thd,
{
/* Add specific procedure access */
int found= 0;
- ulong j;
+ ulonglong j;
for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
{
@@ -9635,13 +9774,13 @@ static int show_routine_grants(THD* thd,
Make a clear-text version of the requested privilege.
*/
-void get_privilege_desc(char *to, uint max_length, ulong access)
+void get_privilege_desc(char *to, uint max_length, privilege_t access_arg)
{
uint pos;
char *start=to;
DBUG_ASSERT(max_length >= 30); // For end ', ' removal
- if (access)
+ if (ulonglong access= access_arg)
{
max_length--; // Reserve place for end-zero
for (pos=0 ; access ; pos++, access>>=1)
@@ -10668,7 +10807,8 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
}
}
- if (replace_user_table(thd, tables.user_table(), user_name, 0, 0, 1, 0))
+ if (replace_user_table(thd, tables.user_table(), user_name,
+ NO_ACL, 0, 1, 0))
{
append_user(thd, &wrong_users, user_name);
result= TRUE;
@@ -10753,7 +10893,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
LEX_USER *user_name, *tmp_user_name;
List_iterator <LEX_USER> user_list(list);
bool binlog= false;
- sql_mode_t old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("mysql_drop_user");
DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
@@ -10765,7 +10904,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
DBUG_RETURN(result != 1);
- thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+ Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
@@ -10840,7 +10979,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- thd->variables.sql_mode= old_sql_mode;
DBUG_RETURN(result);
}
@@ -10972,8 +11110,9 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
while ((tmp_lex_user= users_list_iterator++))
{
LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
- if (!lex_user || replace_user_table(thd, tables.user_table(), lex_user, 0,
- false, false, true))
+ if (!lex_user ||
+ replace_user_table(thd, tables.user_table(), lex_user, NO_ACL,
+ false, false, true))
{
thd->clear_error();
append_user(thd, &wrong_users, tmp_lex_user);
@@ -11035,7 +11174,7 @@ mysql_revoke_sp_privs(THD *thd, Grant_tables *tables, const Sp_handler *sph,
tables->procs_priv_table().table(),
*lex_user,
grant_proc->db, grant_proc->tname,
- sph, ~(ulong)0, 1) == 0)
+ sph, ALL_KNOWN_ACL, 1) == 0)
{
revoked= 1;
continue;
@@ -11101,7 +11240,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
}
if (replace_user_table(thd, tables.user_table(), lex_user,
- ~(ulong)0, 1, 0, 0))
+ ALL_KNOWN_ACL, 1, 0, 0))
{
result= -1;
continue;
@@ -11130,7 +11269,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
/* TODO(cvicentiu) refactor replace_db_table to use
Db_table instead of TABLE directly. */
if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user,
- ~(ulong)0, 1))
+ ALL_KNOWN_ACL, 1))
{
/*
Don't increment counter as replace_db_table deleted the
@@ -11164,7 +11303,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
if (replace_table_table(thd, grant_table,
tables.tables_priv_table().table(),
*lex_user, grant_table->db,
- grant_table->tname, ~(ulong)0, 0, 1))
+ grant_table->tname, ALL_KNOWN_ACL, NO_ACL, 1))
{
result= -1;
}
@@ -11181,7 +11320,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
if (!replace_column_table(grant_table,
tables.columns_priv_table().table(),
*lex_user, columns, grant_table->db,
- grant_table->tname, ~(ulong)0, 1))
+ grant_table->tname, ALL_KNOWN_ACL, 1))
{
revoked= 1;
continue;
@@ -11379,7 +11518,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
for (counter= 0, revoked= 0 ; counter < hash->records ; )
{
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
- if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
+ if (!my_strcasecmp(&my_charset_utf8mb3_bin, grant_proc->db, sp_db) &&
!my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
{
LEX_USER lex_user;
@@ -11390,7 +11529,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
if (replace_routine_table(thd, grant_proc,
tables.procs_priv_table().table(), lex_user,
grant_proc->db, grant_proc->tname,
- sph, ~(ulong)0, 1) == 0)
+ sph, ALL_KNOWN_ACL, 1) == 0)
{
revoked= 1;
continue;
@@ -11724,10 +11863,231 @@ static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
#else
static bool set_user_salt_if_needed(ACL_USER *, int, plugin_ref)
{ return 0; }
-bool check_grant(THD *, ulong, TABLE_LIST *, bool, uint, bool)
+bool check_grant(THD *, privilege_t, TABLE_LIST *, bool, uint, bool)
{ return 0; }
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+
+bool Sql_cmd_grant_proxy::execute(THD *thd)
+{
+ my_ok(thd);
+ return false;
+}
+
+bool Sql_cmd_grant_table::execute(THD *thd)
+{
+ my_ok(thd);
+ return false;
+}
+
+
+bool Sql_cmd_grant_sp::execute(THD *thd)
+{
+ my_ok(thd);
+ return false;
+}
+
+#else // not NO_EMBEDDED_ACCESS_CHECKS
+
+
+void Sql_cmd_grant::warn_hostname_requires_resolving(THD *thd,
+ List<LEX_USER> &users)
+{
+ LEX_USER *user;
+ List_iterator <LEX_USER> it(users);
+ while ((user= it++))
+ {
+ if (specialflag & SPECIAL_NO_RESOLVE &&
+ hostname_requires_resolving(user->host.str))
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_HOSTNAME_WONT_WORK,
+ ER_THD(thd, ER_WARN_HOSTNAME_WONT_WORK));
+ }
+}
+
+
+void Sql_cmd_grant::grant_stage0(THD *thd)
+{
+ thd->binlog_invoker(false); // Replicate current user as grantor
+ if (thd->security_ctx->user) // If not replication
+ warn_hostname_requires_resolving(thd, thd->lex->users_list);
+}
+
+
+bool Sql_cmd_grant::user_list_reset_mqh(THD *thd, List<LEX_USER> &users)
+{
+ List_iterator <LEX_USER> it(users);
+ LEX_USER *user, *tmp_user;
+ while ((tmp_user= it++))
+ {
+ if (!(user= get_current_user(thd, tmp_user)))
+ return true;
+ reset_mqh(user, 0);
+ }
+ return false;
+}
+
+
+bool Sql_cmd_grant_proxy::check_access_proxy(THD *thd, List<LEX_USER> &users)
+{
+ LEX_USER *user;
+ List_iterator <LEX_USER> it(users);
+ if ((user= it++))
+ {
+ // GRANT/REVOKE PROXY has the target user as a first entry in the list
+ if (!(user= get_current_user(thd, user)) || !user->host.str)
+ return true;
+ if (acl_check_proxy_grant_access(thd, user->host.str, user->user.str,
+ m_grant_option & GRANT_ACL))
+ return true;
+ }
+ return false;
+}
+
+
+bool Sql_cmd_grant_proxy::execute(THD *thd)
+{
+ LEX *lex= thd->lex;
+
+ DBUG_ASSERT(lex->first_select_lex()->table_list.first == NULL);
+ DBUG_ASSERT((m_grant_option & ~GRANT_ACL) == NO_ACL); // only WITH GRANT OPTION
+
+ grant_stage0(thd);
+
+ if (thd->security_ctx->user /* If not replication */ &&
+ check_access_proxy(thd, lex->users_list))
+ return true;
+
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+ /* Conditionally writes to binlog */
+ if (mysql_grant(thd, NULL/*db*/, lex->users_list, m_grant_option,
+ is_revoke(), true/*proxy*/))
+ return true;
+
+ return !is_revoke() && user_list_reset_mqh(thd, lex->users_list);
+
+#ifdef WITH_WSREP
+wsrep_error_label:
+ return true;
+#endif // WITH_WSREP
+}
+
+
+bool Sql_cmd_grant_object::grant_stage0_exact_object(THD *thd,
+ TABLE_LIST *table)
+{
+ privilege_t priv= m_object_privilege | m_column_privilege_total | GRANT_ACL;
+ if (check_access(thd, priv, table->db.str,
+ &table->grant.privilege, &table->grant.m_internal,
+ 0, 0))
+ return true;
+ grant_stage0(thd);
+ return false;
+}
+
+
+bool Sql_cmd_grant_table::execute_exact_table(THD *thd, TABLE_LIST *table)
+{
+ LEX *lex= thd->lex;
+ if (grant_stage0_exact_object(thd, table) ||
+ check_grant(thd, m_object_privilege | m_column_privilege_total | GRANT_ACL,
+ lex->query_tables, FALSE, UINT_MAX, FALSE))
+ return true;
+ /* Conditionally writes to binlog */
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+ return mysql_table_grant(thd, lex->query_tables, lex->users_list,
+ m_columns, m_object_privilege,
+ is_revoke());
+#ifdef WITH_WSREP
+wsrep_error_label:
+ return true;
+#endif // WITH_WSREP
+}
+
+
+bool Sql_cmd_grant_sp::execute(THD *thd)
+{
+ DBUG_ASSERT(!m_columns.elements);
+ DBUG_ASSERT(!m_column_privilege_total);
+ LEX *lex= thd->lex;
+ TABLE_LIST *table= lex->first_select_lex()->table_list.first;
+ privilege_t grants= m_all_privileges
+ ? (PROC_ACLS & ~GRANT_ACL) | (m_object_privilege & GRANT_ACL)
+ : m_object_privilege;
+
+ if (!table) // e.g: GRANT EXECUTE ON PROCEDURE *.*
+ {
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
+ MYF(0));
+ return true;
+ }
+
+ if (grant_stage0_exact_object(thd, table) ||
+ check_grant_routine(thd, grants|GRANT_ACL, lex->query_tables, &m_sph, 0))
+ return true;
+
+ /* Conditionally writes to binlog */
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+ if (mysql_routine_grant(thd, lex->query_tables, &m_sph,
+ lex->users_list, grants,
+ is_revoke(), true))
+ return true;
+ my_ok(thd);
+ return false;
+#ifdef WITH_WSREP
+wsrep_error_label:
+ return true;
+#endif // WITH_WSREP
+}
+
+
+bool Sql_cmd_grant_table::execute_table_mask(THD *thd)
+{
+ LEX *lex= thd->lex;
+ DBUG_ASSERT(lex->first_select_lex()->table_list.first == NULL);
+
+ if (check_access(thd, m_object_privilege | m_column_privilege_total | GRANT_ACL,
+ m_db.str, NULL, NULL, 1, 0))
+ return true;
+
+ grant_stage0(thd);
+
+ if (m_columns.elements) // e.g. GRANT SELECT (a) ON *.*
+ {
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
+ MYF(0));
+ return true;
+ }
+
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+ /* Conditionally writes to binlog */
+ if (mysql_grant(thd, m_db.str, lex->users_list, m_object_privilege,
+ is_revoke(), false/*not proxy*/))
+ return true;
+
+ return !is_revoke() && user_list_reset_mqh(thd, lex->users_list);
+
+#ifdef WITH_WSREP
+wsrep_error_label:
+ return true;
+#endif // WITH_WSREP
+}
+
+
+bool Sql_cmd_grant_table::execute(THD *thd)
+{
+ TABLE_LIST *table= thd->lex->first_select_lex()->table_list.first;
+ return table ? execute_exact_table(thd, table) :
+ execute_table_mask(thd);
+}
+
+
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+
+
+
SHOW_VAR acl_statistics[] = {
#ifndef NO_EMBEDDED_ACCESS_CHECKS
{"column_grants", (char*)show_column_grants, SHOW_SIMPLE_FUNC},
@@ -11934,7 +12294,6 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
int error= 0;
uint counter;
ACL_USER *acl_user;
- ulong want_access;
TABLE *table= tables->table;
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
NULL, NULL, 1, 1);
@@ -11955,7 +12314,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
!thd->security_ctx->is_priv_user(user, host))
continue;
- want_access= acl_user->access;
+ privilege_t want_access(acl_user->access);
if (!(want_access & GRANT_ACL))
is_grantable= "NO";
@@ -11972,7 +12331,8 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
else
{
uint priv_id;
- ulong j,test_access= want_access & ~GRANT_ACL;
+ ulonglong j;
+ privilege_t test_access(want_access & ~GRANT_ACL);
for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
{
if (test_access & j)
@@ -12004,7 +12364,6 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
int error= 0;
uint counter;
ACL_DB *acl_db;
- ulong want_access;
TABLE *table= tables->table;
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
NULL, NULL, 1, 1);
@@ -12026,7 +12385,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
!thd->security_ctx->is_priv_user(user, host))
continue;
- want_access=acl_db->access;
+ privilege_t want_access(acl_db->access);
if (want_access)
{
if (!(want_access & GRANT_ACL))
@@ -12046,7 +12405,8 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
else
{
int cnt;
- ulong j,test_access= want_access & ~GRANT_ACL;
+ ulonglong j;
+ privilege_t test_access(want_access & ~GRANT_ACL);
for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
if (test_access & j)
{
@@ -12096,10 +12456,10 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
!thd->security_ctx->is_priv_user(user, host))
continue;
- ulong table_access= grant_table->privs;
+ privilege_t table_access(grant_table->privs);
if (table_access)
{
- ulong test_access= table_access & ~GRANT_ACL;
+ privilege_t test_access(table_access & ~GRANT_ACL);
/*
We should skip 'usage' privilege on table if
we have any privileges on column(s) of this table
@@ -12123,7 +12483,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
else
{
- ulong j;
+ ulonglong j;
int cnt;
for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
{
@@ -12177,19 +12537,19 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
!thd->security_ctx->is_priv_user(user, host))
continue;
- ulong table_access= grant_table->cols;
- if (table_access != 0)
+ privilege_t table_access(grant_table->cols);
+ if (table_access != NO_ACL)
{
if (!(grant_table->privs & GRANT_ACL))
is_grantable= "NO";
- ulong test_access= table_access & ~GRANT_ACL;
+ privilege_t test_access(table_access & ~GRANT_ACL);
Grantee_str grantee(user, host);
if (!test_access)
continue;
else
{
- ulong j;
+ ulonglong j;
int cnt;
for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
{
@@ -12255,8 +12615,8 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
if (!initialized)
{
DBUG_PRINT("info", ("skip grants"));
- grant->privilege= ~NO_ACCESS; // everything is allowed
- DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ grant->privilege= ALL_KNOWN_ACL; // everything is allowed
+ DBUG_PRINT("info", ("privilege 0x%llx", (longlong) grant->privilege));
DBUG_VOID_RETURN;
}
@@ -12300,7 +12660,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
}
mysql_rwlock_unlock(&LOCK_grant);
- DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_PRINT("info", ("privilege 0x%llx", (longlong) grant->privilege));
DBUG_VOID_RETURN;
}
@@ -12802,7 +13162,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
static bool ignore_max_password_errors(const ACL_USER *acl_user)
{
const char *host= acl_user->host.hostname;
- return (acl_user->access & SUPER_ACL)
+ return (acl_user->access & PRIV_IGNORE_MAX_PASSWORD_ERRORS)
&& (!strcasecmp(host, "localhost") ||
!strcmp(host, "127.0.0.1") ||
!strcmp(host, "::1"));
@@ -12854,7 +13214,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
*/
ulong nr1=1, nr2=4;
CHARSET_INFO *cs= &my_charset_latin1;
- cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
+ cs->hash_sort((uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
mysql_mutex_lock(&acl_cache->lock);
if (!acl_users.elements)
@@ -12990,7 +13350,8 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
system_charset_info, user, user_len,
thd->charset(), &dummy_errors);
- if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
+ if (!(sctx->user= my_strndup(key_memory_MPVIO_EXT_auth_info, user_buff,
+ user_len, MYF(MY_WME))))
DBUG_RETURN(1);
/* Clear variables that are allocated */
@@ -13249,8 +13610,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Security_context *sctx= thd->security_ctx;
- my_free((char*) sctx->user);
- if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
+ my_free(const_cast<char*>(sctx->user));
+ if (!(sctx->user= my_strndup(key_memory_MPVIO_EXT_auth_info, user, user_len, MYF(MY_WME))))
return packet_error; /* The error is set by my_strdup(). */
@@ -13507,8 +13868,8 @@ static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
{
-#ifdef HAVE_OPENSSL
Vio *vio= thd->net.vio;
+#ifdef HAVE_OPENSSL
SSL *ssl= (SSL *) vio->ssl_arg;
X509 *cert;
#endif
@@ -13522,6 +13883,24 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
switch (acl_user->ssl_type) {
case SSL_TYPE_NOT_SPECIFIED: // Impossible
case SSL_TYPE_NONE: // SSL is not required
+ if (opt_require_secure_transport)
+ {
+ enum enum_vio_type type= vio_type(vio);
+#ifdef HAVE_OPENSSL
+ return type != VIO_TYPE_SSL &&
+#ifndef _WIN32
+ type != VIO_TYPE_SOCKET;
+#else
+ type != VIO_TYPE_NAMEDPIPE;
+#endif
+#else
+#ifndef _WIN32
+ return type != VIO_TYPE_SOCKET;
+#else
+ return type != VIO_TYPE_NAMEDPIPE;
+#endif
+#endif
+ }
return 0;
#ifdef HAVE_OPENSSL
case SSL_TYPE_ANY: // Any kind of SSL is ok
@@ -13776,6 +14155,8 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
res= do_auth_once(thd, default_auth_plugin_name, &mpvio);
}
+ PSI_CALL_set_connection_type(vio_type(thd->net.vio));
+
Security_context * const sctx= thd->security_ctx;
const ACL_USER * acl_user= mpvio.acl_user;
if (!acl_user)
@@ -13813,17 +14194,9 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
*/
if (sctx->user)
{
- if (strcmp(sctx->priv_user, sctx->user))
- {
- 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",
- safe_str(mpvio.db.str));
- }
- else
- general_log_print(thd, command, (char*) "%s@%s on %s",
- sctx->user, sctx->host_or_ip,
- safe_str(mpvio.db.str));
+ general_log_print(thd, command, (char*) "%s@%s on %s using %s",
+ sctx->user, sctx->host_or_ip,
+ safe_str(mpvio.db.str), safe_vio_type_name(thd->net.vio));
}
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
@@ -13998,20 +14371,16 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
DBUG_PRINT("info",
("Capabilities: %llu packet_length: %ld Host: '%s' "
"Login user: '%s' Priv_user: '%s' Using password: %s "
- "Access: %lu db: '%s'",
+ "Access: %llx db: '%s'",
thd->client_capabilities, thd->max_client_packet_length,
sctx->host_or_ip, sctx->user, sctx->priv_user,
thd->password ? "yes": "no",
- sctx->master_access, mpvio.db.str));
+ (longlong) sctx->master_access, mpvio.db.str));
if (command == COM_CONNECT &&
- !(thd->main_security_ctx.master_access & SUPER_ACL))
+ !(thd->main_security_ctx.master_access & PRIV_IGNORE_MAX_CONNECTIONS))
{
- mysql_mutex_lock(&LOCK_connection_count);
- bool count_ok= (*thd->scheduler->connection_count <=
- *thd->scheduler->max_connections);
- mysql_mutex_unlock(&LOCK_connection_count);
- if (!count_ok)
+ if (*thd->scheduler->connection_count > *thd->scheduler->max_connections)
{ // too many connections
my_error(ER_CON_COUNT_ERROR, MYF(0));
DBUG_RETURN(1);
@@ -14023,14 +14392,14 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
set to 0 here because we don't have an active database yet (and we
may not have an active database to set.
*/
- sctx->db_access=0;
+ sctx->db_access= NO_ACL;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
In case the user has a default role set, attempt to set that role
*/
if (initialized && acl_user->default_rolename.length) {
- ulonglong access= 0;
+ privilege_t access(NO_ACL);
int result;
result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
if (!result)
@@ -14071,16 +14440,17 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
if (mpvio.auth_info.external_user[0])
- sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
+ sctx->external_user= my_strdup(key_memory_MPVIO_EXT_auth_info,
+ mpvio.auth_info.external_user, MYF(0));
if (res == CR_OK_HANDSHAKE_COMPLETE)
thd->get_stmt_da()->disable_status();
else
my_ok(thd);
- PSI_CALL_set_thread_user_host
- (thd->main_security_ctx.user, (uint)strlen(thd->main_security_ctx.user),
- thd->main_security_ctx.host_or_ip, (uint)strlen(thd->main_security_ctx.host_or_ip));
+ PSI_CALL_set_thread_account
+ (thd->main_security_ctx.user, static_cast<uint>(strlen(thd->main_security_ctx.user)),
+ thd->main_security_ctx.host_or_ip, static_cast<uint>(strlen(thd->main_security_ctx.host_or_ip)));
/* Ready to handle queries */
DBUG_RETURN(0);
@@ -14347,3 +14717,40 @@ maria_declare_plugin(mysql_password)
MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
}
maria_declare_plugin_end;
+
+
+/*
+ Exporting functions that allow plugins to do server-style
+ host/user matching. Used in server_audit2 plugin.
+*/
+extern "C" int maria_compare_hostname(
+ const char *wild_host, long wild_ip, long ip_mask,
+ const char *host, const char *ip)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ acl_host_and_ip h;
+ h.hostname= (char *) wild_host;
+ h.ip= wild_ip;
+ h.ip_mask= ip_mask;
+
+ return compare_hostname(&h, host, ip);
+#else
+ return 0;
+#endif
+}
+
+
+extern "C" void maria_update_hostname(
+ const char **wild_host, long *wild_ip, long *ip_mask,
+ const char *host)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ acl_host_and_ip h;
+ update_hostname(&h, host);
+ *wild_host= h.hostname;
+ *wild_ip= h.ip;
+ *ip_mask= h.ip_mask;
+#endif
+}
+
+
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index dc8a085c96c..570da144b46 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -19,145 +19,9 @@
#include "violite.h" /* SSL_type */
#include "sql_class.h" /* LEX_COLUMN */
+#include "grant.h"
+#include "sql_cmd.h" /* Sql_cmd */
-#define SELECT_ACL (1UL << 0)
-#define INSERT_ACL (1UL << 1)
-#define UPDATE_ACL (1UL << 2)
-#define DELETE_ACL (1UL << 3)
-#define CREATE_ACL (1UL << 4)
-#define DROP_ACL (1UL << 5)
-#define RELOAD_ACL (1UL << 6)
-#define SHUTDOWN_ACL (1UL << 7)
-#define PROCESS_ACL (1UL << 8)
-#define FILE_ACL (1UL << 9)
-#define GRANT_ACL (1UL << 10)
-#define REFERENCES_ACL (1UL << 11)
-#define INDEX_ACL (1UL << 12)
-#define ALTER_ACL (1UL << 13)
-#define SHOW_DB_ACL (1UL << 14)
-#define SUPER_ACL (1UL << 15)
-#define CREATE_TMP_ACL (1UL << 16)
-#define LOCK_TABLES_ACL (1UL << 17)
-#define EXECUTE_ACL (1UL << 18)
-#define REPL_SLAVE_ACL (1UL << 19)
-#define REPL_CLIENT_ACL (1UL << 20)
-#define CREATE_VIEW_ACL (1UL << 21)
-#define SHOW_VIEW_ACL (1UL << 22)
-#define CREATE_PROC_ACL (1UL << 23)
-#define ALTER_PROC_ACL (1UL << 24)
-#define CREATE_USER_ACL (1UL << 25)
-#define EVENT_ACL (1UL << 26)
-#define TRIGGER_ACL (1UL << 27)
-#define CREATE_TABLESPACE_ACL (1UL << 28)
-#define DELETE_HISTORY_ACL (1UL << 29)
-/*
- don't forget to update
- 1. static struct show_privileges_st sys_privileges[]
- 2. static const char *command_array[] and static uint command_lengths[]
- 3. mysql_system_tables.sql and mysql_system_tables_fix.sql
- 4. acl_init() or whatever - to define behaviour for old privilege tables
- 5. sql_yacc.yy - for GRANT/REVOKE to work
-*/
-#define NO_ACCESS (1UL << 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 | \
- LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
- CREATE_PROC_ACL | ALTER_PROC_ACL | EVENT_ACL | TRIGGER_ACL | \
- DELETE_HISTORY_ACL)
-
-#define TABLE_ACLS \
-(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
- GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | \
- SHOW_VIEW_ACL | TRIGGER_ACL | DELETE_HISTORY_ACL)
-
-#define COL_ACLS \
-(SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL)
-
-#define PROC_ACLS \
-(ALTER_PROC_ACL | EXECUTE_ACL | GRANT_ACL)
-
-#define SHOW_PROC_ACLS \
-(ALTER_PROC_ACL | EXECUTE_ACL | CREATE_PROC_ACL)
-
-#define GLOBAL_ACLS \
-(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
- RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \
- REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \
- CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \
- EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \
- ALTER_PROC_ACL | CREATE_USER_ACL | EVENT_ACL | TRIGGER_ACL | \
- CREATE_TABLESPACE_ACL | DELETE_HISTORY_ACL)
-
-#define DEFAULT_CREATE_PROC_ACLS \
-(ALTER_PROC_ACL | EXECUTE_ACL)
-
-#define SHOW_CREATE_TABLE_ACLS \
-(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | \
- 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
-*/
-
-/* Privileges that needs to be reallocated (in continous chunks) */
-#define DB_CHUNK0 (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | \
- CREATE_ACL | DROP_ACL)
-#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 )
-#define DB_CHUNK4 (EXECUTE_ACL)
-#define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL)
-#define DB_CHUNK6 (DELETE_HISTORY_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) << 9) & DB_CHUNK5) | \
- (((A) << 10) & DB_CHUNK6))
-#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_CHUNK5) >> 9) | \
- (((A) & DB_CHUNK6) >> 10))
-#define TBL_CHUNK0 DB_CHUNK0
-#define TBL_CHUNK1 DB_CHUNK1
-#define TBL_CHUNK2 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
-#define TBL_CHUNK3 TRIGGER_ACL
-#define TBL_CHUNK4 (DELETE_HISTORY_ACL)
-#define fix_rights_for_table(A) (((A) & TBL_CHUNK0) | \
- (((A) << 4) & TBL_CHUNK1) | \
- (((A) << 11) & TBL_CHUNK2) | \
- (((A) << 15) & TBL_CHUNK3) | \
- (((A) << 16) & TBL_CHUNK4))
-#define get_rights_for_table(A) (((A) & TBL_CHUNK0) | \
- (((A) & TBL_CHUNK1) >> 4) | \
- (((A) & TBL_CHUNK2) >> 11) | \
- (((A) & TBL_CHUNK3) >> 15) | \
- (((A) & TBL_CHUNK4) >> 16))
-#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))
-#define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \
- (((A) & ALTER_PROC_ACL) >> 23) | \
- (((A) & GRANT_ACL) >> 8))
enum mysql_db_table_field
{
@@ -212,8 +76,8 @@ bool hostname_requires_resolving(const char *hostname);
bool acl_init(bool dont_read_acl_tables);
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);
+privilege_t 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 com_change_user_pkt_len);
bool acl_getroot(Security_context *sctx, const char *user, const char *host,
const char *ip, const char *db);
@@ -223,37 +87,38 @@ bool change_password(THD *thd, LEX_USER *user);
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);
+ privilege_t 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,
+ List <LEX_COLUMN> &column_list, privilege_t rights,
bool revoke);
bool mysql_routine_grant(THD *thd, TABLE_LIST *table, const Sp_handler *sph,
- List <LEX_USER> &user_list, ulong rights,
+ List <LEX_USER> &user_list, privilege_t rights,
bool revoke, bool write_to_binlog);
bool grant_init();
void grant_free(void);
bool grant_reload(THD *thd);
-bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
+bool check_grant(THD *thd, privilege_t 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, size_t length, Security_context *sctx);
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
const char *name, size_t length, Field *fld);
-bool check_grant_all_columns(THD *thd, ulong want_access,
+bool check_grant_all_columns(THD *thd, privilege_t want_access,
Field_iterator_table_ref *fields);
-bool check_grant_routine(THD *thd, ulong want_access,
+bool check_grant_routine(THD *thd, privilege_t want_access,
TABLE_LIST *procs, const Sp_handler *sph,
bool no_error);
bool check_grant_db(THD *thd,const char *db);
-bool check_global_access(THD *thd, ulong want_access, bool no_errors= false);
-bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
+bool check_global_access(THD *thd, const privilege_t want_access, bool no_errors= false);
+bool check_access(THD *thd, privilege_t want_access,
+ const char *db, privilege_t *save_priv,
GRANT_INTERNAL_INFO *grant_internal_info,
bool dont_check_global_grants, bool no_errors);
-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);
+privilege_t get_table_grant(THD *thd, TABLE_LIST *table);
+privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *field_name);
bool get_show_user(THD *thd, LEX_USER *lex_user, const char **username,
const char **hostname, const char **rolename);
void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
@@ -262,7 +127,7 @@ bool mysql_show_grants(THD *thd, LEX_USER *user);
bool mysql_show_create_user(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_privilege_desc(char *to, uint max_length, privilege_t access);
void get_mqh(const char *user, const char *host, USER_CONN *uc);
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);
@@ -302,8 +167,6 @@ enum ACL_internal_access_result
/**
Access granted for all the requested privileges,
do not use the grant tables.
- This flag is used only for the INFORMATION_SCHEMA privileges,
- for compatibility reasons.
*/
ACL_INTERNAL_ACCESS_GRANTED,
/** Access denied, do not use the grant tables. */
@@ -343,8 +206,8 @@ public:
privilege. Requested privileges that are granted, if any, are saved
in save_priv.
*/
- virtual ACL_internal_access_result check(ulong want_access,
- ulong *save_priv) const= 0;
+ virtual ACL_internal_access_result check(privilege_t want_access,
+ privilege_t *save_priv) const= 0;
};
/**
@@ -380,8 +243,8 @@ public:
privilege. Requested privileges that are granted, if any, are saved
in save_priv.
*/
- virtual ACL_internal_access_result check(ulong want_access,
- ulong *save_priv) const= 0;
+ virtual ACL_internal_access_result check(privilege_t want_access,
+ privilege_t *save_priv) const= 0;
/**
Search for per table ACL access rules by table name.
@@ -415,8 +278,8 @@ 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, const char *rolename, ulonglong access);
-int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access);
+int acl_setrole(THD *thd, const char *rolename, privilege_t access);
+int acl_check_setrole(THD *thd, const char *rolename, privilege_t *access);
int acl_check_set_default_role(THD *thd, const char *host, const char *user,
const char *role);
int acl_set_default_role(THD *thd, const char *host, const char *user,
@@ -436,4 +299,78 @@ bool check_role_is_granted(const char *username,
extern ulong role_global_merges, role_db_merges, role_table_merges,
role_column_merges, role_routine_merges;
#endif
+
+
+class Sql_cmd_grant: public Sql_cmd
+{
+protected:
+ enum_sql_command m_command;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ void warn_hostname_requires_resolving(THD *thd, List<LEX_USER> &list);
+ bool user_list_reset_mqh(THD *thd, List<LEX_USER> &list);
+ void grant_stage0(THD *thd);
+#endif
+public:
+ Sql_cmd_grant(enum_sql_command command)
+ :m_command(command)
+ { }
+ bool is_revoke() const { return m_command == SQLCOM_REVOKE; }
+ enum_sql_command sql_command_code() const { return m_command; }
+};
+
+
+class Sql_cmd_grant_proxy: public Sql_cmd_grant
+{
+ privilege_t m_grant_option;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool check_access_proxy(THD *thd, List<LEX_USER> &list);
+#endif
+public:
+ Sql_cmd_grant_proxy(enum_sql_command command, privilege_t grant_option)
+ :Sql_cmd_grant(command), m_grant_option(grant_option)
+ { }
+ bool execute(THD *thd);
+};
+
+
+class Sql_cmd_grant_object: public Sql_cmd_grant, public Grant_privilege
+{
+protected:
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool grant_stage0_exact_object(THD *thd, TABLE_LIST *table);
+#endif
+public:
+ Sql_cmd_grant_object(enum_sql_command command, const Grant_privilege &grant)
+ :Sql_cmd_grant(command), Grant_privilege(grant)
+ { }
+};
+
+
+class Sql_cmd_grant_table: public Sql_cmd_grant_object
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool execute_table_mask(THD *thd);
+ bool execute_exact_table(THD *thd, TABLE_LIST *table);
+#endif
+public:
+ Sql_cmd_grant_table(enum_sql_command command, const Grant_privilege &grant)
+ :Sql_cmd_grant_object(command, grant)
+ { }
+ bool execute(THD *thd);
+};
+
+
+
+class Sql_cmd_grant_sp: public Sql_cmd_grant_object
+{
+ const Sp_handler &m_sph;
+public:
+ Sql_cmd_grant_sp(enum_sql_command command, const Grant_privilege &grant,
+ const Sp_handler &sph)
+ :Sql_cmd_grant_object(command, grant),
+ m_sph(sph)
+ { }
+ bool execute(THD *thd);
+};
+
#endif /* SQL_ACL_INCLUDED */
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 6201411d4aa..dc9b971f2ed 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -26,7 +26,6 @@
#include "sql_view.h" // view_checksum
#include "sql_table.h" // mysql_recreate_table
#include "debug_sync.h" // DEBUG_SYNC
-#include "sql_acl.h" // *_ACL
#include "sp.h" // Sroutine_hash_entry
#include "sql_parse.h" // check_table_access
#include "strfunc.h"
@@ -122,9 +121,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
Let us try to open at least a .FRM for this table.
*/
- table_list->mdl_request.init(MDL_key::TABLE,
- table_list->db.str, table_list->table_name.str,
- MDL_EXCLUSIVE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table_list->mdl_request, MDL_key::TABLE,
+ table_list->db.str, table_list->table_name.str,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
if (lock_table_names(thd, table_list, table_list->next_global,
thd->variables.lock_wait_timeout, 0))
@@ -547,8 +546,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
close_thread_tables(thd);
table->table= NULL;
thd->mdl_context.release_transactional_locks();
- table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
- MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table->mdl_request, MDL_key::TABLE, table->db.str,
+ table->table_name.str, MDL_SHARED_NO_READ_WRITE,
+ MDL_TRANSACTION);
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -807,8 +807,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
close_thread_tables(thd);
table->table= NULL;
thd->mdl_context.release_transactional_locks();
- table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
- MDL_SHARED_NO_READ_WRITE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table->mdl_request, MDL_key::TABLE, table->db.str,
+ table->table_name.str, MDL_SHARED_NO_READ_WRITE,
+ MDL_TRANSACTION);
table->mdl_request.set_type(MDL_SHARED_READ);
table->lock_type= TL_READ;
@@ -1032,9 +1033,7 @@ send_result_message:
*save_next_global= table->next_global;
table->next_local= table->next_global= 0;
- 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);
@@ -1152,6 +1151,13 @@ send_result_message:
}
if (table->table && !table->view)
{
+ /*
+ Don't skip flushing if we are collecting EITS statistics.
+ */
+ const bool skip_flush=
+ (operator_func == &handler::ha_analyze) &&
+ (table->table->file->ha_table_flags() & HA_ONLINE_ANALYZE) &&
+ !collect_eis;
if (table->table->s->tmp_table)
{
/*
@@ -1161,10 +1167,9 @@ send_result_message:
if (open_for_modify && !open_error)
table->table->file->info(HA_STATUS_CONST);
}
- else if (open_for_modify || fatal_error)
+ else if ((!skip_flush && open_for_modify) || fatal_error)
{
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table->db.str, table->table_name.str, FALSE);
+ table->table->s->tdc->flush_unused(true);
/*
May be something modified. Consequently, we have to
invalidate the query cache.
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index 21c79a046a5..ede30263e48 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -25,6 +25,7 @@ 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),
+ alter_rename_key_list(rhs.alter_rename_key_list, mem_root),
create_list(rhs.create_list, mem_root),
check_constraint_list(rhs.check_constraint_list, mem_root),
flags(rhs.flags), partition_flags(rhs.partition_flags),
@@ -46,6 +47,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
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(alter_rename_key_list, mem_root);
list_copy_and_replace_each_value(create_list, mem_root);
/* partition_names are not deeply copied currently */
}
@@ -251,7 +253,8 @@ Alter_info::algorithm(const THD *thd) const
Alter_table_ctx::Alter_table_ctx()
- : datetime_field(NULL), error_if_not_empty(false),
+ : implicit_default_value_error_field(NULL),
+ error_if_not_empty(false),
tables_opened(0),
db(null_clex_str), table_name(null_clex_str), alias(null_clex_str),
new_db(null_clex_str), new_name(null_clex_str), new_alias(null_clex_str),
@@ -272,7 +275,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
uint tables_opened_arg,
const LEX_CSTRING *new_db_arg,
const LEX_CSTRING *new_name_arg)
- : datetime_field(NULL), error_if_not_empty(false),
+ : implicit_default_value_error_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),
@@ -331,7 +334,8 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
}
tmp_name.str= tmp_name_buff;
- tmp_name.length= my_snprintf(tmp_name_buff, sizeof(tmp_name_buff), "%s-%lx_%llx",
+ tmp_name.length= my_snprintf(tmp_name_buff, sizeof(tmp_name_buff),
+ "%s-alter-%lx-%llx",
tmp_file_prefix, current_pid, thd->thread_id);
/* Safety fix for InnoDB */
if (lower_case_table_names)
@@ -364,6 +368,21 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
}
+void Alter_table_ctx::report_implicit_default_value_error(THD *thd,
+ const TABLE_SHARE *s)
+ const
+{
+ Create_field *error_field= implicit_default_value_error_field;
+ const Type_handler *h= error_field->type_handler();
+ thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN,
+ h->name().ptr(),
+ h->default_value().ptr(),
+ s ? s->db.str : nullptr,
+ s ? s->table_name.str : nullptr,
+ error_field->field_name.str);
+}
+
+
bool Sql_cmd_alter_table::execute(THD *thd)
{
LEX *lex= thd->lex;
@@ -392,8 +411,9 @@ bool Sql_cmd_alter_table::execute(THD *thd)
*/
HA_CREATE_INFO create_info(lex->create_info);
Alter_info alter_info(lex->alter_info, thd->mem_root);
- ulong priv=0;
- ulong priv_needed= ALTER_ACL;
+ create_info.alter_info= &alter_info;
+ privilege_t priv(NO_ACL);
+ privilege_t priv_needed(ALTER_ACL);
bool result;
DBUG_ENTER("Sql_cmd_alter_table::execute");
@@ -502,9 +522,9 @@ bool Sql_cmd_alter_table::execute(THD *thd)
(!thd->is_current_stmt_binlog_format_row() ||
!thd->find_temporary_table(first_table)))
{
- WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL),
- (lex->name.str ? lex->name.str : NULL),
- first_table, &alter_info);
+ WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : first_table->db.str),
+ (lex->name.str ? lex->name.str : first_table->table_name.str),
+ first_table, &alter_info, used_engine ? &create_info : NULL);
thd->variables.auto_increment_offset = 1;
thd->variables.auto_increment_increment = 1;
@@ -517,7 +537,7 @@ bool Sql_cmd_alter_table::execute(THD *thd)
&alter_info,
select_lex->order_list.elements,
select_lex->order_list.first,
- lex->ignore);
+ lex->ignore, lex->if_exists());
DBUG_RETURN(result);
#ifdef WITH_WSREP
diff --git a/sql/sql_alter.h b/sql/sql_alter.h
index 71920b84792..89eb4ebb3e9 100644
--- a/sql/sql_alter.h
+++ b/sql/sql_alter.h
@@ -19,6 +19,7 @@
class Alter_drop;
class Alter_column;
+class Alter_rename_key;
class Key;
/**
@@ -90,13 +91,10 @@ public:
List<Alter_column> alter_list;
// List of keys, used by both CREATE and ALTER TABLE.
List<Key> key_list;
+ // List of keys to be renamed.
+ List<Alter_rename_key> alter_rename_key_list;
// List of columns, used by both CREATE and ALTER TABLE.
List<Create_field> create_list;
-
- enum flags_bits
- {
- CHECK_CONSTRAINT_IF_NOT_EXISTS= 1
- };
List<Virtual_column_info> check_constraint_list;
// Type of ALTER TABLE operation.
alter_table_operations flags;
@@ -129,6 +127,7 @@ public:
drop_list.empty();
alter_list.empty();
key_list.empty();
+ alter_rename_key_list.empty();
create_list.empty();
check_constraint_list.empty();
flags= 0;
@@ -302,8 +301,9 @@ public:
fk_error_table= fk->foreign_table->str;
}
+ void report_implicit_default_value_error(THD *thd, const TABLE_SHARE *) const;
public:
- Create_field *datetime_field;
+ Create_field *implicit_default_value_error_field;
bool error_if_not_empty;
uint tables_opened;
LEX_CSTRING db;
diff --git a/sql/sql_analyze_stmt.cc b/sql/sql_analyze_stmt.cc
index f1c6e2c73ea..2f87b9b0d40 100644
--- a/sql/sql_analyze_stmt.cc
+++ b/sql/sql_analyze_stmt.cc
@@ -26,6 +26,7 @@
void Filesort_tracker::print_json_members(Json_writer *writer)
{
const char *varied_str= "(varied across executions)";
+ String str;
if (!get_r_loops())
writer->add_member("r_loops").add_null();
@@ -78,5 +79,44 @@ void Filesort_tracker::print_json_members(Json_writer *writer)
else
writer->add_size(sort_buffer_size);
}
+
+ get_data_format(&str);
+ writer->add_member("r_sort_mode").add_str(str.c_ptr(), str.length());
+}
+
+void Filesort_tracker::get_data_format(String *str)
+{
+ if (r_sort_keys_packed)
+ str->append("packed_sort_key");
+ else
+ str->append("sort_key");
+ str->append(",");
+
+ if (r_using_addons)
+ {
+ if (r_packed_addon_fields)
+ str->append("packed_addon_fields");
+ else
+ str->append("addon_fields");
+ }
+ else
+ str->append("rowid");
+}
+
+void attach_gap_time_tracker(THD *thd, Gap_time_tracker *gap_tracker,
+ ulonglong timeval)
+{
+ thd->gap_tracker_data.bill_to= gap_tracker;
+ thd->gap_tracker_data.start_time= timeval;
+}
+
+void process_gap_time_tracker(THD *thd, ulonglong timeval)
+{
+ if (thd->gap_tracker_data.bill_to)
+ {
+ thd->gap_tracker_data.bill_to->log_time(thd->gap_tracker_data.start_time,
+ timeval);
+ thd->gap_tracker_data.bill_to= NULL;
+ }
}
diff --git a/sql/sql_analyze_stmt.h b/sql/sql_analyze_stmt.h
index eec52822ae5..990c79fb9ad 100644
--- a/sql/sql_analyze_stmt.h
+++ b/sql/sql_analyze_stmt.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2015 MariaDB Corporation Ab
+ Copyright (c) 2015, 2020, MariaDB Corporation.
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
@@ -38,6 +38,10 @@ $stmt").
*/
+class Gap_time_tracker;
+void attach_gap_time_tracker(THD *thd, Gap_time_tracker *gap_tracker, ulonglong timeval);
+void process_gap_time_tracker(THD *thd, ulonglong timeval);
+
/*
A class for tracking time it takes to do a certain action
*/
@@ -48,26 +52,37 @@ protected:
ulonglong cycles;
ulonglong last_start;
- void cycles_stop_tracking()
+ void cycles_stop_tracking(THD *thd)
{
ulonglong end= my_timer_cycles();
cycles += end - last_start;
if (unlikely(end < last_start))
cycles += ULONGLONG_MAX;
+
+ process_gap_time_tracker(thd, end);
+ if (my_gap_tracker)
+ attach_gap_time_tracker(thd, my_gap_tracker, end);
}
public:
- Exec_time_tracker() : count(0), cycles(0) {}
-
+ Exec_time_tracker() : count(0), cycles(0), my_gap_tracker(NULL) {}
+
+ /*
+ The time spent between stop_tracking() call on this object and any
+ other time measurement will be billed to this tracker.
+ */
+ Gap_time_tracker *my_gap_tracker;
+
// interface for collecting time
- void start_tracking()
+ void start_tracking(THD *thd)
{
last_start= my_timer_cycles();
+ process_gap_time_tracker(thd, last_start);
}
- void stop_tracking()
+ void stop_tracking(THD *thd)
{
count++;
- cycles_stop_tracking();
+ cycles_stop_tracking(thd);
}
// interface for getting the time
@@ -75,12 +90,39 @@ public:
double get_time_ms() const
{
// convert 'cycles' to milliseconds.
- return 1000 * ((double)cycles) / sys_timer_info.cycles.frequency;
+ return 1000.0 * static_cast<double>(cycles) /
+ static_cast<double>(sys_timer_info.cycles.frequency);
}
};
/*
+ Tracker for time spent between the calls to Exec_time_tracker's {start|
+ stop}_tracking().
+
+ @seealso Gap_time_tracker_data in sql_class.h
+*/
+class Gap_time_tracker
+{
+ ulonglong cycles;
+public:
+ Gap_time_tracker() : cycles(0) {}
+
+ void log_time(ulonglong start, ulonglong end) {
+ cycles += end - start;
+ }
+
+ double get_time_ms() const
+ {
+ // convert 'cycles' to milliseconds.
+ return 1000.0 * static_cast<double>(cycles) /
+ static_cast<double>(sys_timer_info.cycles.frequency);
+ }
+};
+
+
+
+/*
A class for counting certain actions (in all queries), and optionally
collecting the timings (in ANALYZE queries).
*/
@@ -99,22 +141,22 @@ public:
/*
Unlike Exec_time_tracker::stop_tracking, we don't increase loops.
*/
- void stop_tracking()
+ void stop_tracking(THD *thd)
{
- cycles_stop_tracking();
+ cycles_stop_tracking(thd);
}
};
-#define ANALYZE_START_TRACKING(tracker) \
+#define ANALYZE_START_TRACKING(thd, tracker) \
{ \
(tracker)->incr_loops(); \
if (unlikely((tracker)->timed)) \
- { (tracker)->start_tracking(); } \
+ { (tracker)->start_tracking(thd); } \
}
-#define ANALYZE_STOP_TRACKING(tracker) \
+#define ANALYZE_STOP_TRACKING(thd, tracker) \
if (unlikely((tracker)->timed)) \
- { (tracker)->stop_tracking(); }
+ { (tracker)->stop_tracking(thd); }
/*
A class for collecting read statistics.
@@ -138,24 +180,23 @@ public:
ha_rows r_rows; /* How many rows we've got after that */
ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */
- bool has_scans() { return (r_scans != 0); }
- ha_rows get_loops() { return r_scans; }
- double get_avg_rows()
+ bool has_scans() const { return (r_scans != 0); }
+ ha_rows get_loops() const { return r_scans; }
+ double get_avg_rows() const
{
- return r_scans ? ((double)r_rows / r_scans): 0;
+ return r_scans
+ ? static_cast<double>(r_rows) / static_cast<double>(r_scans)
+ : 0;
}
- double get_filtered_after_where()
+ double get_filtered_after_where() const
{
- double r_filtered;
- if (r_rows > 0)
- r_filtered= (double)r_rows_after_where / r_rows;
- else
- r_filtered= 1.0;
-
- return r_filtered;
+ return r_rows > 0
+ ? static_cast<double>(r_rows_after_where) /
+ static_cast<double>(r_rows)
+ : 1.0;
}
-
+
inline void on_scan_init() { r_scans++; }
inline void on_record_read() { r_rows++; }
inline void on_record_after_where() { r_rows_after_where++; }
@@ -181,19 +222,22 @@ public:
time_tracker(do_timing), r_limit(0), r_used_pq(0),
r_examined_rows(0), r_sorted_rows(0), r_output_rows(0),
sort_passes(0),
- sort_buffer_size(0)
+ sort_buffer_size(0),
+ r_using_addons(false),
+ r_packed_addon_fields(false),
+ r_sort_keys_packed(false)
{}
/* Functions that filesort uses to report various things about its execution */
- inline void report_use(ha_rows r_limit_arg)
+ inline void report_use(THD *thd, ha_rows r_limit_arg)
{
if (!time_tracker.get_loops())
r_limit= r_limit_arg;
else
r_limit= (r_limit != r_limit_arg)? 0: r_limit_arg;
- ANALYZE_START_TRACKING(&time_tracker);
+ ANALYZE_START_TRACKING(thd, &time_tracker);
}
inline void incr_pq_used() { r_used_pq++; }
@@ -210,9 +254,9 @@ public:
{
sort_passes -= passes;
}
- inline void report_merge_passes_at_end(ulong passes)
+ inline void report_merge_passes_at_end(THD *thd, ulong passes)
{
- ANALYZE_STOP_TRACKING(&time_tracker);
+ ANALYZE_STOP_TRACKING(thd, &time_tracker);
sort_passes += passes;
}
@@ -223,25 +267,39 @@ public:
else
sort_buffer_size= bufsize;
}
-
+
+ inline void report_addon_fields_format(bool addons_packed)
+ {
+ r_using_addons= true;
+ r_packed_addon_fields= addons_packed;
+ }
+ inline void report_sort_keys_format(bool sort_keys_packed)
+ {
+ r_sort_keys_packed= sort_keys_packed;
+ }
+
+ void get_data_format(String *str);
+
/* Functions to get the statistics */
void print_json_members(Json_writer *writer);
-
+
ulonglong get_r_loops() const { return time_tracker.get_loops(); }
- double get_avg_examined_rows()
- {
- return ((double)r_examined_rows) / get_r_loops();
+ double get_avg_examined_rows() const
+ {
+ return static_cast<double>(r_examined_rows) /
+ static_cast<double>(get_r_loops());
}
- double get_avg_returned_rows()
- {
- return ((double)r_output_rows) / get_r_loops();
+ double get_avg_returned_rows() const
+ {
+ return static_cast<double>(r_output_rows) /
+ static_cast<double>(get_r_loops());
}
- double get_r_filtered()
+ double get_r_filtered() const
{
- if (r_examined_rows > 0)
- return ((double)r_sorted_rows / r_examined_rows);
- else
- return 1.0;
+ return r_examined_rows > 0
+ ? static_cast<double>(r_sorted_rows) /
+ static_cast<double>(r_examined_rows)
+ : 1.0;
}
private:
Time_and_counter_tracker time_tracker;
@@ -282,6 +340,9 @@ private:
other - value
*/
ulonglong sort_buffer_size;
+ bool r_using_addons;
+ bool r_packed_addon_fields;
+ bool r_sort_keys_packed;
};
@@ -318,14 +379,14 @@ public:
container_elements(0), n_checks(0), n_positive_checks(0)
{}
- inline void start_tracking()
+ inline void start_tracking(THD *thd)
{
- ANALYZE_START_TRACKING(&time_tracker);
+ ANALYZE_START_TRACKING(thd, &time_tracker);
}
- inline void stop_tracking()
+ inline void stop_tracking(THD *thd)
{
- ANALYZE_STOP_TRACKING(&time_tracker);
+ ANALYZE_STOP_TRACKING(thd, &time_tracker);
}
/* Save container buffer size in bytes */
@@ -339,7 +400,7 @@ public:
return &time_tracker;
}
- double get_time_fill_container_ms()
+ double get_time_fill_container_ms() const
{
return time_tracker.get_time_ms();
}
@@ -353,13 +414,14 @@ public:
inline void increment_container_elements_count() { container_elements++; }
- uint get_container_elements() { return container_elements; }
+ uint get_container_elements() const { return container_elements; }
- double get_r_selectivity_pct()
+ double get_r_selectivity_pct() const
{
- return (double)n_positive_checks/(double)n_checks;
+ return static_cast<double>(n_positive_checks) /
+ static_cast<double>(n_checks);
}
- size_t get_container_buff_size() { return container_buff_size; }
+ size_t get_container_buff_size() const { return container_buff_size; }
};
diff --git a/sql/sql_array.h b/sql/sql_array.h
index bcfbb98ef19..b6de1b18d78 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -3,6 +3,7 @@
/* Copyright (c) 2003, 2005-2007 MySQL AB, 2009 Sun Microsystems, Inc.
Use is subject to license terms.
+ Copyright (c) 2020, MariaDB Corporation.
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,7 +35,7 @@
template <typename Element_type> class Bounds_checked_array
{
public:
- Bounds_checked_array() : m_array(NULL), m_size(0) {}
+ Bounds_checked_array()= default;
Bounds_checked_array(Element_type *el, size_t size_arg)
: m_array(el), m_size(size_arg)
@@ -85,6 +86,10 @@ public:
Element_type *array() const { return m_array; }
+ Element_type *begin() const { return array(); }
+ Element_type *end() const { return array() + m_size; }
+
+
bool operator==(const Bounds_checked_array<Element_type>&rhs) const
{
return m_array == rhs.m_array && m_size == rhs.m_size;
@@ -95,8 +100,8 @@ public:
}
private:
- Element_type *m_array;
- size_t m_size;
+ Element_type *m_array= nullptr;
+ size_t m_size= 0;
};
/*
@@ -109,21 +114,21 @@ template <class Elem> class Dynamic_array
{
DYNAMIC_ARRAY array;
public:
- Dynamic_array(uint prealloc=16, uint increment=16)
+ Dynamic_array(PSI_memory_key psi_key, uint prealloc=16, uint increment=16)
{
- init(prealloc, increment);
+ init(psi_key, prealloc, increment);
}
Dynamic_array(MEM_ROOT *root, uint prealloc=16, uint increment=16)
{
void *init_buffer= alloc_root(root, sizeof(Elem) * prealloc);
- my_init_dynamic_array2(&array, sizeof(Elem), init_buffer,
+ init_dynamic_array2(root->m_psi_key, &array, sizeof(Elem), init_buffer,
prealloc, increment, MYF(0));
}
- void init(uint prealloc=16, uint increment=16)
+ void init(PSI_memory_key psi_key, uint prealloc=16, uint increment=16)
{
- init_dynamic_array2(&array, sizeof(Elem), 0, prealloc, increment, MYF(0));
+ init_dynamic_array2(psi_key, &array, sizeof(Elem), 0, prealloc, increment, MYF(0));
}
/**
@@ -165,6 +170,11 @@ public:
return ((const Elem*)array.buffer) + array.elements - 1;
}
+ const Elem *end() const
+ {
+ return back() + 1;
+ }
+
/// @returns pointer to n-th element
Elem *get_pos(size_t idx)
{
@@ -177,7 +187,6 @@ public:
return ((const Elem*)array.buffer) + idx;
}
-
/**
@retval false ok
@retval true OOM, @c my_error() has been called.
@@ -235,10 +244,16 @@ public:
freeze_size(&array);
}
+ bool reserve(size_t new_size)
+ {
+ return allocate_dynamic(&array, (uint)new_size);
+ }
+
+
bool resize(size_t new_size, Elem default_val)
{
size_t old_size= elements();
- if (unlikely(allocate_dynamic(&array, (uint)new_size)))
+ if (reserve(new_size))
return true;
if (new_size > old_size)
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index ed175ae4865..3e9379ebe33 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -88,7 +88,7 @@ static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
if (unlikely(!thd->audit_class_plugins.buffer))
{
/* specify some reasonable initialization defaults */
- my_init_dynamic_array(&thd->audit_class_plugins,
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &thd->audit_class_plugins,
sizeof(plugin_ref), 16, 16, MYF(0));
}
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 97317203e34..40276c86a78 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -254,35 +254,36 @@ void mysql_audit_notify_connection_disconnect(THD *thd, int errcode)
}
static inline
-void mysql_audit_notify_connection_change_user(THD *thd)
+void mysql_audit_notify_connection_change_user(THD *thd,
+ const Security_context *old_ctx)
{
if (mysql_audit_connection_enabled())
{
- const Security_context *sctx= thd->security_ctx;
mysql_event_connection event;
event.event_subclass= MYSQL_AUDIT_CONNECTION_CHANGE_USER;
event.status= thd->get_stmt_da()->is_error() ?
thd->get_stmt_da()->sql_errno() : 0;
event.thread_id= (unsigned long)thd->thread_id;
- event.user= sctx->user;
- event.user_length= safe_strlen_uint(sctx->user);
- event.priv_user= sctx->priv_user;
- event.priv_user_length= strlen_uint(sctx->priv_user);
- event.external_user= sctx->external_user;
- event.external_user_length= safe_strlen_uint(sctx->external_user);
- event.proxy_user= sctx->proxy_user;
- event.proxy_user_length= strlen_uint(sctx->proxy_user);
- event.host= sctx->host;
- event.host_length= safe_strlen_uint(sctx->host);
- event.ip= sctx->ip;
- event.ip_length= safe_strlen_uint(sctx->ip);
+ event.user= old_ctx->user;
+ event.user_length= safe_strlen_uint(old_ctx->user);
+ event.priv_user= old_ctx->priv_user;
+ event.priv_user_length= strlen_uint(old_ctx->priv_user);
+ event.external_user= old_ctx->external_user;
+ event.external_user_length= safe_strlen_uint(old_ctx->external_user);
+ event.proxy_user= old_ctx->proxy_user;
+ event.proxy_user_length= strlen_uint(old_ctx->proxy_user);
+ event.host= old_ctx->host;
+ event.host_length= safe_strlen_uint(old_ctx->host);
+ event.ip= old_ctx->ip;
+ event.ip_length= safe_strlen_uint(old_ctx->ip);
event.database= thd->db;
mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
}
}
+
static inline
void mysql_audit_external_lock_ex(THD *thd, my_thread_id thread_id,
const char *user, const char *host, const char *ip, query_id_t query_id,
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 9648f8922c2..2769575f2cb 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -30,9 +30,6 @@
#include "sql_view.h" // mysql_make_view, VIEW_ANY_ACL
#include "sql_parse.h" // check_table_access
#include "sql_insert.h" // kill_delayed_threads
-#include "sql_acl.h" // *_ACL, check_grant_all_columns,
- // check_column_grant_in_table_ref,
- // get_column_grant
#include "sql_partition.h" // ALTER_PARTITION_PARAM_TYPE
#include "sql_derived.h" // mysql_derived_prepare,
// mysql_handle_derived,
@@ -56,7 +53,6 @@
#include "rpl_filter.h"
#include "sql_table.h" // build_table_filename
#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>
@@ -262,7 +258,7 @@ static my_bool list_open_tables_callback(TDC_element *element,
arg->table_list.db.length= db_length;
arg->table_list.table_name.str= table_name;
arg->table_list.table_name.length= strlen(table_name);
- arg->table_list.grant.privilege= 0;
+ arg->table_list.grant.privilege= NO_ACL;
if (check_table_access(arg->thd, SELECT_ACL, &arg->table_list, TRUE, 1, TRUE))
return FALSE;
@@ -312,13 +308,9 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
/**
Close all tables that are not in use in table definition cache
-
- @param purge_flag Argument for tc_purge. true if we should force all
- shares to be deleted. false if it's enough to just
- evict those that are not in use.
*/
-void purge_tables(bool purge_flag)
+void purge_tables()
{
/*
Force close of all open tables.
@@ -332,7 +324,7 @@ void purge_tables(bool purge_flag)
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
*/
- tc_purge(purge_flag);
+ tc_purge();
/* Free table shares which were not freed implicitly by loop above. */
tdc_purge(true);
}
@@ -361,7 +353,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
if (!tables)
{
/* Free tables that are not used */
- purge_tables(false);
+ purge_tables();
if (!wait_for_refresh)
DBUG_RETURN(false);
}
@@ -396,13 +388,12 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
if (! table)
continue;
- if (thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_EXCLUSIVE,
- timeout))
+ if (wait_while_table_is_used(thd, table,
+ HA_EXTRA_PREPARE_FOR_FORCED_CLOSE))
{
result= true;
break;
}
- table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
/*
@@ -442,7 +433,8 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
MDL_request *mdl_request= new (thd->mem_root) MDL_request;
if (mdl_request == NULL)
DBUG_RETURN(true);
- mdl_request->init(&table->mdl_request.key, MDL_EXCLUSIVE, MDL_STATEMENT);
+ MDL_REQUEST_INIT_BY_KEY(mdl_request, &table->mdl_request.key,
+ MDL_EXCLUSIVE, MDL_STATEMENT);
mdl_requests.push_front(mdl_request);
}
@@ -450,8 +442,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
DBUG_RETURN(true);
for (TABLE_LIST *table= tables; table; table= table->next_local)
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str,
- table->table_name.str, false);
+ tdc_remove_table(thd, table->db.str, table->table_name.str);
}
DBUG_RETURN(false);
}
@@ -562,7 +553,7 @@ bool flush_tables(THD *thd, flush_tables_type flag)
flush_tables_error_handler error_handler;
DBUG_ENTER("flush_tables");
- purge_tables(false); /* Flush unused tables and shares */
+ purge_tables(); /* Flush unused tables and shares */
/*
Loop over all shares and collect shares that have open tables
@@ -571,12 +562,12 @@ bool flush_tables(THD *thd, flush_tables_type flag)
write after last time all tables was closed.
*/
- if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table),
+ if (!(tmp_table= (TABLE*) my_malloc(PSI_INSTRUMENT_ME, sizeof(*tmp_table),
MYF(MY_WME | MY_THREAD_SPECIFIC))))
DBUG_RETURN(1);
- my_init_dynamic_array(&collect_arg.shares, sizeof(TABLE_SHARE*), 100, 100,
- MYF(0));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &collect_arg.shares,
+ sizeof(TABLE_SHARE*), 100, 100, MYF(0));
collect_arg.flush_type= flag;
if (tdc_iterate(thd, (my_hash_walk_action) tc_collect_used_shares,
&collect_arg, true))
@@ -607,12 +598,15 @@ bool flush_tables(THD *thd, flush_tables_type flag)
else
{
/*
- HA_OPEN_FOR_ALTER is used to allow us to open the table even if
- TABLE_SHARE::incompatible_version is set.
+ HA_OPEN_FOR_FLUSH is used to allow us to open the table even if
+ TABLE_SHARE::incompatible_version is set. It also will tell
+ SEQUENCE engine that we don't have to read the sequence information
+ (which may cause deadlocks with concurrently running ALTER TABLE or
+ ALTER SEQUENCE) as we will close the table at once.
*/
if (!open_table_from_share(thd, share, &empty_clex_str,
HA_OPEN_KEYFILE, 0,
- HA_OPEN_FOR_ALTER,
+ HA_OPEN_FOR_ALTER | HA_OPEN_FOR_FLUSH,
tmp_table, FALSE,
NULL))
{
@@ -638,93 +632,6 @@ err:
}
-/**
- Close all tables which match specified connection string or
- if specified string is NULL, then any table with a connection string.
-*/
-
-struct close_cached_connection_tables_arg
-{
- THD *thd;
- LEX_CSTRING *connection;
- TABLE_LIST *tables;
-};
-
-
-static my_bool close_cached_connection_tables_callback(
- TDC_element *element, close_cached_connection_tables_arg *arg)
-{
- TABLE_LIST *tmp;
-
- mysql_mutex_lock(&element->LOCK_table_share);
- /* Ignore if table is not open or does not have a connect_string */
- if (!element->share || !element->share->connect_string.length ||
- !element->ref_count)
- goto end;
-
- /* Compare the connection string */
- if (arg->connection &&
- (arg->connection->length > element->share->connect_string.length ||
- (arg->connection->length < element->share->connect_string.length &&
- (element->share->connect_string.str[arg->connection->length] != '/' &&
- element->share->connect_string.str[arg->connection->length] != '\\')) ||
- strncasecmp(arg->connection->str, element->share->connect_string.str,
- arg->connection->length)))
- goto end;
-
- /* close_cached_tables() only uses these elements */
- if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) ||
- !(arg->thd->make_lex_string(&tmp->db, element->share->db.str, element->share->db.length)) ||
- !(arg->thd->make_lex_string(&tmp->table_name, element->share->table_name.str,
- element->share->table_name.length)))
- {
- mysql_mutex_unlock(&element->LOCK_table_share);
- return TRUE;
- }
-
- tmp->next_local= arg->tables;
- arg->tables= tmp;
-
-end:
- mysql_mutex_unlock(&element->LOCK_table_share);
- return FALSE;
-}
-
-
-/**
- Close cached connections
-
- @return false ok
- @return true If there was an error from closed_cached_connection_tables or
- if there was any open connections that we had to force closed
-*/
-
-bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
-{
- bool res= false;
- close_cached_connection_tables_arg argument;
- DBUG_ENTER("close_cached_connections");
- DBUG_ASSERT(thd);
-
- argument.thd= thd;
- argument.connection= connection;
- argument.tables= NULL;
-
- if (tdc_iterate(thd,
- (my_hash_walk_action) close_cached_connection_tables_callback,
- &argument))
- DBUG_RETURN(true);
-
- for (TABLE_LIST *table= argument.tables; table; table= table->next_local)
- res|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table->db.str,
- table->table_name.str, TRUE);
-
- /* Return true if we found any open connections */
- DBUG_RETURN(res);
-}
-
-
/*
Mark all tables in the list which were used by current substatement
as free for reuse.
@@ -741,8 +648,9 @@ bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
Clear 'check_table_binlog_row_based_done' flag. For tables which were used
by current substatement the flag is cleared as part of 'ha_reset()' call.
For the rest of the open tables not used by current substament if this
- flag is enabled as part of current substatement execution, clear the flag
- explicitly.
+ flag is enabled as part of current substatement execution,
+ (for example when THD::binlog_write_table_maps() calls
+ prepare_for_row_logging()), clear the flag explicitly.
NOTE
The reason we reset query_id is that it's not enough to just test
@@ -766,7 +674,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
table->query_id= 0;
table->file->ha_reset();
}
- else if (table->file->check_table_binlog_row_based_done)
+ else
table->file->clear_cached_table_binlog_row_based_flag();
}
DBUG_VOID_RETURN;
@@ -806,11 +714,10 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
TABLE *skip_table)
{
DBUG_ASSERT(!share->tmp_table);
+ DBUG_ASSERT(share->tdc->flushed);
char key[MAX_DBKEY_LENGTH];
size_t key_length= share->table_cache_key.length;
- const char *db= key;
- const char *table_name= db + share->db.length + 1;
bool remove_from_locked_tables= extra != HA_EXTRA_NOT_USED;
memcpy(key, share->table_cache_key.str, key_length);
@@ -847,12 +754,6 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
prev= &table->next;
}
}
- if (skip_table == NULL)
- {
- /* Remove the table share from the cache. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
- FALSE);
- }
}
@@ -874,9 +775,10 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
leave prelocked mode if needed.
*/
-void close_thread_tables(THD *thd)
+int close_thread_tables(THD *thd)
{
TABLE *table;
+ int error= 0;
DBUG_ENTER("close_thread_tables");
THD_STAGE_INFO(thd, stage_closing_tables);
@@ -894,14 +796,11 @@ void close_thread_tables(THD *thd)
DEBUG_SYNC(thd, "before_close_thread_tables");
#endif
- DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt ||
+ DBUG_ASSERT(thd->transaction->stmt.is_empty() || thd->in_sub_stmt ||
(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
for (table= thd->open_tables; table; table= table->next)
{
- if (table->update_handler)
- table->delete_update_handler();
-
/* Table might be in use by some outer statement. */
DBUG_PRINT("tcache", ("table: '%s' query_id: %lu",
table->s->table_name.str, (ulong) table->query_id));
@@ -979,7 +878,7 @@ void close_thread_tables(THD *thd)
we will exit this function a few lines below.
*/
if (! thd->lex->requires_prelocking())
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
/*
We are in the top-level statement of a prelocked statement,
@@ -990,7 +889,7 @@ void close_thread_tables(THD *thd)
thd->locked_tables_mode= LTM_LOCK_TABLES;
if (thd->locked_tables_mode == LTM_LOCK_TABLES)
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
thd->leave_locked_tables_mode();
@@ -1009,7 +908,7 @@ void close_thread_tables(THD *thd)
binlog_query()) or when preparing a pending event.
*/
(void)thd->binlog_flush_pending_rows_event(TRUE);
- mysql_unlock_tables(thd, thd->lock);
+ error= mysql_unlock_tables(thd, thd->lock);
thd->lock=0;
}
/*
@@ -1019,7 +918,7 @@ void close_thread_tables(THD *thd)
while (thd->open_tables)
(void) close_thread_table(thd, &thd->open_tables);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(error);
}
@@ -1405,21 +1304,24 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
{
DBUG_ENTER("wait_while_table_is_used");
DBUG_ASSERT(!table->s->tmp_table);
- DBUG_PRINT("enter", ("table: '%s' share: %p db_stat: %u version: %lld",
+ DBUG_PRINT("enter", ("table: '%s' share: %p db_stat: %u",
table->s->table_name.str, table->s,
- table->db_stat, table->s->tdc->version));
+ table->db_stat));
if (thd->mdl_context.upgrade_shared_lock(
table->mdl_ticket, MDL_EXCLUSIVE,
thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
- tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
- table->s->db.str, table->s->table_name.str,
- FALSE);
+ table->s->tdc->flush(thd, true);
/* extra() call must come only after all instances above are closed */
if (function != HA_EXTRA_NOT_USED)
- DBUG_RETURN(table->file->extra(function));
+ {
+ int error= table->file->extra(function);
+ if (error)
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
DBUG_RETURN(FALSE);
}
@@ -1455,10 +1357,8 @@ void drop_open_table(THD *thd, TABLE *table, const LEX_CSTRING *db_name,
handlerton *table_type= table->s->db_type();
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
+ table->s->tdc->flush(thd, true);
close_thread_table(thd, &thd->open_tables);
- /* Remove the table share from the table cache. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name->str, table_name->str,
- FALSE);
/* Remove the table from the storage engine and rm the .frm. */
quick_rm_table(thd, table_type, db_name, table_name, 0);
}
@@ -1587,10 +1487,9 @@ open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
DBUG_ASSERT(!(flags & MYSQL_OPEN_FORCE_SHARED_MDL) ||
!(flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL));
- mdl_request_shared.init(&mdl_request->key,
- (flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
- MDL_SHARED : MDL_SHARED_HIGH_PRIO,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT_BY_KEY(&mdl_request_shared, &mdl_request->key,
+ flags & MYSQL_OPEN_FORCE_SHARED_MDL ? MDL_SHARED : MDL_SHARED_HIGH_PRIO,
+ MDL_TRANSACTION);
mdl_request= &mdl_request_shared;
}
@@ -1676,9 +1575,10 @@ static int set_partitions_as_used(TABLE_LIST *tl, TABLE *t)
needed to remedy problem before retrying again.
@retval FALSE 't' was not locked, not a VIEW or an error happened.
*/
+
bool is_locked_view(THD *thd, TABLE_LIST *t)
{
- DBUG_ENTER("check_locked_view");
+ DBUG_ENTER("is_locked_view");
/*
Is this table a view and not a base table?
(it is work around to allow to open view with locked tables,
@@ -2027,8 +1927,6 @@ retry_share:
{
if (share->tdc->flushed)
{
- DBUG_PRINT("info", ("Found old share version: %lld current: %lld",
- 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
@@ -2080,7 +1978,14 @@ retry_share:
if (table)
{
DBUG_ASSERT(table->file != NULL);
- MYSQL_REBIND_TABLE(table->file);
+ if (table->file->discover_check_version())
+ {
+ tc_release_table(table);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
+ table_list);
+ DBUG_RETURN(TRUE);
+ }
+ table->file->rebind_psi();
#ifdef WITH_PARTITION_STORAGE_ENGINE
part_names_error= set_partitions_as_used(table_list, table);
#endif
@@ -2089,7 +1994,8 @@ retry_share:
{
enum open_frm_error error;
/* make a new table */
- if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
+ if (!(table=(TABLE*) my_malloc(key_memory_TABLE, sizeof(*table),
+ MYF(MY_WME))))
goto err_lock;
error= open_table_from_share(thd, share, &table_list->alias,
@@ -2175,8 +2081,8 @@ retry_share:
DBUG_RETURN(TRUE);
}
- protection_request.init(MDL_key::BACKUP, "", "", mdl_type,
- MDL_STATEMENT);
+ MDL_REQUEST_INIT(&protection_request, MDL_key::BACKUP, "", "", mdl_type,
+ MDL_STATEMENT);
/*
Install error handler which if possible will convert deadlock error
@@ -2238,13 +2144,14 @@ retry_share:
DBUG_RETURN(true);
}
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(thd->locked_tables_mode || table->file->row_logging == 0);
+ DBUG_RETURN(false);
err_lock:
tdc_release_share(share);
DBUG_PRINT("exit", ("failed"));
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(true);
}
@@ -2436,9 +2343,10 @@ Locked_tables_list::init_locked_tables(THD *thd)
@note This function is a no-op if we're not in LOCK TABLES.
*/
-void
+int
Locked_tables_list::unlock_locked_tables(THD *thd)
{
+ int error;
DBUG_ASSERT(!thd->in_sub_stmt &&
!(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
/*
@@ -2448,7 +2356,7 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
open tables, e.g. from begin_trans().
*/
if (thd->locked_tables_mode != LTM_LOCK_TABLES)
- return;
+ return 0;
for (TABLE_LIST *table_list= m_locked_tables;
table_list; table_list= table_list->next_global)
@@ -2464,8 +2372,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
TRANSACT_TRACKER(clear_trx_state(thd, TX_LOCKED_TABLES));
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
- close_thread_tables(thd);
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
+ error= close_thread_tables(thd);
/*
We rely on the caller to implicitly commit the
@@ -2477,6 +2385,7 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
request for metadata locks and TABLE_LIST elements.
*/
reset();
+ return error;
}
@@ -2485,7 +2394,7 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
table mode if there is no locked tables anymore
*/
-void
+int
Locked_tables_list::unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket)
{
/*
@@ -2494,7 +2403,7 @@ Locked_tables_list::unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket)
to check this condition here than in the caller.
*/
if (thd->locked_tables_mode != LTM_LOCK_TABLES)
- return;
+ return 0;
if (mdl_ticket)
{
@@ -2507,7 +2416,8 @@ Locked_tables_list::unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket)
}
if (thd->lock->table_count == 0)
- unlock_locked_tables(thd);
+ return unlock_locked_tables(thd);
+ return 0;
}
@@ -2699,7 +2609,8 @@ Locked_tables_list::reopen_tables(THD *thd, bool need_reopen)
/* Reset flag that some table was marked for reopen */
- some_table_marked_for_reopen= 0;
+ if (need_reopen)
+ some_table_marked_for_reopen= 0;
for (TABLE_LIST *table_list= m_locked_tables;
table_list; table_list= table_list->next_global)
@@ -3066,16 +2977,13 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
{
TABLE_SHARE *share;
- TABLE *entry;
+ TABLE entry;
bool result= TRUE;
thd->clear_error();
- if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
- return result;
-
if (!(share= tdc_acquire_share(thd, table_list, GTS_TABLE)))
- goto end_free;
+ return result;
DBUG_ASSERT(! share->is_view);
@@ -3083,31 +2991,25 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
HA_OPEN_KEYFILE | HA_TRY_READ_ONLY,
EXTRA_RECORD,
ha_open_options | HA_OPEN_FOR_REPAIR,
- entry, FALSE) || ! entry->file ||
- (entry->file->is_crashed() && entry->file->ha_check_and_repair(thd)))
+ &entry, FALSE) || ! entry.file ||
+ (entry.file->is_crashed() && entry.file->ha_check_and_repair(thd)))
{
/* Give right error message */
thd->clear_error();
my_error(ER_NOT_KEYFILE, MYF(0), share->table_name.str);
sql_print_error("Couldn't repair table: %s.%s", share->db.str,
share->table_name.str);
- if (entry->file)
- closefrm(entry);
+ if (entry.file)
+ closefrm(&entry);
}
else
{
thd->clear_error(); // Clear error message
- closefrm(entry);
+ closefrm(&entry);
result= FALSE;
}
- tdc_release_share(share);
- /* Remove the repaired share from the table cache. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- table_list->db.str, table_list->table_name.str,
- FALSE);
-end_free:
- my_free(entry);
+ tdc_remove_referenced_share(thd, share);
return result;
}
@@ -3266,65 +3168,57 @@ Open_table_context::recover_from_failed_open()
switch (m_action)
{
case OT_BACKOFF_AND_RETRY:
- break;
case OT_REOPEN_TABLES:
break;
case OT_DISCOVER:
- {
- if ((result= lock_table_names(m_thd, m_thd->lex->create_info,
- m_failed_table, NULL,
- get_timeout(), 0)))
- break;
-
- tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str,
- m_failed_table->table_name.str, FALSE);
-
- m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id);
- m_thd->clear_error(); // Clear error message
+ case OT_REPAIR:
+ if ((result= lock_table_names(m_thd, m_thd->lex->create_info,
+ m_failed_table, NULL,
+ get_timeout(), 0)))
+ break;
- No_such_table_error_handler no_such_table_handler;
- bool open_if_exists= m_failed_table->open_strategy == TABLE_LIST::OPEN_IF_EXISTS;
+ tdc_remove_table(m_thd, m_failed_table->db.str,
+ m_failed_table->table_name.str);
- if (open_if_exists)
- m_thd->push_internal_handler(&no_such_table_handler);
-
- result= !tdc_acquire_share(m_thd, m_failed_table,
- GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK);
- if (open_if_exists)
+ switch (m_action)
+ {
+ case OT_DISCOVER:
{
- m_thd->pop_internal_handler();
- if (result && no_such_table_handler.safely_trapped_errors())
- result= FALSE;
- }
+ m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id);
+ m_thd->clear_error(); // Clear error message
- /*
- Rollback to start of the current statement to release exclusive lock
- on table which was discovered but preserve locks from previous statements
- in current transaction.
- */
- m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
- break;
- }
- case OT_REPAIR:
- {
- if ((result= lock_table_names(m_thd, m_thd->lex->create_info,
- m_failed_table, NULL,
- get_timeout(), 0)))
- break;
+ No_such_table_error_handler no_such_table_handler;
+ bool open_if_exists= m_failed_table->open_strategy == TABLE_LIST::OPEN_IF_EXISTS;
- tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db.str,
- m_failed_table->table_name.str, FALSE);
+ if (open_if_exists)
+ m_thd->push_internal_handler(&no_such_table_handler);
- result= auto_repair_table(m_thd, m_failed_table);
- /*
- Rollback to start of the current statement to release exclusive lock
- on table which was discovered but preserve locks from previous statements
- in current transaction.
- */
- m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
- break;
+ result= !tdc_acquire_share(m_thd, m_failed_table,
+ 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;
+ }
+ break;
+ }
+ case OT_REPAIR:
+ result= auto_repair_table(m_thd, m_failed_table);
+ break;
+ case OT_BACKOFF_AND_RETRY:
+ case OT_REOPEN_TABLES:
+ case OT_NO_ACTION:
+ DBUG_ASSERT(0);
}
- default:
+ /*
+ Rollback to start of the current statement to release exclusive lock
+ on table which was discovered but preserve locks from previous statements
+ in current transaction.
+ */
+ m_thd->mdl_context.rollback_to_savepoint(start_of_statement_svp());
+ break;
+ case OT_NO_ACTION:
DBUG_ASSERT(0);
}
m_thd->pop_internal_handler();
@@ -3805,10 +3699,11 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
temporary table or SEQUENCE (see sequence_insert()).
*/
DBUG_ASSERT(is_temporary_table(tables) || tables->table->s->sequence);
- if (tables->sequence && tables->table->s->table_type != TABLE_TYPE_SEQUENCE)
+ if (tables->sequence &&
+ tables->table->s->table_type != TABLE_TYPE_SEQUENCE)
{
- my_error(ER_NOT_SEQUENCE, MYF(0), tables->db.str, tables->alias.str);
- DBUG_RETURN(true);
+ my_error(ER_NOT_SEQUENCE, MYF(0), tables->db.str, tables->alias.str);
+ DBUG_RETURN(true);
}
}
else if (tables->open_type == OT_TEMPORARY_ONLY)
@@ -3862,9 +3757,9 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
The problem is that since those attributes are not set in merge
children, another round of PREPARE will not help.
*/
- error= thd->open_temporary_table(tables);
-
- if (!error && !tables->table)
+ if (!thd->has_temporary_tables() ||
+ (!(error= thd->open_temporary_table(tables)) &&
+ !tables->table))
error= open_table(thd, tables, ot_ctx);
thd->pop_internal_handler();
@@ -3881,9 +3776,9 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
Repair_mrg_table_error_handler repair_mrg_table_handler;
thd->push_internal_handler(&repair_mrg_table_handler);
- error= thd->open_temporary_table(tables);
-
- if (!error && !tables->table)
+ if (!thd->has_temporary_tables() ||
+ (!(error= thd->open_temporary_table(tables)) &&
+ !tables->table))
error= open_table(thd, tables, ot_ctx);
thd->pop_internal_handler();
@@ -3898,7 +3793,8 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags,
still might need to look for a temporary table if this table
list element corresponds to underlying table of a merge table.
*/
- error= thd->open_temporary_table(tables);
+ if (thd->has_temporary_tables())
+ error= thd->open_temporary_table(tables);
}
if (!error && !tables->table)
@@ -4021,7 +3917,8 @@ static bool upgrade_lock_if_not_exists(THD *thd,
{
DEBUG_SYNC(thd,"create_table_before_check_if_exists");
if (!create_info.or_replace() &&
- ha_table_exists(thd, &create_table->db, &create_table->table_name))
+ ha_table_exists(thd, &create_table->db, &create_table->table_name,
+ &create_table->db_type))
{
if (create_info.if_not_exists())
{
@@ -4069,7 +3966,8 @@ static bool upgrade_lock_if_not_exists(THD *thd,
Note that for CREATE TABLE IF EXISTS we only generate a warning
but still return TRUE (to abort the calling open_table() function).
On must check THD->is_error() if one wants to distinguish between warning
- and error.
+ and error. If table existed, tables_start->db_type is set to the handlerton
+ for the found table.
*/
bool
@@ -4111,9 +4009,8 @@ lock_table_names(THD *thd, const DDL_options_st &options,
MDL_request *schema_request= new (thd->mem_root) MDL_request;
if (schema_request == NULL)
DBUG_RETURN(TRUE);
- schema_request->init(MDL_key::SCHEMA, table->db.str, "",
- MDL_INTENTION_EXCLUSIVE,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(schema_request, MDL_key::SCHEMA, table->db.str, "",
+ MDL_INTENTION_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(schema_request);
}
@@ -4135,7 +4032,8 @@ lock_table_names(THD *thd, const DDL_options_st &options,
if (thd->has_read_only_protection())
DBUG_RETURN(true);
- global_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DDL, MDL_STATEMENT);
+ MDL_REQUEST_INIT(&global_request, MDL_key::BACKUP, "", "", MDL_BACKUP_DDL,
+ MDL_STATEMENT);
mdl_savepoint= thd->mdl_context.mdl_savepoint();
while (!thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout) &&
@@ -4286,7 +4184,7 @@ bool open_tables(THD *thd, const DDL_options_st &options,
DBUG_ENTER("open_tables");
/* Data access in XA transaction is only allowed when it is active. */
- if (*start && thd->transaction.xid_state.check_has_uncommitted_xa())
+ if (*start && thd->transaction->xid_state.check_has_uncommitted_xa())
DBUG_RETURN(true);
thd->current_tablenr= 0;
@@ -4428,7 +4326,7 @@ restart:
list, we still need to call open_and_process_routine() to take
MDL locks on the routines.
*/
- if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
+ if (thd->locked_tables_mode <= LTM_LOCK_TABLES && *sroutine_to_open)
{
/*
Process elements of the prelocking set which are present there
@@ -5290,7 +5188,9 @@ bool open_and_lock_tables(THD *thd, const DDL_options_st &options,
if (lock_tables(thd, tables, counter, flags))
goto err;
- (void) read_statistics_for_tables_if_needed(thd, tables);
+ /* Don't read statistics tables when opening internal tables */
+ if (!(flags & MYSQL_OPEN_IGNORE_LOGGING_FORMAT))
+ (void) read_statistics_for_tables_if_needed(thd, tables);
if (derived)
{
@@ -5353,7 +5253,7 @@ end:
table on the fly, and thus mustn't manipulate with the
transaction of the enclosing statement.
*/
- DBUG_ASSERT(thd->transaction.stmt.is_empty() ||
+ DBUG_ASSERT(thd->transaction->stmt.is_empty() ||
(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
close_thread_tables(thd);
/* Don't keep locks for a failed statement. */
@@ -5424,12 +5324,19 @@ bool open_tables_only_view_structure(THD *thd, TABLE_LIST *table_list,
static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list)
{
TABLE_LIST *table;
+ DBUG_ENTER("mark_real_tables_as_free_for_reuse");
+
+ /*
+ We have to make two loops as HA_EXTRA_DETACH_CHILDREN may
+ remove items from the table list that we have to reset
+ */
for (table= table_list; table; table= table->next_global)
+ {
if (!table->placeholder())
- {
table->table->query_id= 0;
- }
+ }
for (table= table_list; table; table= table->next_global)
+ {
if (!table->placeholder())
{
/*
@@ -5440,6 +5347,8 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list)
*/
table->table->file->extra(HA_EXTRA_DETACH_CHILDREN);
}
+ }
+ DBUG_VOID_RETURN;
}
int TABLE::fix_vcol_exprs(THD *thd)
@@ -5513,7 +5422,7 @@ static bool fix_all_session_vcol_exprs(THD *thd, TABLE_LIST *tables)
bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
{
- TABLE_LIST *table;
+ TABLE_LIST *table, *first_not_own;
DBUG_ENTER("lock_tables");
/*
We can't meet statement requiring prelocking if we already
@@ -5523,7 +5432,9 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
!thd->lex->requires_prelocking());
if (!tables && !thd->lex->requires_prelocking())
- DBUG_RETURN(thd->decide_logging_format(tables));
+ DBUG_RETURN(0);
+
+ first_not_own= thd->lex->first_not_own_table();
/*
Check for thd->locked_tables_mode to avoid a redundant
@@ -5539,13 +5450,26 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
{
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
TABLE **start,**ptr;
+ bool found_first_not_own= 0;
if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count)))
DBUG_RETURN(TRUE);
+
+ /*
+ Collect changes tables for table lock.
+ Mark own tables with query id as this is needed by
+ prepare_for_row_logging()
+ */
for (table= tables; table; table= table->next_global)
{
+ if (table == first_not_own)
+ found_first_not_own= 1;
if (!table->placeholder())
- *(ptr++)= table->table;
+ {
+ *(ptr++)= table->table;
+ if (!found_first_not_own)
+ table->table->query_id= thd->query_id;
+ }
}
DEBUG_SYNC(thd, "before_lock_tables_takes_lock");
@@ -5559,7 +5483,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
if (thd->lex->requires_prelocking() &&
thd->lex->sql_command != SQLCOM_LOCK_TABLES)
{
- TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
/*
We just have done implicit LOCK TABLES, and now we have
to emulate first open_and_lock_tables() after it.
@@ -5577,7 +5500,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
{
if (!table->placeholder())
{
- table->table->query_id= thd->query_id;
if (check_lock_and_start_stmt(thd, thd->lex, table))
{
mysql_unlock_tables(thd, thd->lock);
@@ -5597,7 +5519,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
}
else
{
- TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
/*
When open_and_lock_tables() is called for a single table out of
a table list, the 'next_global' chain is temporarily broken. We
@@ -5613,6 +5534,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
if (table->placeholder())
continue;
+ table->table->query_id= thd->query_id;
/*
In a stored function or trigger we should ensure that we won't change
a table that is already used by the calling statement.
@@ -5652,7 +5574,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, uint flags)
}
bool res= fix_all_session_vcol_exprs(thd, tables);
- if (!res)
+ if (!res && !(flags & MYSQL_OPEN_IGNORE_LOGGING_FORMAT))
res= thd->decide_logging_format(tables);
DBUG_RETURN(res);
@@ -5737,7 +5659,7 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
table on the fly, and thus mustn't manipulate with the
transaction of the enclosing statement.
*/
- DBUG_ASSERT(thd->transaction.stmt.is_empty() ||
+ DBUG_ASSERT(thd->transaction->stmt.is_empty() ||
(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(start_of_statement_svp);
@@ -5853,9 +5775,8 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
replace. If the item was aliased by the user, set the alias to
the replacing item.
*/
- if (*ref && !(*ref)->is_autogenerated_name)
- item->set_name(thd, (*ref)->name.str, (*ref)->name.length,
- system_charset_info);
+ if (*ref && !(*ref)->is_autogenerated_name())
+ item->set_name(thd, (*ref)->name);
if (register_tree_change)
thd->change_item_tree(ref, item);
else
@@ -5945,9 +5866,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, si
replace. If the item was aliased by the user, set the alias to
the replacing item.
*/
- if (*ref && !(*ref)->is_autogenerated_name)
- item->set_name(thd, (*ref)->name.str, (*ref)->name.length,
- system_charset_info);
+ if (*ref && !(*ref)->is_autogenerated_name())
+ item->set_name(thd, (*ref)->name);
if (register_tree_change && arena)
thd->restore_active_arena(arena, &backup);
@@ -6363,8 +6283,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
bool check_privileges, bool register_tree_change)
{
Field *found=0;
- const char *db= item->db_name;
- const char *table_name= item->table_name;
+ const char *db= item->db_name.str;
+ const char *table_name= item->table_name.str;
const char *name= item->field_name.str;
size_t length= item->field_name.length;
char name_buff[SAFE_NAME_LEN+1];
@@ -6436,10 +6356,11 @@ find_field_in_tables(THD *thd, Item_ident *item,
for (SELECT_LEX *sl= current_sel; sl && sl!=last_select;
sl=sl->outer_select())
{
- Item *subs= sl->master_unit()->item;
- if (subs->type() == Item::SUBSELECT_ITEM &&
- ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS &&
- ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN))
+ Item_in_subselect *in_subs=
+ sl->master_unit()->item->get_IN_subquery();
+ if (in_subs &&
+ in_subs->substype() == Item_subselect::IN_SUBS &&
+ in_subs->test_strategy(SUBS_SEMI_JOIN))
{
continue;
}
@@ -6640,8 +6561,8 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
if (is_ref_by_name)
{
field_name= &((Item_ident*) find)->field_name;
- table_name= ((Item_ident*) find)->table_name;
- db_name= ((Item_ident*) find)->db_name;
+ table_name= ((Item_ident*) find)->table_name.str;
+ db_name= ((Item_ident*) find)->db_name.str;
}
for (uint i= 0; i < n_items; i++)
@@ -6681,13 +6602,13 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
item_field->field_name and item_field->table_name can be 0x0 if
item is not fix_field()'ed yet.
*/
- if (item_field->field_name.str && item_field->table_name &&
+ if (item_field->field_name.str && item_field->table_name.str &&
!lex_string_cmp(system_charset_info, &item_field->field_name,
field_name) &&
- !my_strcasecmp(table_alias_charset, item_field->table_name,
+ !my_strcasecmp(table_alias_charset, item_field->table_name.str,
table_name) &&
- (!db_name || (item_field->db_name &&
- !strcmp(item_field->db_name, db_name))))
+ (!db_name || (item_field->db_name.str &&
+ !strcmp(item_field->db_name.str, db_name))))
{
if (found_unaliased)
{
@@ -7545,17 +7466,16 @@ static bool setup_natural_join_row_types(THD *thd,
****************************************************************************/
int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
- List<Item> *sum_func_list,
- uint wild_num, uint *hidden_bit_fields)
+ List<Item> *sum_func_list, SELECT_LEX *select_lex)
{
- if (!wild_num)
- return(0);
-
Item *item;
List_iterator<Item> it(fields);
Query_arena *arena, backup;
DBUG_ENTER("setup_wild");
+ if (!select_lex->with_wild)
+ DBUG_RETURN(0);
+
/*
Don't use arena if we are not in prepared statements or stored procedures
For PS/SP we have to use arena to remember the changes
@@ -7563,7 +7483,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
arena= thd->activate_stmt_arena_if_needed(&backup);
thd->lex->current_select->cur_pos_in_select_list= 0;
- while (wild_num && (item= it++))
+ while (select_lex->with_wild && (item= it++))
{
if (item->type() == Item::FIELD_ITEM &&
((Item_field*) item)->field_name.str == star_clex_str.str &&
@@ -7584,9 +7504,9 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
MY_INT64_NUM_DECIMAL_DIGITS));
}
else if (insert_fields(thd, ((Item_field*) item)->context,
- ((Item_field*) item)->db_name,
- ((Item_field*) item)->table_name, &it,
- any_privileges, hidden_bit_fields))
+ ((Item_field*) item)->db_name.str,
+ ((Item_field*) item)->table_name.str, &it,
+ any_privileges, &select_lex->hidden_bit_fields))
{
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -7601,30 +7521,15 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
*/
sum_func_list->elements+= fields.elements - elem;
}
- wild_num--;
+ select_lex->with_wild--;
}
else
thd->lex->current_select->cur_pos_in_select_list++;
}
+ DBUG_ASSERT(!select_lex->with_wild);
thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
if (arena)
- {
- /* make * substituting permanent */
- SELECT_LEX *select_lex= thd->lex->current_select;
- select_lex->with_wild= 0;
-#ifdef HAVE_valgrind
- if (&select_lex->item_list != &fields) // Avoid warning
-#endif
- /*
- The assignment below is translated to memcpy() call (at least on some
- platforms). memcpy() expects that source and destination areas do not
- overlap. That problem was detected by valgrind.
- */
- if (&select_lex->item_list != &fields)
- select_lex->item_list= fields;
-
thd->restore_active_arena(arena, &backup);
- }
DBUG_RETURN(0);
}
@@ -7734,6 +7639,26 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
/*
+ Perform checks like all given fields exists, if exists fill struct with
+ current data and expand all '*' in given fields for LEX::returning.
+
+ SYNOPSIS
+ thd Thread handler
+ table_list Global/local table list
+*/
+
+int setup_returning_fields(THD* thd, TABLE_LIST* table_list)
+{
+ if (!thd->lex->has_returning())
+ return 0;
+ return setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL,
+ thd->lex->returning())
+ || setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list,
+ MARK_COLUMNS_READ, NULL, NULL, false);
+}
+
+
+/*
make list of leaves of join table tree
SYNOPSIS
@@ -7958,9 +7883,12 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
*/
bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context,
List<TABLE_LIST> *from_clause,
- TABLE_LIST *tables, List<TABLE_LIST> &leaves,
- bool select_insert, ulong want_access_first,
- ulong want_access, bool full_table_list)
+ TABLE_LIST *tables,
+ List<TABLE_LIST> &leaves,
+ bool select_insert,
+ privilege_t want_access_first,
+ privilege_t want_access,
+ bool full_table_list)
{
DBUG_ENTER("setup_tables_and_check_access");
@@ -7970,7 +7898,7 @@ bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context,
List_iterator<TABLE_LIST> ti(leaves);
TABLE_LIST *table_list;
- ulong access= want_access_first;
+ privilege_t access= want_access_first;
while ((table_list= ti++))
{
if (table_list->belong_to_view && !table_list->view &&
@@ -8014,7 +7942,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
name->length(), 1)) <=
0)
{
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), name->c_ptr(),
+ my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0), name->c_ptr(),
table->pos_in_table_list->alias.str);
map->set_all();
return 1;
@@ -8330,7 +8258,7 @@ bool setup_on_expr(THD *thd, TABLE_LIST *table, bool is_update)
*/
if (embedded->sj_subq_pred)
{
- Item **left_expr= &embedded->sj_subq_pred->left_expr;
+ Item **left_expr= embedded->sj_subq_pred->left_exp_ptr();
if ((*left_expr)->fix_fields_if_needed(thd, left_expr))
return TRUE;
}
@@ -8799,8 +8727,8 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
if (unlikely(field->invisible))
continue;
- else
- value=v++;
+
+ value=v++;
bool vers_sys_field= table->versioned() && field->vers_sys_field();
@@ -8897,7 +8825,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr,
my_bool mysql_rm_tmp_tables(void)
{
uint i, idx;
- char filePath[FN_REFLEN], *tmpdir, filePathCopy[FN_REFLEN];
+ char path[FN_REFLEN], *tmpdir, path_copy[FN_REFLEN];
MY_DIR *dirp;
FILEINFO *file;
TABLE_SHARE share;
@@ -8926,23 +8854,17 @@ my_bool mysql_rm_tmp_tables(void)
{
char *ext= fn_ext(file->name);
size_t ext_len= strlen(ext);
- size_t filePath_len= my_snprintf(filePath, sizeof(filePath),
+ size_t path_len= my_snprintf(path, sizeof(path),
"%s%c%s", tmpdir, FN_LIBCHAR,
file->name);
if (!strcmp(reg_ext, ext))
{
- handler *handler_file= 0;
/* We should cut file extention before deleting of table */
- 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) &&
- ((handler_file= get_new_handler(&share, thd->mem_root,
- share.db_type()))))
- {
- handler_file->ha_delete_table(filePathCopy);
- delete handler_file;
- }
+ memcpy(path_copy, path, path_len - ext_len);
+ path_copy[path_len - ext_len]= 0;
+ init_tmp_table_share(thd, &share, "", 0, "", path_copy);
+ if (!open_table_def(thd, &share))
+ share.db_type()->drop_table(share.db_type(), path_copy);
free_table_share(&share);
}
/*
@@ -8950,7 +8872,7 @@ my_bool mysql_rm_tmp_tables(void)
So we hide error messages which happnes during deleting of these
files(MYF(0)).
*/
- (void) mysql_file_delete(key_file_misc, filePath, MYF(0));
+ (void) mysql_file_delete(key_file_misc, path, MYF(0));
}
}
my_dirend(dirp);
@@ -9032,17 +8954,16 @@ bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b)
open_system_tables_for_read()
thd Thread context.
table_list List of tables to open.
- backup Pointer to Open_tables_state instance where
- information about currently open tables will be
- saved, and from which will be restored when we will
- end work with system tables.
NOTES
+ Caller should have used start_new_trans object to start a new
+ transcation when reading system tables.
+
Thanks to restrictions which we put on opening and locking of
system tables for writing, we can open and lock them for reading
- even when we already have some other tables open and locked. One
- must call close_system_tables() to close systems tables opened
- with this call.
+ even when we already have some other tables open and locked.
+ One should call thd->commit_whole_transaction_and_close_tables()
+ to close systems tables opened with this call.
NOTES
In some situations we use this function to open system tables for
@@ -9056,22 +8977,20 @@ bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b)
*/
bool
-open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
- Open_tables_backup *backup)
+open_system_tables_for_read(THD *thd, TABLE_LIST *table_list)
{
Query_tables_list query_tables_list_backup;
LEX *lex= thd->lex;
DBUG_ENTER("open_system_tables_for_read");
+ DBUG_ASSERT(thd->internal_transaction());
/*
Besides using new Open_tables_state for opening system tables,
we also have to backup and reset/and then restore part of LEX
which is accessed by open_tables() in order to determine if
prelocking is needed and what tables should be added for it.
- close_system_tables() doesn't require such treatment.
*/
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
- thd->reset_n_backup_open_tables_state(backup);
thd->lex->sql_command= SQLCOM_SELECT;
/*
@@ -9081,17 +9000,18 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
*/
if (open_and_lock_tables(thd, table_list, FALSE,
(MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_IGNORE_LOGGING_FORMAT |
(table_list->lock_type < TL_WRITE_ALLOW_WRITE ?
MYSQL_LOCK_IGNORE_TIMEOUT : 0))))
{
lex->restore_backup_query_tables_list(&query_tables_list_backup);
- thd->restore_backup_open_tables_state(backup);
DBUG_RETURN(TRUE);
}
for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
{
DBUG_ASSERT(tables->table->s->table_category == TABLE_CATEGORY_SYSTEM);
+ tables->table->file->row_logging= 0;
tables->table->use_all_columns();
}
lex->restore_backup_query_tables_list(&query_tables_list_backup);
@@ -9099,33 +9019,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
DBUG_RETURN(FALSE);
}
-
-/*
- Close system tables, opened with open_system_tables_for_read().
-
- SYNOPSIS
- close_system_tables()
- thd Thread context
- backup Pointer to Open_tables_backup instance which holds
- information about tables which were open before we
- decided to access system tables.
-*/
-
-void
-close_system_tables(THD *thd, Open_tables_backup *backup)
-{
- /*
- Inform the transaction handler that we are closing the
- system tables and we don't need the read view anymore.
- */
- for (TABLE *table= thd->open_tables ; table ; table= table->next)
- table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
-
- close_thread_tables(thd);
- thd->restore_backup_open_tables_state(backup);
-}
-
-
/**
A helper function to close a mysql.* table opened
in an auxiliary THD during bootstrap or in the main
@@ -9179,8 +9072,9 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
{
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM);
table->use_all_columns();
+ /* This table instance is not row logged */
+ table->file->row_logging= 0;
}
-
DBUG_RETURN(table);
}
@@ -9213,6 +9107,8 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
if ((table= open_ltable(thd, one_table, one_table->lock_type, flags)))
{
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG);
+ DBUG_ASSERT(!table->file->row_logging);
+
/* Make sure all columns get assigned to a default value */
table->use_all_columns();
DBUG_ASSERT(table->s->no_replicate);
@@ -9231,9 +9127,17 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
@param thd The current thread
@param backup [in] the context to restore.
*/
+
void close_log_table(THD *thd, Open_tables_backup *backup)
{
- close_system_tables(thd, backup);
+ /*
+ Inform the transaction handler that we are closing the
+ system tables and we don't need the read view anymore.
+ */
+ for (TABLE *table= thd->open_tables ; table ; table= table->next)
+ table->file->extra(HA_EXTRA_PREPARE_FOR_FORCED_CLOSE);
+ close_thread_tables(thd);
+ thd->restore_backup_open_tables_state(backup);
}
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 28a787c56dd..ccfacaf9086 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -125,6 +125,11 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
*/
#define MYSQL_OPEN_IGNORE_REPAIR 0x10000
+/**
+ Don't call decide_logging_format. Used for statistic tables etc
+*/
+#define MYSQL_OPEN_IGNORE_LOGGING_FORMAT 0x20000
+
/** Please refer to the internals manual. */
#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\
MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |\
@@ -155,7 +160,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST *TABLE_LIST::*link,
const LEX_CSTRING *db_name,
const LEX_CSTRING *table_name);
-void close_thread_tables(THD *thd);
+int close_thread_tables(THD *thd);
void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *);
void switch_defaults_to_nullable_trigger_fields(TABLE *table);
bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
@@ -175,7 +180,8 @@ bool insert_fields(THD *thd, Name_resolution_context *context,
void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables,
bool full_table_list, TABLE_LIST *boundary);
int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
- List<Item> *sum_func_list, uint wild_num, uint * hidden_bit_fields);
+ List<Item> *sum_func_list, SELECT_LEX *sl);
+int setup_returning_fields(THD* thd, TABLE_LIST* table_list);
bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &item, enum_column_usage column_usage,
List<Item> *sum_func_list, List<Item> *pre_fix,
@@ -217,8 +223,8 @@ bool setup_tables_and_check_access(THD *thd,
TABLE_LIST *tables,
List<TABLE_LIST> &leaves,
bool select_insert,
- ulong want_access_first,
- ulong want_access,
+ privilege_t want_access_first,
+ privilege_t want_access,
bool full_table_list);
bool wait_while_table_is_used(THD *thd, TABLE *table,
enum ha_extra_function function);
@@ -288,9 +294,8 @@ bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *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);
-void close_system_tables(THD *thd, Open_tables_backup *backup);
+bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list);
+void close_system_tables(THD *thd);
void close_mysql_tables(THD *thd);
TABLE *open_system_table_for_update(THD *thd, TABLE_LIST *one_table);
TABLE *open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup);
@@ -298,9 +303,8 @@ void close_log_table(THD *thd, Open_tables_backup *backup);
bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout);
-void purge_tables(bool purge_flag);
+void purge_tables();
bool flush_tables(THD *thd, flush_tables_type flag);
-bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connect_string);
void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
ha_extra_function extra,
TABLE *skip_table);
diff --git a/sql/sql_basic_types.h b/sql/sql_basic_types.h
index 170e93741ef..3200228618f 100644
--- a/sql/sql_basic_types.h
+++ b/sql/sql_basic_types.h
@@ -23,6 +23,8 @@
typedef ulonglong sql_mode_t;
typedef int64 query_id_t;
+enum enum_nullability { NOT_NULL, NULLABLE };
+
/*
"fuzzydate" with strict data type control.
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index f1326d97a21..0011487f1dc 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -144,7 +144,7 @@ int binlog_defragment(THD *thd)
}
thd->lex->comment.str= // to be freed by the caller
- (char *) my_malloc(thd->lex->comment.length, MYF(MY_WME));
+ (char *) my_malloc(PSI_INSTRUMENT_ME, thd->lex->comment.length, MYF(MY_WME));
if (!thd->lex->comment.str)
{
my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1);
@@ -190,7 +190,7 @@ void mysql_client_binlog_statement(THD* thd)
thd->lex->comment.length : 2048),
thd->lex->comment.str));
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_BINLOG))
DBUG_VOID_RETURN;
/*
@@ -211,7 +211,7 @@ void mysql_client_binlog_statement(THD* thd)
size_t coded_len= 0, decoded_len= 0;
rli= thd->rli_fake;
- if (!rli && (rli= thd->rli_fake= new Relay_log_info(FALSE)))
+ if (!rli && (rli= thd->rli_fake= new Relay_log_info(FALSE, "BINLOG_BASE64_EVENT")))
rli->sql_driver_thd= thd;
if (!(rgi= thd->rgi_fake))
rgi= thd->rgi_fake= new rpl_group_info(rli);
@@ -243,7 +243,8 @@ void mysql_client_binlog_statement(THD* thd)
}
decoded_len= my_base64_needed_decoded_length((int)coded_len);
- if (!(buf= (char *) my_malloc(decoded_len, MYF(MY_WME))))
+ if (!(buf= (char *) my_malloc(key_memory_binlog_statement_buffer,
+ decoded_len, MYF(MY_WME))))
{
my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1);
goto end;
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index cce80ce2bc8..02dc8198c7c 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -28,9 +28,26 @@
#include <my_bit.h>
-template <uint width> class Bitmap
+/* An iterator to quickly walk over bits in ulonglong bitmap. */
+class Table_map_iterator
{
+ ulonglong bmp;
+public:
+ Table_map_iterator(ulonglong t): bmp(t){}
+ uint next_bit()
+ {
+ if (!bmp)
+ return BITMAP_END;
+ uint bit= my_find_first_bit(bmp);
+ bmp &= ~(1ULL << bit);
+ return bit;
+ }
+ int operator++(int) { return next_bit(); }
+ enum { BITMAP_END= 64 };
+};
+template <uint width> class Bitmap
+{
/*
Workaround GCC optimizer bug (generating SSE instuctions on unaligned data)
*/
@@ -43,12 +60,38 @@ template <uint width> class Bitmap
#pragma GCC target ("no-sse")
#endif
- uint32 buffer[(width + 31) / 32];
-public:
- Bitmap()
+private:
+ static const int BITS_PER_ELEMENT= sizeof(ulonglong) * 8;
+ static const int ARRAY_ELEMENTS= (width + BITS_PER_ELEMENT - 1) / BITS_PER_ELEMENT;
+ static const ulonglong ALL_BITS_SET= ULLONG_MAX;
+
+ ulonglong buffer[ARRAY_ELEMENTS];
+
+ uint bit_index(uint n) const
+ {
+ DBUG_ASSERT(n < width);
+ return ARRAY_ELEMENTS == 1 ? 0 : n / BITS_PER_ELEMENT;
+ }
+ ulonglong bit_mask(uint n) const
{
- clear_all();
+ DBUG_ASSERT(n < width);
+ return ARRAY_ELEMENTS == 1 ? 1ULL << n : 1ULL << (n % BITS_PER_ELEMENT);
+ }
+ ulonglong last_element_mask(int n) const
+ {
+ DBUG_ASSERT(n % BITS_PER_ELEMENT != 0);
+ return bit_mask(n) - 1;
}
+
+public:
+ /*
+ The default constructor does nothing.
+ The caller is supposed to either zero the memory
+ or to call set_all()/clear_all()/set_prefix()
+ to initialize bitmap.
+ */
+ Bitmap() { }
+
explicit Bitmap(uint prefix)
{
set_prefix(prefix);
@@ -57,50 +100,76 @@ public:
{
set_prefix(prefix);
}
-
uint length() const
{
return width;
}
void set_bit(uint n)
{
- DBUG_ASSERT(n < width);
- ((uchar*)buffer)[n / 8] |= (1 << (n & 7));
+ buffer[bit_index(n)] |= bit_mask(n);
}
void clear_bit(uint n)
{
- DBUG_ASSERT(n < width);
- ((uchar*)buffer)[n / 8] &= ~(1 << (n & 7));
+ buffer[bit_index(n)] &= ~bit_mask(n);
+ }
+ bool is_set(uint n) const
+ {
+ return buffer[bit_index(n)] & bit_mask(n);
}
void set_prefix(uint prefix_size)
{
set_if_smaller(prefix_size, width);
- uint prefix_bytes, prefix_bits, d;
- uchar* m = (uchar*)buffer;
- if ((prefix_bytes = prefix_size / 8))
- memset(m, 0xff, prefix_bytes);
- m += prefix_bytes;
- if ((prefix_bits = prefix_size & 7))
- {
- *(m++) = (1 << prefix_bits) - 1;
- // As the prefix bits are set, lets count this byte too as a prefix byte.
- prefix_bytes++;
- }
- if ((d = (width + 7) / 8 - prefix_bytes))
- memset(m, 0, d);
+ size_t idx= prefix_size / BITS_PER_ELEMENT;
+
+ for (size_t i= 0; i < idx; i++)
+ buffer[i]= ALL_BITS_SET;
+
+ if (prefix_size % BITS_PER_ELEMENT)
+ buffer[idx++]= last_element_mask(prefix_size);
+
+ for (size_t i= idx; i < ARRAY_ELEMENTS; i++)
+ buffer[i]= 0;
+ }
+ bool is_prefix(uint prefix_size) const
+ {
+ DBUG_ASSERT(prefix_size <= width);
+
+ size_t idx= prefix_size / BITS_PER_ELEMENT;
+
+ for (size_t i= 0; i < idx; i++)
+ if (buffer[i] != ALL_BITS_SET)
+ return false;
+
+ if (prefix_size % BITS_PER_ELEMENT)
+ if (buffer[idx++] != last_element_mask(prefix_size))
+ return false;
+
+ for (size_t i= idx; i < ARRAY_ELEMENTS; i++)
+ if (buffer[i] != 0)
+ return false;
+
+ return true;
}
void set_all()
{
- set_prefix(width);
+ if (width % BITS_PER_ELEMENT)
+ set_prefix(width);
+ else if (ARRAY_ELEMENTS > 1)
+ memset(buffer, 0xff, sizeof(buffer));
+ else
+ buffer[0] = ALL_BITS_SET;
}
void clear_all()
{
- memset(buffer, 0x00, sizeof(buffer));
+ if (ARRAY_ELEMENTS > 1)
+ memset(buffer, 0, sizeof(buffer));
+ else
+ buffer[0]= 0;
}
- void intersect(Bitmap & map2)
+ void intersect(const Bitmap& map2)
{
- for (uint i = 0; i < array_elements(buffer); i++)
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
buffer[i] &= map2.buffer[i];
}
@@ -112,27 +181,13 @@ private:
*/
void intersect_and_pad(ulonglong map2buff, bool pad_with_ones)
{
- compile_time_assert(sizeof(ulonglong) == 8);
- uint32 tmp[2];
- int8store(tmp, map2buff);
+ buffer[0] &= map2buff;
- buffer[0] &= tmp[0];
- if (array_elements(buffer) > 1)
- buffer[1] &= tmp[1];
-
- if (array_elements(buffer) <= 2)
- return;
- if (pad_with_ones)
- {
- memset((char*)buffer + 8, 0xff , sizeof(buffer) - 8);
- if (width != sizeof(buffer) * 8)
- {
- ((uchar*)buffer)[sizeof(buffer)-1] = last_byte_mask(width);
- }
- }
- else
- memset((char*)buffer + 8, 0 , sizeof(buffer) - 8);
+ for (size_t i= 1; i < ARRAY_ELEMENTS; i++)
+ buffer[i]= pad_with_ones ? ALL_BITS_SET : 0;
+ if (ARRAY_ELEMENTS > 1 && (width % BITS_PER_ELEMENT) && pad_with_ones)
+ buffer[ARRAY_ELEMENTS - 1]= last_element_mask(width);
}
public:
@@ -140,141 +195,110 @@ public:
{
intersect_and_pad(map2buff, 0);
}
- /* Use highest bit for all bits above sizeof(ulonglong)*8. */
+ /* Use highest bit for all bits above first element. */
void intersect_extended(ulonglong map2buff)
{
intersect_and_pad(map2buff, (map2buff & (1ULL << 63)));
}
- void subtract(Bitmap & map2)
+ void subtract(const Bitmap& map2)
{
- for (size_t i = 0; i < array_elements(buffer); i++)
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
buffer[i] &= ~(map2.buffer[i]);
}
- void merge(Bitmap & map2)
+ void merge(const Bitmap& map2)
{
- for (size_t i = 0; i < array_elements(buffer); i++)
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
buffer[i] |= map2.buffer[i];
}
- bool is_set(uint n) const
- {
- DBUG_ASSERT(n < width);
- return ((uchar*)buffer)[n / 8] & (1 << (n & 7));
- }
- bool is_prefix(uint prefix_size) const
- {
- uint prefix_mask = last_byte_mask(prefix_size);
- uchar* m = (uchar*)buffer;
- uchar* end_prefix = m + (prefix_size - 1) / 8;
- uchar* end;
- DBUG_ASSERT(prefix_size <= width);
-
- /* Empty prefix is always true */
- if (!prefix_size)
- return true;
-
- while (m < end_prefix)
- if (*m++ != 0xff)
- return false;
-
- end = ((uchar*)buffer) + (width + 7) / 8 - 1;
- if (m == end)
- return ((*m & last_byte_mask(width)) == prefix_mask);
-
- if (*m != prefix_mask)
- return false;
-
- while (++m < end)
- if (*m != 0)
- return false;
- return ((*m & last_byte_mask(width)) == 0);
- }
bool is_clear_all() const
{
- for (size_t i= 0; i < array_elements(buffer); i++)
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
if (buffer[i])
return false;
return true;
}
- bool is_set_all() const
- {
- if (width == sizeof(buffer) * 8)
- {
- for (size_t i = 0; i < array_elements(buffer); i++)
- if (buffer[i] != 0xFFFFFFFFU)
- return false;
- return true;
- }
- else
- return is_prefix(width);
- }
-
- bool is_subset(const Bitmap & map2) const
+ bool is_subset(const Bitmap& map2) const
{
- for (size_t i= 0; i < array_elements(buffer); i++)
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
if (buffer[i] & ~(map2.buffer[i]))
return false;
return true;
}
- bool is_overlapping(const Bitmap & map2) const
+ bool is_overlapping(const Bitmap& map2) const
{
- for (size_t i = 0; i < array_elements(buffer); i++)
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
if (buffer[i] & map2.buffer[i])
return true;
return false;
}
- bool operator==(const Bitmap & map2) const
+ bool operator==(const Bitmap& map2) const
{
- return memcmp(buffer, map2.buffer, sizeof(buffer)) == 0;
+ if (ARRAY_ELEMENTS > 1)
+ return !memcmp(buffer,map2.buffer,sizeof(buffer));
+ return buffer[0] == map2.buffer[0];
}
- bool operator!=(const Bitmap & map2) const
+ bool operator!=(const Bitmap& map2) const
{
return !(*this == map2);
}
+ /*
+ Print hexadecimal representation of bitmap.
+ Truncate trailing zeros.
+ */
char *print(char *buf) const
{
- char *s=buf;
- const uchar *e=(uchar *)buffer, *b=e+sizeof(buffer)-1;
- while (!*b && b>e)
- b--;
- if ((*s=_dig_vec_upper[*b >> 4]) != '0')
- s++;
- *s++=_dig_vec_upper[*b & 15];
- while (--b>=e)
+ size_t last; /*index of the last non-zero element, or 0. */
+
+ for (last= ARRAY_ELEMENTS - 1; last && !buffer[last]; last--){}
+
+ const int HEX_DIGITS_PER_ELEMENT= BITS_PER_ELEMENT / 4;
+ for (size_t i= 0; i < last; i++)
{
- *s++=_dig_vec_upper[*b >> 4];
- *s++=_dig_vec_upper[*b & 15];
+ ulonglong num = buffer[i];
+ uint shift = BITS_PER_ELEMENT - 4;
+ size_t pos= i * HEX_DIGITS_PER_ELEMENT;
+ for (size_t j= 0; j < HEX_DIGITS_PER_ELEMENT; j++)
+ {
+ buf[pos + j]= _dig_vec_upper[(num >> shift) & 0xf];
+ shift += 4;
+ }
}
- *s=0;
+ longlong2str(buffer[last], buf, 16);
return buf;
}
ulonglong to_ulonglong() const
{
- DBUG_ASSERT(sizeof(buffer) >= 4);
- uchar *b=(uchar *)buffer;
- if (sizeof(buffer) >= 8)
- return uint8korr(b);
- return (ulonglong) uint4korr(b);
+ return buffer[0];
}
uint bits_set()
{
- uint res = 0;
- for (size_t i = 0; i < array_elements(buffer); i++)
- res += my_count_bits_uint32(buffer[i]);
+ uint res= 0;
+ for (size_t i= 0; i < ARRAY_ELEMENTS; i++)
+ res += my_count_bits(buffer[i]);
return res;
}
class Iterator
{
- Bitmap &map;
- uint no;
+ const Bitmap& map;
+ uint offset;
+ Table_map_iterator tmi;
public:
- Iterator(Bitmap<width> &map2): map(map2), no(0) {}
- int operator++(int) {
- if (no == width) return BITMAP_END;
- while (!map.is_set(no))
+ Iterator(const Bitmap<width>& map2) : map(map2), offset(0), tmi(map2.buffer[0]) {}
+ int operator++(int)
+ {
+ for (;;)
{
- if ((++no) == width) return BITMAP_END;
+ int nextbit= tmi++;
+
+ if (nextbit != Table_map_iterator::BITMAP_END)
+ return offset + nextbit;
+
+ if (offset + BITS_PER_ELEMENT >= map.length())
+ return BITMAP_END;
+
+ offset += BITS_PER_ELEMENT;
+ tmi= Table_map_iterator(map.buffer[offset / BITS_PER_ELEMENT]);
}
- return no++;
}
enum { BITMAP_END = width };
};
@@ -283,98 +307,8 @@ public:
#pragma GCC pop_options
#undef NEED_GCC_NO_SSE_WORKAROUND
#endif
-
};
-
-/* An iterator to quickly walk over bits in ulonglong bitmap. */
-class Table_map_iterator
-{
- ulonglong bmp;
- uint no;
-public:
- Table_map_iterator(ulonglong t) : bmp(t), no(0) {}
- uint next_bit()
- {
- static const uchar last_bit[16]= {32, 0, 1, 0,
- 2, 0, 1, 0,
- 3, 0, 1, 0,
- 2, 0, 1, 0};
- uint bit;
- while ((bit= last_bit[bmp & 0xF]) == 32)
- {
- no += 4;
- bmp= bmp >> 4;
- if (!bmp)
- return BITMAP_END;
- }
- bmp &= ~(1ULL << bit);
- return no + bit;
- }
- uint operator++(int) { return next_bit(); }
- enum { BITMAP_END= 64 };
-};
-
-template <> class Bitmap<64>
-{
- ulonglong map;
-public:
- Bitmap<64>() { }
- explicit Bitmap<64>(uint prefix_to_set) { set_prefix(prefix_to_set); }
- void init(uint prefix_to_set) { set_prefix(prefix_to_set); }
- uint length() const { return 64; }
- void set_bit(uint n) { map|= ((ulonglong)1) << n; }
- void clear_bit(uint n) { map&= ~(((ulonglong)1) << n); }
- void set_prefix(uint n)
- {
- if (n >= length())
- set_all();
- else
- map= (((ulonglong)1) << n)-1;
- }
- void set_all() { map=~(ulonglong)0; }
- void clear_all() { map=(ulonglong)0; }
- void intersect(Bitmap<64>& map2) { map&= map2.map; }
- void intersect(ulonglong map2) { map&= map2; }
- 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 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; }
- bool is_subset(const Bitmap<64>& map2) const { return !(map & ~map2.map); }
- bool is_overlapping(const Bitmap<64>& map2) const { return (map & map2.map)!= 0; }
- bool operator==(const Bitmap<64>& map2) const { return map == map2.map; }
- char *print(char *buf) const {
- longlong2str(longlong(map), buf, 16);
- return buf;
- }
- ulonglong to_ulonglong() const { return map; }
- class Iterator : public Table_map_iterator
- {
- public:
- Iterator(Bitmap<64> &map2) : Table_map_iterator(map2.map) {}
- };
- uint bits_set()
- {
- //TODO: use my_count_bits()
- uint res= 0, i= 0;
- for (; i < 64 ; i++)
- {
- if (map & ((ulonglong)1<<i))
- res++;
- }
- return res;
- }
-};
-
-#if MAX_INDEXES <= 64
-typedef Bitmap<64> key_map; /* Used for finding keys */
-#elif MAX_INDEXES > 128
-#error "MAX_INDEXES values greater than 128 is not supported."
-#else
-typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
-#endif
+typedef Bitmap<MAX_INDEXES> key_map; /* Used for finding keys */
#endif /* SQL_BITMAP_INCLUDED */
diff --git a/sql/sql_bootstrap.cc b/sql/sql_bootstrap.cc
index a8c930820a7..dbeb971cd5a 100644
--- a/sql/sql_bootstrap.cc
+++ b/sql/sql_bootstrap.cc
@@ -29,6 +29,7 @@ int read_bootstrap_query(char *query, int *query_length,
int fgets_error= 0;
*error= 0;
+ *query_length= 0;
for ( ; ; )
{
line= (*fgets_fn)(line_buffer, sizeof(line_buffer), input, &fgets_error);
diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in
index 5ac044afd5d..810f98a876c 100644
--- a/sql/sql_builtin.cc.in
+++ b/sql/sql_builtin.cc.in
@@ -32,9 +32,6 @@ extern
builtin_maria_plugin
@mysql_mandatory_plugins@ @mysql_optional_plugins@
builtin_maria_binlog_plugin,
-#ifdef WITH_WSREP
- builtin_maria_wsrep_plugin,
-#endif /* WITH_WSREP */
builtin_maria_mysql_password_plugin;
struct st_maria_plugin *mysql_optional_plugins[]=
@@ -45,8 +42,5 @@ struct st_maria_plugin *mysql_optional_plugins[]=
struct st_maria_plugin *mysql_mandatory_plugins[]=
{
builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin,
-#ifdef WITH_WSREP
- builtin_maria_wsrep_plugin,
-#endif /* WITH_WSREP */
@mysql_mandatory_plugins@ 0
};
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 0ba4732b6cd..35e3a7c5608 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -2727,8 +2727,8 @@ size_t Query_cache::init_cache()
DUMP(this);
- (void) my_hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0,
- query_cache_query_get_key, 0, 0);
+ (void) my_hash_init(key_memory_Query_cache, &queries, &my_charset_bin,
+ def_query_hash_size, 0,0, query_cache_query_get_key,0,0);
#ifndef FN_NO_CASE_SENSE
/*
If lower_case_table_names!=0 then db and table names are already
@@ -2738,8 +2738,8 @@ size_t Query_cache::init_cache()
lower_case_table_names == 0 then we should distinguish my_table
and MY_TABLE cases and so again can use binary collation.
*/
- (void) my_hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0,
- query_cache_table_get_key, 0, 0);
+ (void) my_hash_init(key_memory_Query_cache, &tables, &my_charset_bin,
+ def_table_hash_size, 0,0, query_cache_table_get_key, 0,0);
#else
/*
On windows, OS/2, MacOS X with HFS+ or any other case insensitive
@@ -2749,11 +2749,9 @@ size_t Query_cache::init_cache()
file system) and so should use case insensitive collation for
comparison.
*/
- (void) my_hash_init(&tables,
- lower_case_table_names ? &my_charset_bin :
- files_charset_info,
- def_table_hash_size, 0, 0,query_cache_table_get_key,
- 0, 0);
+ (void) my_hash_init(PSI_INSTRUMENT_ME, &tables, lower_case_table_names ?
+ &my_charset_bin : files_charset_info,
+ def_table_hash_size, 0,0, query_cache_table_get_key, 0,0);
#endif
queries_in_cache = 0;
@@ -3505,7 +3503,8 @@ my_bool Query_cache::register_all_tables(THD *thd,
my_bool
Query_cache::insert_table(THD *thd, size_t key_len, const char *key,
- Query_cache_block_table *node, size_t db_length, uint8 suffix_length_arg,
+ Query_cache_block_table *node, size_t db_length,
+ uint8 suffix_length_arg,
uint8 cache_type,
qc_engine_callback callback,
ulonglong engine_data,
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 92635ecacc7..b92b3972512 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -608,5 +608,5 @@ struct Query_cache_query_flags
#define query_cache_is_cacheable_query(L) 0
#endif /*HAVE_QUERY_CACHE*/
-extern Query_cache query_cache;
+extern MYSQL_PLUGIN_IMPORT Query_cache query_cache;
#endif
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index ab9a0118bde..6011dbbc6dd 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -35,7 +35,7 @@
#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,
+#include "sql_acl.h" // NO_ACL,
// acl_getroot_no_password
#include "sql_base.h"
#include "sql_handler.h" // mysql_ha_cleanup
@@ -72,6 +72,7 @@
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
#include "opt_trace.h"
+#include <mysql/psi/mysql_transaction.h>
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
@@ -129,6 +130,39 @@ bool Key_part_spec::operator==(const Key_part_spec& other) const
&other.field_name);
}
+
+bool Key_part_spec::check_key_for_blob(const handler *file) const
+{
+ if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
+ {
+ my_error(ER_BLOB_USED_AS_KEY, MYF(0), field_name.str, file->table_type());
+ return true;
+ }
+ return false;
+}
+
+
+bool Key_part_spec::check_key_length_for_blob() const
+{
+ if (!length)
+ {
+ my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), field_name.str);
+ return true;
+ }
+ return false;
+}
+
+
+bool Key_part_spec::init_multiple_key_for_blob(const handler *file)
+{
+ if (check_key_for_blob(file))
+ return true;
+ if (!length)
+ length= file->max_key_length() + 1;
+ return false;
+}
+
+
/**
Construct an (almost) deep copy of this key. Only those
elements that are known to never change are not copied.
@@ -142,7 +176,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), invisible(false)
+ generated(rhs.generated), invisible(false),
+ without_overlaps(rhs.without_overlaps), period(rhs.period)
{
list_copy_and_replace_each_value(columns, mem_root);
}
@@ -156,6 +191,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),
+ constraint_name(rhs.constraint_name),
ref_db(rhs.ref_db),
ref_table(rhs.ref_table),
ref_columns(rhs.ref_columns,mem_root),
@@ -312,6 +348,12 @@ void thd_clear_errors(THD *thd)
}
+extern "C" unsigned long long thd_query_id(const MYSQL_THD thd)
+{
+ return((unsigned long long)thd->query_id);
+}
+
+
/**
Get thread attributes for connection threads
@@ -420,12 +462,6 @@ void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
}
extern "C"
-void **thd_ha_data(const THD *thd, const struct handlerton *hton)
-{
- return (void **) &thd->ha_data[hton->slot].ha_ptr;
-}
-
-extern "C"
void thd_storage_lock_wait(THD *thd, long long value)
{
thd->utime_after_lock+= value;
@@ -437,7 +473,7 @@ void thd_storage_lock_wait(THD *thd, long long value)
extern "C"
void *thd_get_ha_data(const THD *thd, const struct handlerton *hton)
{
- return *thd_ha_data(thd, hton);
+ return thd->ha_data[hton->slot].ha_ptr;
}
@@ -450,6 +486,7 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton,
const void *ha_data)
{
plugin_ref *lock= &thd->ha_data[hton->slot].lock;
+ thd->ha_data[hton->slot].ha_ptr= const_cast<void*>(ha_data);
if (ha_data && !*lock)
*lock= ha_lock_engine(NULL, (handlerton*) hton);
else if (!ha_data && *lock)
@@ -457,7 +494,6 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton,
plugin_unlock(NULL, *lock);
*lock= NULL;
}
- *thd_ha_data(thd, hton)= (void*) ha_data;
}
@@ -604,19 +640,20 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
:Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
/* statement id */ 0),
rli_fake(0), rgi_fake(0), rgi_slave(NULL),
- protocol_text(this), protocol_binary(this),
- m_current_stage_key(0),
+ protocol_text(this), protocol_binary(this), initial_status_var(0),
+ m_current_stage_key(0), m_psi(0),
in_sub_stmt(0), log_all_errors(0),
binlog_unsafe_warning_flags(0),
current_stmt_binlog_format(BINLOG_FORMAT_MIXED),
- binlog_table_maps(0),
bulk_param(0),
table_map_for_update(0),
m_examined_row_count(0),
accessed_rows_and_keys(0),
m_digest(NULL),
m_statement_psi(NULL),
+ m_transaction_psi(NULL),
m_idle_psi(NULL),
+ col_access(NO_ACL),
thread_id(id),
thread_dbug_id(id),
os_thread_id(0),
@@ -670,9 +707,10 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
wsrep_apply_format(0),
wsrep_rbr_buf(NULL),
wsrep_sync_wait_gtid(WSREP_GTID_UNDEFINED),
+ wsrep_last_written_gtid_seqno(0),
+ wsrep_current_gtid_seqno(0),
wsrep_affected_rows(0),
wsrep_has_ignored_error(false),
- wsrep_replicate_GTID(false),
wsrep_ignore_table(false),
wsrep_aborter(0),
@@ -715,8 +753,9 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
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, "THD::main_mem_root",
- ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_thd_main_mem_root,
+ &main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
/*
Allocation of user variables for binary logging is always done with main
@@ -728,9 +767,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
thread_stack= 0;
scheduler= thread_scheduler; // Will be fixed later
event_scheduler.data= 0;
- event_scheduler.m_psi= 0;
skip_wait_timeout= false;
- extra_port= 0;
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
@@ -740,7 +777,6 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
killed_err= 0;
- col_access=0;
is_slave_error= thread_specific_used= FALSE;
my_hash_clear(&handler_tables_hash);
my_hash_clear(&ull_hash);
@@ -770,6 +806,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
bzero((void*) ha_data, sizeof(ha_data));
mysys_var=0;
binlog_evt_union.do_union= FALSE;
+ binlog_table_maps= FALSE;
enable_slow_log= 0;
durability_property= HA_REGULAR_DURABILITY;
@@ -784,12 +821,14 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
system_thread= NON_SYSTEM_THREAD;
cleanup_done= free_connection_done= abort_on_warning= 0;
peer_port= 0; // For SHOW PROCESSLIST
- transaction.m_pending_rows_event= 0;
- transaction.on= 1;
- wt_thd_lazy_init(&transaction.wt, &variables.wt_deadlock_search_depth_short,
- &variables.wt_timeout_short,
- &variables.wt_deadlock_search_depth_long,
- &variables.wt_timeout_long);
+ transaction= &default_transaction;
+ transaction->m_pending_rows_event= 0;
+ transaction->on= 1;
+ wt_thd_lazy_init(&transaction->wt,
+ &variables.wt_deadlock_search_depth_short,
+ &variables.wt_timeout_short,
+ &variables.wt_deadlock_search_depth_long,
+ &variables.wt_timeout_long);
#ifdef SIGNAL_WITH_VIO_CLOSE
active_vio = 0;
#endif
@@ -813,16 +852,18 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
reset_open_tables_state(this);
init();
+ debug_sync_init_thread(this);
#if defined(ENABLED_PROFILING)
profiling.set_thd(this);
#endif
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_init(key_memory_user_var_entry, &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, HASH_THREAD_SPECIFIC);
- my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0,
- (my_hash_get_key) get_sequence_last_key,
- (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC);
+ my_hash_init(PSI_INSTRUMENT_ME, &sequences, system_charset_info,
+ SEQUENCES_HASH_SIZE, 0, 0, (my_hash_get_key)
+ get_sequence_last_key, (my_hash_free_key) free_sequence_last,
+ HASH_THREAD_SPECIFIC);
sp_proc_cache= NULL;
sp_func_cache= NULL;
@@ -831,7 +872,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
/* For user vars replication*/
if (opt_bin_log)
- my_init_dynamic_array(&user_var_events,
+ my_init_dynamic_array(key_memory_user_var_entry, &user_var_events,
sizeof(BINLOG_USER_VAR_EVENT *), 16, 16, MYF(0));
else
bzero((char*) &user_var_events, sizeof(user_var_events));
@@ -859,7 +900,8 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
m_token_array= NULL;
if (max_digest_length > 0)
{
- m_token_array= (unsigned char*) my_malloc(max_digest_length,
+ m_token_array= (unsigned char*) my_malloc(PSI_INSTRUMENT_ME,
+ max_digest_length,
MYF(MY_WME|MY_THREAD_SPECIFIC));
}
@@ -1159,19 +1201,9 @@ void *thd_memdup(MYSQL_THD thd, const void* str, size_t size)
extern "C"
void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid)
{
-#ifdef WITH_WSREP
- if (!thd->wsrep_xid.is_null())
- {
- *xid = *(MYSQL_XID *) &thd->wsrep_xid;
- return;
- }
-#endif /* WITH_WSREP */
- *xid= thd->transaction.xid_state.is_explicit_XA() ?
- *(MYSQL_XID *) thd->transaction.xid_state.get_xid() :
- *(MYSQL_XID *) &thd->transaction.implicit_xid;
+ *xid = *(MYSQL_XID *) thd->get_xid();
}
-
extern "C"
my_time_t thd_TIME_to_gmt_sec(MYSQL_THD thd, const MYSQL_TIME *ltime,
unsigned int *errcode)
@@ -1192,11 +1224,6 @@ void thd_gmt_sec_to_TIME(MYSQL_THD thd, MYSQL_TIME *ltime, my_time_t t)
#ifdef _WIN32
-extern "C" THD *_current_thd_noinline(void)
-{
- return my_pthread_getspecific_ptr(THD*,THR_THD);
-}
-
extern "C" my_thread_id next_thread_id_noinline()
{
#undef next_thread_id
@@ -1243,10 +1270,10 @@ void THD::init()
if (variables.sql_mode & MODE_ANSI_QUOTES)
server_status|= SERVER_STATUS_ANSI_QUOTES;
- transaction.all.modified_non_trans_table=
- transaction.stmt.modified_non_trans_table= FALSE;
- transaction.all.m_unsafe_rollback_flags=
- transaction.stmt.m_unsafe_rollback_flags= 0;
+ transaction->all.modified_non_trans_table=
+ transaction->stmt.modified_non_trans_table= FALSE;
+ transaction->all.m_unsafe_rollback_flags=
+ transaction->stmt.m_unsafe_rollback_flags= 0;
open_options=ha_open_options;
update_lock_default= (variables.low_priority_updates ?
@@ -1287,7 +1314,6 @@ void THD::init()
wsrep_rbr_buf = NULL;
wsrep_affected_rows = 0;
m_wsrep_next_trx_id = WSREP_UNDEFINED_TRX_ID;
- wsrep_replicate_GTID = false;
wsrep_aborter = 0;
#endif /* WITH_WSREP */
@@ -1296,22 +1322,16 @@ void THD::init()
else
variables.option_bits&= ~OPTION_BIN_LOG;
- variables.sql_log_bin_off= 0;
-
select_commands= update_commands= other_commands= 0;
/* Set to handle counting of aborted connections */
userstat_running= opt_userstat_running;
last_global_update_time= current_connect_time= time(NULL);
-#if defined(ENABLED_DEBUG_SYNC)
- /* Initialize the Debug Sync Facility. See debug_sync.cc. */
- debug_sync_init_thread(this);
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
#ifndef EMBEDDED_LIBRARY
session_tracker.enable(this);
#endif //EMBEDDED_LIBRARY
apc_target.init(&LOCK_thd_kill);
+ gap_tracker_data.init();
DBUG_VOID_RETURN;
}
@@ -1381,21 +1401,18 @@ void THD::update_all_stats()
void THD::init_for_queries()
{
- set_time();
- /*
- We don't need to call ha_enable_transaction() as we can't have
- any active transactions that has to be committed
- */
- DBUG_ASSERT(transaction.is_empty());
- transaction.on= TRUE;
+ DBUG_ASSERT(transaction->on);
+ DBUG_ASSERT(m_transaction_psi == NULL);
+ /* Set time for --init-file queries */
+ set_time();
reset_root_defaults(mem_root, variables.query_alloc_block_size,
variables.query_prealloc_size);
- reset_root_defaults(&transaction.mem_root,
+ reset_root_defaults(&transaction->mem_root,
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
- DBUG_ASSERT(!transaction.xid_state.is_explicit_XA());
- DBUG_ASSERT(transaction.implicit_xid.is_null());
+ DBUG_ASSERT(!transaction->xid_state.is_explicit_XA());
+ DBUG_ASSERT(transaction->implicit_xid.is_null());
}
@@ -1427,12 +1444,13 @@ void THD::change_user(void)
init();
stmt_map.reset();
- 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_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0,
- (my_hash_get_key) get_sequence_last_key,
- (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC);
+ my_hash_init(key_memory_user_var_entry, &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, HASH_THREAD_SPECIFIC);
+ my_hash_init(key_memory_user_var_entry, &sequences, system_charset_info,
+ SEQUENCES_HASH_SIZE, 0, 0, (my_hash_get_key)
+ get_sequence_last_key, (my_hash_free_key) free_sequence_last,
+ HASH_THREAD_SPECIFIC);
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
sp_cache_clear(&sp_package_spec_cache);
@@ -1469,7 +1487,8 @@ bool THD::set_db(const LEX_CSTRING *new_db)
const char *tmp= NULL;
if (new_db->str)
{
- if (!(tmp= my_strndup(new_db->str, new_db->length, MYF(MY_WME | ME_FATAL))))
+ if (!(tmp= my_strndup(key_memory_THD_db, new_db->str, new_db->length,
+ MYF(MY_WME | ME_FATAL))))
result= 1;
}
@@ -1532,12 +1551,14 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- if (transaction.xid_state.is_explicit_XA())
+ if (transaction->xid_state.is_explicit_XA())
trans_xa_detach(this);
else
trans_rollback(this);
DBUG_ASSERT(open_tables == NULL);
+ DBUG_ASSERT(m_transaction_psi == NULL);
+
/*
If the thread was in the middle of an ongoing transaction (rolled
back a few lines above) or under LOCK TABLES (unlocked the tables
@@ -1558,12 +1579,7 @@ void THD::cleanup(void)
decrease_user_connections(user_connect);
user_connect= 0; // Safety
}
- wt_thd_destroy(&transaction.wt);
-
-#if defined(ENABLED_DEBUG_SYNC)
- /* End the Debug Sync Facility. See debug_sync.cc. */
- debug_sync_end_thread(this);
-#endif /* defined(ENABLED_DEBUG_SYNC) */
+ wt_thd_destroy(&transaction->wt);
my_hash_free(&user_vars);
my_hash_free(&sequences);
@@ -1618,6 +1634,7 @@ void THD::free_connection()
#if defined(ENABLED_PROFILING)
profiling.restart(); // Reset profiling
#endif
+ debug_sync_reset_thread(this);
}
/*
@@ -1642,7 +1659,7 @@ void THD::reset_for_reuse()
abort_on_warning= 0;
free_connection_done= 0;
m_command= COM_CONNECT;
- transaction.on= 1;
+ transaction->on= 1;
#if defined(ENABLED_PROFILING)
profiling.reset();
#endif
@@ -1662,6 +1679,8 @@ THD::~THD()
DBUG_ENTER("~THD()");
/* Make sure threads are not available via server_threads. */
assert_not_linked();
+ if (m_psi)
+ PSI_CALL_set_thread_THD(m_psi, 0);
/*
In error cases, thd may not be current thd. We have to fix this so
@@ -1693,7 +1712,7 @@ THD::~THD()
#endif
mdl_context.destroy();
- free_root(&transaction.mem_root,MYF(0));
+ transaction->free();
mysql_cond_destroy(&COND_wakeup_ready);
mysql_mutex_destroy(&LOCK_wakeup_ready);
mysql_mutex_destroy(&LOCK_thd_data);
@@ -1725,12 +1744,16 @@ THD::~THD()
lf_hash_put_pins(tdc_hash_pins);
if (xid_hash_pins)
lf_hash_put_pins(xid_hash_pins);
+ debug_sync_end_thread(this);
/* Ensure everything is freed */
status_var.local_memory_used-= sizeof(THD);
/* trick to make happy memory accounting system */
#ifndef EMBEDDED_LIBRARY
session_tracker.sysvars.deinit();
+#ifdef USER_VAR_TRACKING
+ session_tracker.user_variables.deinit();
+#endif // USER_VAR_TRACKING
#endif //EMBEDDED_LIBRARY
if (status_var.local_memory_used != 0)
@@ -2149,7 +2172,7 @@ void THD::reset_killed()
the structure for the net buffer
*/
-bool THD::store_globals()
+void THD::store_globals()
{
/*
Assert that thread_stack is initialized: it's necessary to be able
@@ -2157,8 +2180,7 @@ bool THD::store_globals()
*/
DBUG_ASSERT(thread_stack);
- if (set_current_thd(this))
- return 1;
+ set_current_thd(this);
/*
mysys_var is concurrently readable by a killer thread.
It is protected by LOCK_thd_kill, it is not needed to lock while the
@@ -2200,8 +2222,6 @@ bool THD::store_globals()
created in another thread
*/
thr_lock_info_init(&lock_info, mysys_var);
-
- return 0;
}
/**
@@ -2493,7 +2513,8 @@ bool THD::to_ident_sys_alloc(Lex_ident_sys_st *to, const Lex_ident_cli_st *ident
Item_basic_constant *
-THD::make_string_literal(const char *str, size_t length, uint repertoire)
+THD::make_string_literal(const char *str, size_t length,
+ my_repertoire_t repertoire)
{
if (!length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
return new (mem_root) Item_null(this, 0, variables.collation_connection);
@@ -2534,8 +2555,7 @@ THD::make_string_literal_charset(const Lex_string_with_metadata_st &str,
{
if (!str.length && (variables.sql_mode & MODE_EMPTY_STRING_IS_NULL))
return new (mem_root) Item_null(this, 0, cs);
- return new (mem_root) Item_string_with_introducer(this,
- str.str, (uint)str.length, cs);
+ return new (mem_root) Item_string_with_introducer(this, str, cs);
}
@@ -2593,7 +2613,8 @@ void THD::add_changed_table(TABLE *table)
{
DBUG_ENTER("THD::add_changed_table(table)");
- DBUG_ASSERT(in_multi_stmt_transaction_mode() && table->file->has_transactions());
+ DBUG_ASSERT(in_multi_stmt_transaction_mode() &&
+ table->file->has_transactions());
add_changed_table(table->s->table_cache_key.str,
(long) table->s->table_cache_key.length);
DBUG_VOID_RETURN;
@@ -2603,8 +2624,8 @@ void THD::add_changed_table(TABLE *table)
void THD::add_changed_table(const char *key, size_t key_length)
{
DBUG_ENTER("THD::add_changed_table(key)");
- CHANGED_TABLE_LIST **prev_changed = &transaction.changed_tables;
- CHANGED_TABLE_LIST *curr = transaction.changed_tables;
+ CHANGED_TABLE_LIST **prev_changed = &transaction->changed_tables;
+ CHANGED_TABLE_LIST *curr = transaction->changed_tables;
for (; curr; prev_changed = &(curr->next), curr = curr->next)
{
@@ -3000,15 +3021,6 @@ int select_send::send_data(List<Item> &items)
Protocol *protocol= thd->protocol;
DBUG_ENTER("select_send::send_data");
- /* 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);
- }
- if (thd->killed == ABORT_QUERY)
- DBUG_RETURN(FALSE);
-
protocol->prepare_for_resend();
if (protocol->send_result_set_row(&items))
{
@@ -3271,13 +3283,6 @@ int select_export::send_data(List<Item> &items)
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
tmp.length(0);
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (thd->killed == ABORT_QUERY)
- DBUG_RETURN(0);
row_count++;
Item *item;
uint used_length=0,items_left=items.elements;
@@ -3395,7 +3400,7 @@ int select_export::send_data(List<Item> &items)
pos++)
{
#ifdef USE_MB
- if (use_mb(res_charset))
+ if (res_charset->use_mb())
{
int l;
if ((l=my_ismbchar(res_charset, pos, end)))
@@ -3531,14 +3536,6 @@ int select_dump::send_data(List<Item> &items)
Item *item;
DBUG_ENTER("select_dump::send_data");
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (thd->killed == ABORT_QUERY)
- DBUG_RETURN(0);
-
if (row_count++ > 1)
{
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
@@ -3574,13 +3571,6 @@ int select_singlerow_subselect::send_data(List<Item> &items)
MYF(current_thd->lex->ignore ? ME_WARNING : 0));
DBUG_RETURN(1);
}
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (thd->killed == ABORT_QUERY)
- DBUG_RETURN(0);
List_iterator_fast<Item> li(items);
Item *val_item;
for (uint i= 0; (val_item= li++); i++)
@@ -3715,13 +3705,6 @@ int select_exists_subselect::send_data(List<Item> &items)
{
DBUG_ENTER("select_exists_subselect::send_data");
Item_exists_subselect *it= (Item_exists_subselect *)item;
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (thd->killed == ABORT_QUERY)
- DBUG_RETURN(0);
it->value= 1;
it->assigned(1);
DBUG_RETURN(0);
@@ -3950,12 +3933,12 @@ Statement_map::Statement_map() :
START_STMT_HASH_SIZE = 16,
START_NAME_HASH_SIZE = 16
};
- my_hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0,
- get_statement_id_as_hash_key,
+ my_hash_init(key_memory_prepared_statement_map, &st_hash, &my_charset_bin,
+ START_STMT_HASH_SIZE, 0, 0, get_statement_id_as_hash_key,
delete_statement_as_hash_key, MYF(0));
- my_hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0,
+ my_hash_init(key_memory_prepared_statement_map, &names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0,
(my_hash_get_key) get_stmt_name_hash_key,
- NULL,MYF(0));
+ NULL, MYF(0));
}
@@ -4123,12 +4106,7 @@ int select_dumpvar::send_data(List<Item> &items)
{
DBUG_ENTER("select_dumpvar::send_data");
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (row_count++)
+ if (row_count++)
{
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
@@ -4318,10 +4296,10 @@ void Security_context::init()
host= user= ip= external_user= 0;
host_or_ip= "connecting host";
priv_user[0]= priv_host[0]= proxy_user[0]= priv_role[0]= '\0';
- master_access= 0;
+ master_access= NO_ACL;
password_expired= false;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- db_access= NO_ACCESS;
+ db_access= NO_ACL;
#endif
}
@@ -4356,7 +4334,7 @@ void Security_context::skip_grants()
{
/* privileges for the user are unknown everything is allowed */
host_or_ip= (char *)"";
- master_access= ~NO_ACCESS;
+ master_access= ALL_KNOWN_ACL;
*priv_user= *priv_host= '\0';
password_expired= false;
}
@@ -4364,15 +4342,16 @@ void Security_context::skip_grants()
bool Security_context::set_user(char *user_arg)
{
- my_free((char*) user);
- user= my_strdup(user_arg, MYF(0));
+ my_free(const_cast<char*>(user));
+ user= my_strdup(key_memory_MPVIO_EXT_auth_info, user_arg, MYF(0));
return user == 0;
}
-bool Security_context::check_access(ulong want_access, bool match_any)
+bool Security_context::check_access(const privilege_t want_access,
+ bool match_any)
{
DBUG_ENTER("Security_context::check_access");
- DBUG_RETURN((match_any ? (master_access & want_access)
+ DBUG_RETURN((match_any ? (master_access & want_access) != NO_ACL
: ((master_access & want_access) == want_access)));
}
@@ -4759,7 +4738,8 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
DBUG_ASSERT(thd->open_tables == NULL);
DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED);
- Open_table_context ot_ctx(thd, 0);
+ /* Purge already hold the MDL for the table */
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_HAS_MDL_LOCK);
TABLE_LIST *tl= (TABLE_LIST*)thd->alloc(sizeof(TABLE_LIST));
LEX_CSTRING db_name= {db, dblen };
LEX_CSTRING table_name= { tb, tblen };
@@ -4778,6 +4758,12 @@ TABLE *open_purge_table(THD *thd, const char *db, size_t dblen,
DBUG_RETURN(error ? NULL : tl->table);
}
+TABLE *get_purge_table(THD *thd)
+{
+ /* see above, at most one table can be opened */
+ DBUG_ASSERT(thd->open_tables == NULL || thd->open_tables->next == NULL);
+ return thd->open_tables;
+}
/** Find an open table in the list of prelocked tabled
@@ -4821,6 +4807,115 @@ void destroy_thd(MYSQL_THD thd)
delete thd;
}
+/**
+ Create a THD that only has auxilliary functions
+ It will never be added to the global connection list
+ server_threads. It does not represent any client connection.
+
+ It should never be counted, because it will stall the
+ shutdown. It is solely for engine's internal use,
+ like for example, evaluation of virtual function in innodb
+ purge.
+*/
+extern "C" pthread_key(struct st_my_thread_var *, THR_KEY_mysys);
+MYSQL_THD create_background_thd()
+{
+ DBUG_ASSERT(!current_thd);
+ auto save_mysysvar= pthread_getspecific(THR_KEY_mysys);
+
+ /*
+ Allocate new mysys_var specifically this THD,
+ so that e.g safemalloc, DBUG etc are happy.
+ */
+ pthread_setspecific(THR_KEY_mysys, 0);
+ my_thread_init();
+ auto thd_mysysvar= pthread_getspecific(THR_KEY_mysys);
+ auto thd= new THD(0);
+ pthread_setspecific(THR_KEY_mysys, save_mysysvar);
+ thd->set_psi(PSI_CALL_get_thread());
+
+ /*
+ Workaround the adverse effect of incrementing thread_count
+ in THD constructor. We do not want these THDs to be counted,
+ or waited for on shutdown.
+ */
+ thread_count--;
+
+ thd->mysys_var= (st_my_thread_var *) thd_mysysvar;
+ thd->set_command(COM_DAEMON);
+ thd->system_thread= SYSTEM_THREAD_GENERIC;
+ thd->security_ctx->host_or_ip= "";
+ return thd;
+}
+
+
+/*
+ Attach a background THD.
+
+ Changes current value THR_KEY_mysys TLS variable,
+ and returns the original value.
+*/
+void *thd_attach_thd(MYSQL_THD thd)
+{
+ DBUG_ASSERT(!current_thd);
+ DBUG_ASSERT(thd && thd->mysys_var);
+
+ auto save_mysysvar= pthread_getspecific(THR_KEY_mysys);
+ pthread_setspecific(THR_KEY_mysys, thd->mysys_var);
+ thd->thread_stack= (char *) &thd;
+ thd->store_globals();
+ return save_mysysvar;
+}
+
+/*
+ Restore THR_KEY_mysys TLS variable,
+ which was changed thd_attach_thd().
+*/
+void thd_detach_thd(void *mysysvar)
+{
+ /* Restore mysys_var that is changed when THD was attached.*/
+ pthread_setspecific(THR_KEY_mysys, mysysvar);
+ /* Restore the THD (we assume it was NULL during attach).*/
+ set_current_thd(0);
+}
+
+/*
+ Destroy a THD that was previously created by
+ create_background_thd()
+*/
+void destroy_background_thd(MYSQL_THD thd)
+{
+ DBUG_ASSERT(!current_thd);
+ auto thd_mysys_var= thd->mysys_var;
+ auto save_mysys_var= thd_attach_thd(thd);
+ DBUG_ASSERT(thd_mysys_var != save_mysys_var);
+ /*
+ Workaround the adverse effect decrementing thread_count on THD()
+ destructor.
+ As we decremented it in create_background_thd(), in order for it
+ not to go negative, we have to increment it before destructor.
+ */
+ thread_count++;
+ delete thd;
+
+ thd_detach_thd(save_mysys_var);
+ /*
+ Delete THD-specific my_thread_var, that was
+ allocated in create_background_thd().
+ Also preserve current PSI context, since my_thread_end()
+ would kill it, if we're not careful.
+ */
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ auto save_psi_thread= PSI_CALL_get_thread();
+#endif
+ PSI_CALL_set_thread(0);
+ pthread_setspecific(THR_KEY_mysys, thd_mysys_var);
+ my_thread_end();
+ pthread_setspecific(THR_KEY_mysys, save_mysys_var);
+ PSI_CALL_set_thread(save_psi_thread);
+}
+
+
void reset_thd(MYSQL_THD thd)
{
close_thread_tables(thd);
@@ -4895,6 +4990,55 @@ extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen)
}
+extern "C" const char *thd_user_name(MYSQL_THD thd)
+{
+ if (!thd->security_ctx)
+ return 0;
+
+ return thd->security_ctx->user;
+}
+
+
+extern "C" const char *thd_client_host(MYSQL_THD thd)
+{
+ if (!thd->security_ctx)
+ return 0;
+
+ return thd->security_ctx->host;
+}
+
+
+extern "C" const char *thd_client_ip(MYSQL_THD thd)
+{
+ if (!thd->security_ctx)
+ return 0;
+
+ return thd->security_ctx->ip;
+}
+
+
+extern "C" LEX_CSTRING *thd_current_db(MYSQL_THD thd)
+{
+ return &thd->db;
+}
+
+
+extern "C" int thd_current_status(MYSQL_THD thd)
+{
+ Diagnostics_area *da= thd->get_stmt_da();
+ if (!da)
+ return 0;
+
+ return da->is_error() ? da->sql_errno() : 0;
+}
+
+
+extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd)
+{
+ return thd->get_command();
+}
+
+
extern "C" int thd_slave_thread(const MYSQL_THD thd)
{
return(thd->slave_thread);
@@ -4982,7 +5126,7 @@ thd_rpl_deadlock_check(MYSQL_THD thd, MYSQL_THD other_thd)
if (!thd)
return 0;
DEBUG_SYNC(thd, "thd_report_wait_for");
- thd->transaction.stmt.mark_trans_did_wait();
+ thd->transaction->stmt.mark_trans_did_wait();
if (!other_thd)
return 0;
binlog_report_wait_for(thd, other_thd);
@@ -5085,7 +5229,7 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
{
- return(thd->transaction.all.modified_non_trans_table);
+ return(thd->transaction->all.modified_non_trans_table);
}
extern "C" int thd_binlog_format(const MYSQL_THD thd)
@@ -5231,6 +5375,18 @@ extern "C" void thd_wait_end(MYSQL_THD thd)
#endif // INNODB_COMPATIBILITY_HOOKS */
+
+/**
+ MDL_context accessor
+ @param thd the current session
+ @return pointer to thd->mdl_context
+*/
+extern "C" void *thd_mdl_context(MYSQL_THD thd)
+{
+ return &thd->mdl_context;
+}
+
+
/****************************************************************************
Handling of statement states in functions and triggers.
@@ -5282,7 +5438,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
backup->limit_found_rows= limit_found_rows;
backup->cuted_fields= cuted_fields;
backup->client_capabilities= client_capabilities;
- backup->savepoints= transaction.savepoints;
+ backup->savepoints= transaction->savepoints;
backup->first_successful_insert_id_in_prev_stmt=
first_successful_insert_id_in_prev_stmt;
backup->first_successful_insert_id_in_cur_stmt=
@@ -5304,7 +5460,7 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
client_capabilities &= ~CLIENT_MULTI_RESULTS;
in_sub_stmt|= new_state;
cuted_fields= 0;
- transaction.savepoints= 0;
+ transaction->savepoints= 0;
first_successful_insert_id_in_cur_stmt= 0;
reset_slow_query_state();
}
@@ -5330,16 +5486,16 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
level. It is enough to release first savepoint set on this level since
all later savepoints will be released automatically.
*/
- if (transaction.savepoints)
+ if (transaction->savepoints)
{
SAVEPOINT *sv;
- for (sv= transaction.savepoints; sv->prev; sv= sv->prev)
+ for (sv= transaction->savepoints; sv->prev; sv= sv->prev)
{}
/* ha_release_savepoint() never returns error. */
(void)ha_release_savepoint(this, sv);
}
count_cuted_fields= backup->count_cuted_fields;
- transaction.savepoints= backup->savepoints;
+ transaction->savepoints= backup->savepoints;
variables.option_bits= backup->option_bits;
in_sub_stmt= backup->in_sub_stmt;
enable_slow_log= backup->enable_slow_log;
@@ -5661,6 +5817,96 @@ void THD::mark_transaction_to_rollback(bool all)
/**
+ Commit the whole transaction (both statment and all)
+
+ This is used mainly to commit an independent transaction,
+ like reading system tables.
+
+ @return 0 0k
+ @return <>0 error code. my_error() has been called()
+*/
+
+int THD::commit_whole_transaction_and_close_tables()
+{
+ int error, error2;
+ DBUG_ENTER("THD::commit_whole_transaction_and_close_tables");
+
+ /*
+ This can only happened if we failed to open any table in the
+ new transaction
+ */
+ DBUG_ASSERT(open_tables);
+
+ if (!open_tables) // Safety for production usage
+ DBUG_RETURN(0);
+
+ /*
+ Ensure table was locked (opened with open_and_lock_tables()). If not
+ the THD can't be part of any transactions and doesn't have to call
+ this function.
+ */
+ DBUG_ASSERT(lock);
+
+ error= ha_commit_trans(this, FALSE);
+ /* This will call external_lock to unlock all tables */
+ if ((error2= mysql_unlock_tables(this, lock)))
+ {
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), error2);
+ error= error2;
+ }
+ lock= 0;
+ if ((error2= ha_commit_trans(this, TRUE)))
+ error= error2;
+ close_thread_tables(this);
+ DBUG_RETURN(error);
+}
+
+/**
+ Start a new independent transaction
+*/
+
+start_new_trans::start_new_trans(THD *thd)
+{
+ org_thd= thd;
+ mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ memcpy(old_ha_data, thd->ha_data, sizeof(old_ha_data));
+ thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+ for (auto &data : thd->ha_data)
+ data.reset();
+ old_transaction= thd->transaction;
+ thd->transaction= &new_transaction;
+ new_transaction.on= 1;
+ in_sub_stmt= thd->in_sub_stmt;
+ thd->in_sub_stmt= 0;
+ server_status= thd->server_status;
+ m_transaction_psi= thd->m_transaction_psi;
+ thd->m_transaction_psi= 0;
+ wsrep_on= thd->variables.wsrep_on;
+ thd->variables.wsrep_on= 0;
+ thd->server_status&= ~(SERVER_STATUS_IN_TRANS |
+ SERVER_STATUS_IN_TRANS_READONLY);
+ thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
+}
+
+
+void start_new_trans::restore_old_transaction()
+{
+ org_thd->transaction= old_transaction;
+ org_thd->restore_backup_open_tables_state(&open_tables_state_backup);
+ ha_close_connection(org_thd);
+ memcpy(org_thd->ha_data, old_ha_data, sizeof(old_ha_data));
+ org_thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
+ org_thd->in_sub_stmt= in_sub_stmt;
+ org_thd->server_status= server_status;
+ if (org_thd->m_transaction_psi)
+ MYSQL_COMMIT_TRANSACTION(org_thd->m_transaction_psi);
+ org_thd->m_transaction_psi= m_transaction_psi;
+ org_thd->variables.wsrep_on= wsrep_on;
+ org_thd= 0;
+}
+
+
+/**
Decide on logging format to use for the statement and issue errors
or warnings as needed. The decision depends on the following
parameters:
@@ -5759,8 +6005,9 @@ int THD::decide_logging_format(TABLE_LIST *tables)
{
DBUG_ENTER("THD::decide_logging_format");
DBUG_PRINT("info", ("Query: %.*s", (uint) query_length(), query()));
- DBUG_PRINT("info", ("variables.binlog_format: %lu",
- variables.binlog_format));
+ DBUG_PRINT("info", ("binlog_format: %lu", (ulong) variables.binlog_format));
+ DBUG_PRINT("info", ("current_stmt_binlog_format: %lu",
+ (ulong) current_stmt_binlog_format));
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
lex->get_stmt_unsafe_flags()));
@@ -5785,18 +6032,11 @@ int THD::decide_logging_format(TABLE_LIST *tables)
DBUG_RETURN(-1);
}
}
-
- if ((WSREP_EMULATE_BINLOG_NNULL(this) ||
- (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG))) &&
- !(wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
- !binlog_filter->db_ok(db.str)))
-#else
- if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
- !(wsrep_binlog_format() == BINLOG_FORMAT_STMT &&
- !binlog_filter->db_ok(db.str)))
#endif /* WITH_WSREP */
- {
+ if (WSREP_EMULATE_BINLOG_NNULL(this) ||
+ binlog_table_should_be_logged(&db))
+ {
if (is_bulk_op())
{
if (wsrep_binlog_format() == BINLOG_FORMAT_STMT)
@@ -5839,6 +6079,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
bool has_auto_increment_write_tables_not_first= FALSE;
bool found_first_not_own_table= FALSE;
bool has_write_tables_with_unsafe_statements= FALSE;
+ bool blackhole_table_found= 0;
/*
A pointer to a previous table that was changed.
@@ -5964,6 +6205,10 @@ int THD::decide_logging_format(TABLE_LIST *tables)
if (prev_write_table && prev_write_table->file->ht !=
table->file->ht)
multi_write_engine= TRUE;
+
+ if (table->file->ht->db_type == DB_TYPE_BLACKHOLE_DB)
+ blackhole_table_found= 1;
+
if (share->non_determinstic_insert &&
!(sql_command_flags[lex->sql_command] & CF_SCHEMA_CHANGE))
has_write_tables_with_unsafe_statements= true;
@@ -6209,7 +6454,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
is_current_stmt_binlog_format_row() ?
"ROW" : "STATEMENT"));
- if (variables.binlog_format == BINLOG_FORMAT_ROW &&
+ if (blackhole_table_found &&
+ variables.binlog_format == BINLOG_FORMAT_ROW &&
(sql_command_flags[lex->sql_command] &
(CF_UPDATES_DATA | CF_DELETES_DATA)))
{
@@ -6225,8 +6471,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
if (table->table->file->ht->db_type == DB_TYPE_BLACKHOLE_DB &&
table->lock_type >= TL_WRITE_ALLOW_WRITE)
{
- table_names.append(&table->table_name);
- table_names.append(",");
+ table_names.append(&table->table_name);
+ table_names.append(",");
}
}
if (!table_names.is_empty())
@@ -6246,9 +6492,12 @@ int THD::decide_logging_format(TABLE_LIST *tables)
table_names.c_ptr());
}
}
+
+ if (is_write && is_current_stmt_binlog_format_row())
+ binlog_prepare_for_row_logging();
}
-#ifndef DBUG_OFF
else
+ {
DBUG_PRINT("info", ("decision: no logging since "
"mysql_bin_log.is_open() = %d "
"and (options & OPTION_BIN_LOG) = 0x%llx "
@@ -6258,22 +6507,23 @@ int THD::decide_logging_format(TABLE_LIST *tables)
(variables.option_bits & OPTION_BIN_LOG),
(uint) wsrep_binlog_format(),
binlog_filter->db_ok(db.str)));
-#endif
-
+ if (WSREP_NNULL(this) && is_current_stmt_binlog_format_row())
+ binlog_prepare_for_row_logging();
+ }
DBUG_RETURN(0);
}
int THD::decide_logging_format_low(TABLE *table)
{
+ DBUG_ENTER("decide_logging_format_low");
/*
- INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
- can be unsafe.
- */
- if(wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
- !is_current_stmt_binlog_format_row() &&
- !lex->is_stmt_unsafe() &&
- lex->sql_command == SQLCOM_INSERT &&
- lex->duplicates == DUP_UPDATE)
+ INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
+ can be unsafe.
+ */
+ if (wsrep_binlog_format() <= BINLOG_FORMAT_STMT &&
+ !is_current_stmt_binlog_format_row() &&
+ !lex->is_stmt_unsafe() &&
+ lex->duplicates == DUP_UPDATE)
{
uint unique_keys= 0;
uint keys= table->s->keys, i= 0;
@@ -6300,19 +6550,29 @@ exit:;
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
binlog_unsafe_warning_flags|= lex->get_stmt_unsafe_flags();
set_current_stmt_binlog_format_row_if_mixed();
- return 1;
+ if (is_current_stmt_binlog_format_row())
+ binlog_prepare_for_row_logging();
+ DBUG_RETURN(1);
}
}
- return 0;
+ DBUG_RETURN(0);
}
-/*
- Implementation of interface to write rows to the binary log through the
- thread. The thread is responsible for writing the rows it has
- inserted/updated/deleted.
+#ifndef MYSQL_CLIENT
+/**
+ Check if we should log a table DDL to the binlog
+
+ @retval true yes
+ @retval false no
*/
-#ifndef MYSQL_CLIENT
+bool THD::binlog_table_should_be_logged(const LEX_CSTRING *db)
+{
+ return (mysql_bin_log.is_open() &&
+ (variables.option_bits & OPTION_BIN_LOG) &&
+ (wsrep_binlog_format() != BINLOG_FORMAT_STMT ||
+ binlog_filter->db_ok(db->str)));
+}
/*
Template member function for ensuring that there is an rows log
@@ -6320,7 +6580,7 @@ exit:;
PRE CONDITION:
- Events of type 'RowEventT' have the type code 'type_code'.
-
+
POST CONDITION:
If a non-NULL pointer is returned, the pending event for thread 'thd' will
be an event of type 'RowEventT' (which have the type code 'type_code')
@@ -6513,7 +6773,8 @@ CPP_UNNAMED_NS_START
}
else
{
- m_memory= (uchar *) my_malloc(total_length, MYF(MY_WME));
+ m_memory= (uchar *) my_malloc(key_memory_Row_data_memory_memory,
+ total_length, MYF(MY_WME));
m_release_memory_on_destruction= TRUE;
}
}
@@ -6732,12 +6993,13 @@ void THD::binlog_prepare_row_images(TABLE *table)
/**
if there is a primary key in the table (ie, user declared PK or a
- non-null unique index) and we dont want to ship the entire image,
+ non-null unique index) and we don't want to ship the entire image,
and the handler involved supports this.
*/
if (table->s->primary_key < MAX_KEY &&
(thd->variables.binlog_row_image < BINLOG_ROW_IMAGE_FULL) &&
- !ha_check_storage_engine_flag(table->s->db_type(), HTON_NO_BINLOG_ROW_OPT))
+ !ha_check_storage_engine_flag(table->s->db_type(),
+ HTON_NO_BINLOG_ROW_OPT))
{
/**
Just to be sure that tmp_set is currently not in use as
@@ -6782,7 +7044,7 @@ void THD::binlog_prepare_row_images(TABLE *table)
-int THD::binlog_remove_pending_rows_event(bool clear_maps,
+int THD::binlog_remove_pending_rows_event(bool reset_stmt,
bool is_transactional)
{
DBUG_ENTER("THD::binlog_remove_pending_rows_event");
@@ -6796,12 +7058,12 @@ int THD::binlog_remove_pending_rows_event(bool clear_maps,
mysql_bin_log.remove_pending_rows_event(this, is_transactional);
- if (clear_maps)
- binlog_table_maps= 0;
-
+ if (reset_stmt)
+ reset_binlog_for_next_statement();
DBUG_RETURN(0);
}
+
int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
{
DBUG_ENTER("THD::binlog_flush_pending_rows_event");
@@ -6827,9 +7089,8 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
if (stmt_end)
{
pending->set_flags(Rows_log_event::STMT_END_F);
- binlog_table_maps= 0;
+ reset_binlog_for_next_statement();
}
-
error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0,
is_transactional);
}
@@ -7189,20 +7450,23 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
log event is written to the binary log, we pretend that no
table maps were written.
*/
- if(binlog_should_compress(query_len))
+ if (binlog_should_compress(query_len))
{
- Query_compressed_log_event qinfo(this, query_arg, query_len, is_trans, direct,
- suppress_use, errcode);
+ Query_compressed_log_event qinfo(this, query_arg, query_len, is_trans,
+ direct, suppress_use, errcode);
error= mysql_bin_log.write(&qinfo);
}
else
{
Query_log_event qinfo(this, query_arg, query_len, is_trans, direct,
- suppress_use, errcode);
+ suppress_use, errcode);
error= mysql_bin_log.write(&qinfo);
}
+ /*
+ row logged binlog may not have been reset in the case of locked tables
+ */
+ reset_binlog_for_next_statement();
- binlog_table_maps= 0;
DBUG_RETURN(error >= 0 ? error : 1);
}
@@ -7213,6 +7477,38 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
DBUG_RETURN(0);
}
+
+/**
+ Binlog current query as a statement, ignoring the binlog filter setting.
+
+ The filter is in decide_logging_format() to mark queries to not be stored
+ in the binary log, for example by a shared distributed engine like S3.
+ This function resets the filter to ensure the the query is logged if
+ the binlog is active.
+
+ Note that 'direct' is set to false, which means that the query will
+ not be directly written to the binary log but instead to the cache.
+
+ @retval false ok
+ @retval true error
+*/
+
+
+bool THD::binlog_current_query_unfiltered()
+{
+ if (!mysql_bin_log.is_open())
+ return 0;
+
+ reset_binlog_local_stmt_filter();
+ clear_binlog_local_stmt_filter();
+ return binlog_query(THD::STMT_QUERY_TYPE, query(), query_length(),
+ /* is_trans */ FALSE,
+ /* direct */ FALSE,
+ /* suppress_use */ FALSE,
+ /* Error */ 0) > 0;
+}
+
+
void
THD::wait_for_wakeup_ready()
{
@@ -7238,11 +7534,10 @@ void THD::set_last_commit_gtid(rpl_gtid &gtid)
#endif
m_last_commit_gtid= gtid;
#ifndef EMBEDDED_LIBRARY
- if (changed_gtid && session_tracker.sysvars.is_enabled())
+ if (changed_gtid)
{
DBUG_ASSERT(current_thd == this);
- session_tracker.sysvars.
- mark_as_changed(this, (LEX_CSTRING*)Sys_last_gtid_ptr);
+ session_tracker.sysvars.mark_as_changed(this, Sys_last_gtid_ptr);
}
#endif
}
@@ -7313,6 +7608,7 @@ wait_for_commit::~wait_for_commit()
mysql_cond_destroy(&COND_wait_commit);
}
+
void
wait_for_commit::wakeup(int wakeup_error)
{
@@ -7720,3 +8016,8 @@ bool THD::timestamp_to_TIME(MYSQL_TIME *ltime, my_time_t ts,
}
return 0;
}
+
+THD_list_iterator *THD_list_iterator::iterator()
+{
+ return &server_threads;
+}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index fb22b0bcf6d..87773b8338a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -75,14 +75,15 @@ void set_thd_stage_info(void *thd,
#include "wsrep_condition_variable.h"
class Wsrep_applier_service;
-
#endif /* WITH_WSREP */
+
class Reprepare_observer;
class Relay_log_info;
struct rpl_group_info;
class Rpl_filter;
class Query_log_event;
class Load_log_event;
+class Log_event_writer;
class sp_rcontext;
class sp_cache;
class Lex_input_stream;
@@ -106,7 +107,8 @@ enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
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};
+ SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING,
+ SLAVE_RUN_TRIGGERS_FOR_RBR_ENFORCE};
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
@@ -193,7 +195,14 @@ extern char empty_c_string[1];
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd);
+extern "C" unsigned long long thd_query_id(const MYSQL_THD thd);
extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen);
+extern "C" const char *thd_user_name(MYSQL_THD thd);
+extern "C" const char *thd_client_host(MYSQL_THD thd);
+extern "C" const char *thd_client_ip(MYSQL_THD thd);
+extern "C" LEX_CSTRING *thd_current_db(MYSQL_THD thd);
+extern "C" int thd_current_status(MYSQL_THD thd);
+extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd);
/**
@class CSET_STRING
@@ -291,6 +300,17 @@ public:
*/
Key_part_spec *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Key_part_spec(*this); }
+ bool check_key_for_blob(const class handler *file) const;
+ bool check_key_length_for_blob() const;
+ bool check_primary_key_for_blob(const class handler *file) const
+ {
+ return check_key_for_blob(file) || check_key_length_for_blob();
+ }
+ bool check_foreign_key_for_blob(const class handler *file) const
+ {
+ return check_key_for_blob(file) || check_key_length_for_blob();
+ }
+ bool init_multiple_key_for_blob(const class handler *file);
};
@@ -323,17 +343,41 @@ public:
class Alter_column :public Sql_alloc {
public:
- const char *name;
+ LEX_CSTRING name;
+ LEX_CSTRING new_name;
Virtual_column_info *default_value;
bool alter_if_exists;
- Alter_column(const char *par_name, Virtual_column_info *expr, bool par_exists)
- :name(par_name), default_value(expr), alter_if_exists(par_exists) {}
+ Alter_column(LEX_CSTRING par_name, Virtual_column_info *expr, bool par_exists)
+ :name(par_name), new_name{NULL, 0}, default_value(expr), alter_if_exists(par_exists) {}
+ Alter_column(LEX_CSTRING par_name, LEX_CSTRING _new_name, bool exists)
+ :name(par_name), new_name(_new_name), default_value(NULL), alter_if_exists(exists) {}
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for Key_part_spec::clone
*/
Alter_column *clone(MEM_ROOT *mem_root) const
{ return new (mem_root) Alter_column(*this); }
+ bool is_rename()
+ {
+ DBUG_ASSERT(!new_name.str || !default_value);
+ return new_name.str;
+ }
+};
+
+
+class Alter_rename_key : public Sql_alloc
+{
+public:
+ LEX_CSTRING old_name;
+ LEX_CSTRING new_name;
+ bool alter_if_exists;
+
+ Alter_rename_key(LEX_CSTRING old_name_arg, LEX_CSTRING new_name_arg, bool exists)
+ : old_name(old_name_arg), new_name(new_name_arg), alter_if_exists(exists) {}
+
+ Alter_rename_key *clone(MEM_ROOT *mem_root) const
+ { return new (mem_root) Alter_rename_key(*this); }
+
};
@@ -347,13 +391,15 @@ public:
engine_option_value *option_list;
bool generated;
bool invisible;
+ bool without_overlaps;
+ Lex_ident period;
Key(enum Keytype type_par, const LEX_CSTRING *name_arg,
ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options)
:DDL_options(ddl_options),
type(type_par), key_create_info(default_key_create_info),
name(*name_arg), option_list(NULL), generated(generated_arg),
- invisible(false)
+ invisible(false), without_overlaps(false)
{
key_create_info.algorithm= algorithm_arg;
}
@@ -364,7 +410,7 @@ public:
:DDL_options(ddl_options),
type(type_par), key_create_info(*key_info_arg), columns(*cols),
name(*name_arg), option_list(create_opt), generated(generated_arg),
- invisible(false)
+ invisible(false), without_overlaps(false)
{}
Key(const Key &rhs, MEM_ROOT *mem_root);
virtual ~Key() {}
@@ -383,12 +429,14 @@ class Foreign_key: public Key {
public:
enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL,
FK_MATCH_PARTIAL, FK_MATCH_SIMPLE};
+ LEX_CSTRING constraint_name;
LEX_CSTRING ref_db;
LEX_CSTRING ref_table;
List<Key_part_spec> ref_columns;
enum enum_fk_option delete_opt, update_opt;
enum fk_match_opt match_opt;
Foreign_key(const LEX_CSTRING *name_arg, List<Key_part_spec> *cols,
+ const LEX_CSTRING *constraint_name_arg,
const LEX_CSTRING *ref_db_arg, const LEX_CSTRING *ref_table_arg,
List<Key_part_spec> *ref_cols,
enum_fk_option delete_opt_arg, enum_fk_option update_opt_arg,
@@ -396,6 +444,7 @@ public:
DDL_options ddl_options)
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL,
ddl_options),
+ constraint_name(*constraint_name_arg),
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)
@@ -427,8 +476,8 @@ class LEX_COLUMN : public Sql_alloc
{
public:
String column;
- uint rights;
- LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {}
+ privilege_t rights;
+ LEX_COLUMN (const String& x,const privilege_t & y ): column (x),rights (y) {}
};
class MY_LOCALE;
@@ -616,6 +665,7 @@ typedef struct system_variables
are based on the cluster size):
*/
ulong saved_auto_increment_increment, saved_auto_increment_offset;
+ ulonglong wsrep_gtid_seq_no;
#endif /* WITH_WSREP */
uint eq_range_index_dive_limit;
ulong column_compression_zlib_strategy;
@@ -629,7 +679,6 @@ typedef struct system_variables
ulong max_tmp_tables;
ulong max_insert_delayed_threads;
ulong min_examined_row_limit;
- ulong multi_range_count;
ulong net_buffer_length;
ulong net_interactive_timeout;
ulong net_read_timeout;
@@ -700,11 +749,6 @@ typedef struct system_variables
my_bool query_cache_strip_comments;
my_bool sql_log_slow;
my_bool sql_log_bin;
- /*
- A flag to help detect whether binary logging was temporarily disabled
- (see tmp_disable_binlog(A) macro).
- */
- my_bool sql_log_bin_off;
my_bool binlog_annotate_row_events;
my_bool binlog_direct_non_trans_update;
my_bool column_compression_zlib_wrap;
@@ -757,6 +801,9 @@ typedef struct system_variables
ulong session_track_transaction_info;
my_bool session_track_schema;
my_bool session_track_state_change;
+#ifdef USER_VAR_TRACKING
+ my_bool session_track_user_variables;
+#endif // USER_VAR_TRACKING
my_bool tcp_nodelay;
ulong threadpool_priority;
@@ -868,6 +915,7 @@ typedef struct system_status_var
ulong feature_system_versioning; /* +1 opening a table WITH SYSTEM VERSIONING */
ulong feature_application_time_periods;
/* +1 opening a table with application-time period */
+ ulong feature_insert_returning; /* +1 when INSERT...RETURNING is used */
ulong feature_timezone; /* +1 when XPATH is used */
ulong feature_trigger; /* +1 opening a table with triggers */
ulong feature_xml; /* +1 when XPATH is used */
@@ -921,6 +969,11 @@ typedef struct system_status_var
#define last_system_status_var questions
#define last_cleared_system_status_var local_memory_used
+/** Number of contiguous global status variables */
+constexpr int COUNT_GLOBAL_STATUS_VARS= int(offsetof(STATUS_VAR,
+ last_system_status_var) /
+ sizeof(ulong)) + 1;
+
/*
Global status variables
*/
@@ -978,6 +1031,39 @@ inline bool is_supported_parser_charset(CHARSET_INFO *cs)
return MY_TEST(cs->mbminlen == 1);
}
+/** THD registry */
+class THD_list_iterator
+{
+protected:
+ I_List<THD> threads;
+ mutable mysql_rwlock_t lock;
+
+public:
+
+ /**
+ Iterates registered threads.
+
+ @param action called for every element
+ @param argument opque argument passed to action
+
+ @return
+ @retval 0 iteration completed successfully
+ @retval 1 iteration was interrupted (action returned 1)
+ */
+ template <typename T> int iterate(my_bool (*action)(THD *thd, T *arg), T *arg= 0)
+ {
+ int res= 0;
+ mysql_rwlock_rdlock(&lock);
+ I_List_iterator<THD> it(threads);
+ while (auto tmp= it++)
+ if ((res= action(tmp, arg)))
+ break;
+ mysql_rwlock_unlock(&lock);
+ return res;
+ }
+ static THD_list_iterator *iterator();
+};
+
#ifdef MYSQL_SERVER
void free_tmp_table(THD *thd, TABLE *entry);
@@ -1287,7 +1373,10 @@ struct st_savepoint {
class Security_context {
public:
- Security_context() {} /* Remove gcc warning */
+ Security_context()
+ :master_access(NO_ACL),
+ db_access(NO_ACL)
+ {} /* Remove gcc warning */
/*
host - host of the client
user - user of the client, set to NULL until the user has been read from
@@ -1307,8 +1396,8 @@ public:
char *external_user;
/* points to host if host is available, otherwise points to ip */
const char *host_or_ip;
- ulong master_access; /* Global privileges from mysql.user */
- ulong db_access; /* Privileges for current db */
+ privilege_t master_access; /* Global privileges from mysql.user */
+ privilege_t db_access; /* Privileges for current db */
bool password_expired;
@@ -1341,7 +1430,7 @@ public:
* privileges.
@return True if the security context fulfills the access requirements.
*/
- bool check_access(ulong want_access, bool match_any = false);
+ bool check_access(const privilege_t want_access, bool match_any = false);
bool is_priv_user(const char *user, const char *host);
};
@@ -1890,12 +1979,11 @@ public:
m_locked_tables_count(0),
some_table_marked_for_reopen(0)
{
- init_sql_alloc(&m_locked_tables_root, "Locked_tables_list",
- MEM_ROOT_BLOCK_SIZE, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_locked_table_list, &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);
+ int unlock_locked_tables(THD *thd);
+ int unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket);
~Locked_tables_list()
{
reset();
@@ -1944,6 +2032,14 @@ struct Ha_data
*/
plugin_ref lock;
Ha_data() :ha_ptr(NULL) {}
+
+ void reset()
+ {
+ ha_ptr= nullptr;
+ for (auto &info : ha_info)
+ info.reset();
+ lock= nullptr;
+ }
};
/**
@@ -2140,6 +2236,22 @@ struct wait_for_commit
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
+class Gap_time_tracker;
+
+/*
+ Thread context for Gap_time_tracker class.
+*/
+class Gap_time_tracker_data
+{
+public:
+ Gap_time_tracker_data(): bill_to(NULL) {}
+
+ Gap_time_tracker *bill_to;
+ ulonglong start_time;
+
+ void init() { bill_to = NULL; }
+};
+
/**
A wrapper around thread_count.
@@ -2321,9 +2433,22 @@ public:
*/
const char *proc_info;
+ void set_psi(PSI_thread *psi)
+ {
+ my_atomic_storeptr((void*volatile*)&m_psi, psi);
+ }
+
+ PSI_thread* get_psi()
+ {
+ return static_cast<PSI_thread*>(my_atomic_loadptr((void*volatile*)&m_psi));
+ }
+
private:
unsigned int m_current_stage_key;
+ /** Performance schema thread instrumentation for this session. */
+ PSI_thread *m_psi;
+
public:
void enter_stage(const PSI_stage_info *stage,
const char *calling_func,
@@ -2340,7 +2465,7 @@ public:
calling_line);
#endif
#ifdef HAVE_PSI_THREAD_INTERFACE
- MYSQL_SET_STAGE(m_current_stage_key, calling_file, calling_line);
+ m_stage_progress_psi= MYSQL_SET_STAGE(m_current_stage_key, calling_file, calling_line);
#endif
}
@@ -2400,7 +2525,8 @@ public:
// track down slow pthread_create
ulonglong prior_thr_create_utime, thr_create_utime;
ulonglong start_utime, utime_after_lock, utime_after_query;
-
+ /* This can be used by handlers to send signals to the SQL level */
+ ulonglong replication_flags;
// Process indicator
struct {
/*
@@ -2475,14 +2601,17 @@ public:
*/
void binlog_start_trans_and_stmt();
void binlog_set_stmt_begin();
- int binlog_write_table_map(TABLE *table, bool is_transactional,
- my_bool *with_annotate= 0);
int binlog_write_row(TABLE* table, bool is_transactional,
const uchar *buf);
int binlog_delete_row(TABLE* table, bool is_transactional,
const uchar *buf);
int binlog_update_row(TABLE* table, bool is_transactional,
const uchar *old_data, const uchar *new_data);
+ bool prepare_handlers_for_update(uint flag);
+ bool binlog_write_annotated_row(Log_event_writer *writer);
+ void binlog_prepare_for_row_logging();
+ bool binlog_write_table_maps();
+ bool binlog_write_table_map(TABLE *table, bool with_annotate);
static void binlog_prepare_row_images(TABLE* table);
void set_server_id(uint32 sid) { variables.server_id = sid; }
@@ -2576,22 +2705,21 @@ private:
*/
enum_binlog_format current_stmt_binlog_format;
- /*
- Number of outstanding table maps, i.e., table maps in the
- transaction cache.
- */
- uint binlog_table_maps;
public:
+
+ /* 1 if binlog table maps has been written */
+ bool binlog_table_maps;
+
void issue_unsafe_warnings();
void reset_unsafe_warnings()
{ binlog_unsafe_warning_flags= 0; }
- uint get_binlog_table_maps() const {
- return binlog_table_maps;
- }
- void clear_binlog_table_maps() {
+ void reset_binlog_for_next_statement()
+ {
binlog_table_maps= 0;
}
+ bool binlog_table_should_be_logged(const LEX_CSTRING *db);
+
#endif /* MYSQL_CLIENT */
public:
@@ -2631,6 +2759,10 @@ public:
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
+ void free()
+ {
+ free_root(&mem_root,MYF(0));
+ }
bool is_active()
{
return (all.ha_list != NULL);
@@ -2643,11 +2775,10 @@ public:
{
bzero((char*)this, sizeof(*this));
implicit_xid.null();
- init_sql_alloc(&mem_root, "THD::transactions",
- ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_thd_transactions, &mem_root,
+ ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
}
- } transaction;
+ } default_transaction, *transaction;
Global_read_lock global_read_lock;
Field *dup_field;
#ifndef __WIN__
@@ -2943,6 +3074,8 @@ public:
PROFILING profiling;
#endif
+ /** Current stage progress instrumentation. */
+ PSI_stage_progress *m_stage_progress_psi;
/** Current statement digest. */
sql_digest_state *m_digest;
/** Current statement digest token array. */
@@ -2956,6 +3089,14 @@ public:
/** Current statement instrumentation state. */
PSI_statement_locker_state m_statement_state;
#endif /* HAVE_PSI_STATEMENT_INTERFACE */
+
+ /** Current transaction instrumentation. */
+ PSI_transaction_locker *m_transaction_psi;
+#ifdef HAVE_PSI_TRANSACTION_INTERFACE
+ /** Current transaction instrumentation state. */
+ PSI_transaction_locker_state m_transaction_state;
+#endif /* HAVE_PSI_TRANSACTION_INTERFACE */
+
/** Idle instrumentation. */
PSI_idle_locker *m_idle_psi;
#ifdef HAVE_PSI_IDLE_INTERFACE
@@ -2972,7 +3113,7 @@ public:
update auto-updatable fields (like auto_increment and timestamp).
*/
query_id_t query_id;
- ulong col_access;
+ privilege_t col_access;
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;
@@ -3080,7 +3221,6 @@ public:
uint8 password; /* 0, 1 or 2 */
uint8 failed_com_change_user;
bool slave_thread;
- bool extra_port; /* If extra connection */
bool no_errors;
/**
@@ -3284,7 +3424,7 @@ public:
void cleanup_after_query();
void free_connection();
void reset_for_reuse();
- bool store_globals();
+ void store_globals();
void reset_globals();
bool trace_started()
{
@@ -3336,6 +3476,7 @@ public:
*/
Apc_target apc_target;
+ Gap_time_tracker_data gap_tracker_data;
#ifndef MYSQL_CLIENT
enum enum_binlog_query_type {
/* The query can be logged in row format or in statement format. */
@@ -3351,6 +3492,7 @@ public:
char const *query, ulong query_len, bool is_trans,
bool direct, bool suppress_use,
int errcode);
+ bool binlog_current_query_unfiltered();
#endif
inline void
@@ -3475,8 +3617,8 @@ public:
timeval transaction_time()
{
if (!in_multi_stmt_transaction_mode())
- transaction.start_time.reset(this);
- return transaction.start_time;
+ transaction->start_time.reset(this);
+ return transaction->start_time;
}
inline void set_start_time()
@@ -3630,6 +3772,8 @@ public:
{
return server_status & SERVER_STATUS_IN_TRANS;
}
+ /* Commit both statement and full transaction */
+ int commit_whole_transaction_and_close_tables();
void give_protection_error();
/*
Give an error if any of the following is true for this connection
@@ -3656,7 +3800,19 @@ public:
}
inline void* trans_alloc(size_t size)
{
- return alloc_root(&transaction.mem_root,size);
+ return alloc_root(&transaction->mem_root,size);
+ }
+
+ LEX_CSTRING strmake_lex_cstring(const char *str, size_t length)
+ {
+ const char *tmp= strmake_root(mem_root, str, length);
+ if (!tmp)
+ return {0,0};
+ return {tmp, length};
+ }
+ LEX_CSTRING strmake_lex_cstring(const LEX_CSTRING &from)
+ {
+ return strmake_lex_cstring(from.str, from.length);
}
LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, size_t length)
@@ -3768,7 +3924,7 @@ public:
const char *src, size_t src_length);
/*
If either "dstcs" or "srccs" is &my_charset_bin,
- then performs native copying using cs->cset->copy_fix().
+ then performs native copying using copy_fix().
Otherwise, performs Unicode conversion using convert_fix().
*/
bool copy_fix(CHARSET_INFO *dstcs, LEX_STRING *dst,
@@ -3802,10 +3958,10 @@ public:
@param repertoire - the repertoire of the string
*/
Item_basic_constant *make_string_literal(const char *str, size_t length,
- uint repertoire);
+ my_repertoire_t repertoire);
Item_basic_constant *make_string_literal(const Lex_string_with_metadata_st &str)
{
- uint repertoire= str.repertoire(variables.character_set_client);
+ my_repertoire_t repertoire= str.repertoire(variables.character_set_client);
return make_string_literal(str.str, str.length, repertoire);
}
Item_basic_constant *make_string_literal_nchar(const Lex_string_with_metadata_st &str);
@@ -4068,7 +4224,7 @@ public:
inline bool really_abort_on_warning()
{
return (abort_on_warning &&
- (!transaction.stmt.modified_non_trans_table ||
+ (!transaction->stmt.modified_non_trans_table ||
(variables.sql_mode & MODE_STRICT_ALL_TABLES)));
}
void set_status_var_init();
@@ -4580,7 +4736,7 @@ public:
information to decide the logging format. So that cases we call decide_logging_format_2
at later stages in execution.
One example would be binlog format for IODKU but column with unique key is not inserted.
- We dont have inserted columns info when we call decide_logging_format so on later stage we call
+ We don't have inserted columns info when we call decide_logging_format so on later stage we call
decide_logging_format_low
@returns 0 if no format is changed
@@ -4657,6 +4813,7 @@ public:
}
void mark_transaction_to_rollback(bool all);
+ bool internal_transaction() { return transaction != &default_transaction; }
private:
/** The current internal error handler for this thread, or NULL. */
@@ -4701,9 +4858,7 @@ private:
AUTHID invoker;
public:
-#ifndef EMBEDDED_LIBRARY
Session_tracker session_tracker;
-#endif //EMBEDDED_LIBRARY
/*
Flag, mutex and condition for a thread to wait for a signal from another
thread.
@@ -4730,6 +4885,17 @@ public:
LF_PINS *xid_hash_pins;
bool fix_xid_hash_pins();
+ const XID *get_xid() const
+ {
+#ifdef WITH_WSREP
+ if (!wsrep_xid.is_null())
+ return &wsrep_xid;
+#endif /* WITH_WSREP */
+ return (transaction->xid_state.is_explicit_XA() ?
+ transaction->xid_state.get_xid() :
+ &transaction->implicit_xid);
+ }
+
/* Members related to temporary tables. */
public:
/* Opened table states. */
@@ -4739,6 +4905,7 @@ public:
TMP_TABLE_ANY
};
bool has_thd_temporary_tables();
+ bool has_temporary_tables();
TABLE *create_and_open_tmp_table(LEX_CUSTRING *frm,
const char *path,
@@ -4777,7 +4944,6 @@ private:
/* Whether a lock has been acquired? */
bool m_tmp_tables_locked;
- bool has_temporary_tables();
uint create_tmp_table_def_key(char *key, const char *db,
const char *table_name);
TMP_TABLE_SHARE *create_temporary_table(LEX_CUSTRING *frm,
@@ -4863,17 +5029,13 @@ public:
size_t wsrep_TOI_pre_query_len;
wsrep_po_handle_t wsrep_po_handle;
size_t wsrep_po_cnt;
-#ifdef GTID_SUPPORT
- my_bool wsrep_po_in_trans;
- rpl_sid wsrep_po_sid;
-#endif /* GTID_SUPPORT */
void *wsrep_apply_format;
uchar* wsrep_rbr_buf;
wsrep_gtid_t wsrep_sync_wait_gtid;
- // wsrep_gtid_t wsrep_last_written_gtid;
+ uint64 wsrep_last_written_gtid_seqno;
+ uint64 wsrep_current_gtid_seqno;
ulong wsrep_affected_rows;
bool wsrep_has_ignored_error;
- bool wsrep_replicate_GTID;
/*
When enabled, do not replicate/binlog updates from the current table that's
@@ -4976,10 +5138,10 @@ public:
/* Copy relevant `stmt` transaction flags to `all` transaction. */
void merge_unsafe_rollback_flags()
{
- if (transaction.stmt.modified_non_trans_table)
- transaction.all.modified_non_trans_table= TRUE;
- transaction.all.m_unsafe_rollback_flags|=
- (transaction.stmt.m_unsafe_rollback_flags &
+ if (transaction->stmt.modified_non_trans_table)
+ transaction->all.modified_non_trans_table= TRUE;
+ transaction->all.m_unsafe_rollback_flags|=
+ (transaction->stmt.m_unsafe_rollback_flags &
(THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE |
THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL));
}
@@ -4989,7 +5151,7 @@ public:
{
if (in_active_multi_stmt_transaction())
{
- if (transaction.all.is_trx_read_write())
+ if (transaction->all.is_trx_read_write())
{
if (variables.idle_write_transaction_timeout > 0)
return variables.idle_write_transaction_timeout;
@@ -5037,6 +5199,41 @@ public:
};
+
+/*
+ Start a new independent transaction for the THD.
+ The old one is stored in this object and restored when calling
+ restore_old_transaction() or when the object is freed
+*/
+
+class start_new_trans
+{
+ /* container for handler's private per-connection data */
+ Ha_data old_ha_data[MAX_HA];
+ struct THD::st_transactions *old_transaction, new_transaction;
+ Open_tables_backup open_tables_state_backup;
+ MDL_savepoint mdl_savepoint;
+ PSI_transaction_locker *m_transaction_psi;
+ THD *org_thd;
+ uint in_sub_stmt;
+ uint server_status;
+ my_bool wsrep_on;
+
+public:
+ start_new_trans(THD *thd);
+ ~start_new_trans()
+ {
+ destroy();
+ }
+ void destroy()
+ {
+ if (org_thd) // Safety
+ restore_old_transaction();
+ new_transaction.free();
+ }
+ void restore_old_transaction();
+};
+
/** A short cut for thd->get_stmt_da()->set_ok_status(). */
inline void
@@ -5063,11 +5260,10 @@ my_eof(THD *thd)
#define tmp_disable_binlog(A) \
{ulonglong tmp_disable_binlog__save_options= (A)->variables.option_bits; \
(A)->variables.option_bits&= ~OPTION_BIN_LOG; \
- (A)->variables.sql_log_bin_off= 1;
+ (A)->variables.option_bits|= OPTION_BIN_TMP_LOG_OFF;
#define reenable_binlog(A) \
- (A)->variables.option_bits= tmp_disable_binlog__save_options; \
- (A)->variables.sql_log_bin_off= 0;}
+ (A)->variables.option_bits= tmp_disable_binlog__save_options; }
inline date_conv_mode_t sql_mode_for_dates(THD *thd)
@@ -5117,6 +5313,18 @@ class select_result_sink: public Sql_alloc
public:
THD *thd;
select_result_sink(THD *thd_arg): thd(thd_arg) {}
+ inline int send_data_with_check(List<Item> &items,
+ SELECT_LEX_UNIT *u,
+ ha_rows sent)
+ {
+ if (u->lim.check_offset(sent))
+ return 0;
+
+ if (u->thd->killed == ABORT_QUERY)
+ return 0;
+
+ return send_data(items);
+ }
/*
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.
@@ -5152,7 +5360,7 @@ protected:
/* Something used only by the parser: */
public:
ha_rows est_records; /* estimated number of records in the result */
- select_result(THD *thd_arg): select_result_sink(thd_arg), est_records(0) {}
+ select_result(THD *thd_arg): select_result_sink(thd_arg), est_records(0) {}
void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
virtual ~select_result() {};
/**
@@ -5218,9 +5426,9 @@ public:
/* this method is called just before the first row of the table can be read */
virtual void prepare_to_read_rows() {}
- void reset_offset_limit()
+ void remove_offset_limit()
{
- unit->offset_limit_cnt= 0;
+ unit->lim.remove_offset();
}
/*
@@ -5246,7 +5454,7 @@ public:
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
+ output of a SELECT_LEX_UNIT (and so we don't apply "LIMIT x,y" from the
unit)
- We don't try to convert the target table to MyISAM
*/
@@ -5527,16 +5735,17 @@ public:
class select_insert :public select_result_interceptor {
public:
+ select_result *sel_result;
TABLE_LIST *table_list;
TABLE *table;
List<Item> *fields;
ulonglong autoinc_value_of_last_inserted_row; // autogenerated or not
COPY_INFO info;
bool insert_into_view;
- select_insert(THD *thd_arg, TABLE_LIST *table_list_par,
- TABLE *table_par, List<Item> *fields_par,
- List<Item> *update_fields, List<Item> *update_values,
- enum_duplicates duplic, bool ignore);
+ select_insert(THD *thd_arg, TABLE_LIST *table_list_par, TABLE *table_par,
+ List<Item> *fields_par, List<Item> *update_fields,
+ List<Item> *update_values, enum_duplicates duplic,
+ bool ignore, select_result *sel_ret_list);
~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
virtual int prepare2(JOIN *join);
@@ -5572,7 +5781,7 @@ public:
List<Item> &select_fields,enum_duplicates duplic, bool ignore,
TABLE_LIST *select_tables_arg):
select_insert(thd_arg, table_arg, NULL, &select_fields, 0, 0, duplic,
- ignore),
+ ignore, NULL),
create_table(table_arg),
create_info(create_info_par),
select_tables(select_tables_arg),
@@ -5582,7 +5791,6 @@ public:
{}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- int binlog_show_create_table(TABLE **tables, uint count);
void store_values(List<Item> &values);
bool send_eof();
virtual void abort_result_set();
@@ -5727,17 +5935,18 @@ public:
class select_unit :public select_result_interceptor
{
+protected:
uint curr_step, prev_step, curr_sel;
enum sub_select_type step;
public:
- Item_int *intersect_mark;
TMP_TABLE_PARAM tmp_table_param;
+ /* Number of additional (hidden) field of the used temporary table */
+ int addon_cnt;
int write_err; /* Error code from the last send_data->ha_write_row call. */
TABLE *table;
select_unit(THD *thd_arg):
- select_result_interceptor(thd_arg),
- intersect_mark(0), table(0)
+ select_result_interceptor(thd_arg), addon_cnt(0), table(0)
{
init();
tmp_table_param.init();
@@ -5754,6 +5963,9 @@ public:
virtual bool postponed_prepare(List<Item> &types)
{ return false; }
int send_data(List<Item> &items);
+ int write_record();
+ int update_counter(Field *counter, longlong value);
+ int delete_record();
bool send_eof();
virtual bool flush();
void cleanup();
@@ -5772,7 +5984,148 @@ public:
step= UNION_TYPE;
write_err= 0;
}
+ virtual void change_select();
+ virtual bool force_enable_index_if_needed() { return false; }
+};
+
+
+/**
+ @class select_unit_ext
+
+ The class used when processing rows produced by operands of query expressions
+ containing INTERSECT ALL and/or EXCEPT all operations. One or two extra fields
+ of the temporary to store the rows of the partial and final result can be employed.
+ Both of them contain counters. The second additional field is used only when
+ the processed query expression contains INTERSECT ALL.
+
+ Consider how these extra fields are used.
+
+ Let
+ table t1 (f char(8))
+ table t2 (f char(8))
+ table t3 (f char(8))
+ contain the following sets:
+ ("b"),("a"),("d"),("c"),("b"),("a"),("c"),("a")
+ ("c"),("b"),("c"),("c"),("a"),("b"),("g")
+ ("c"),("a"),("b"),("d"),("b"),("e")
+
+ - Let's demonstrate how the the set operation INTERSECT ALL is proceesed
+ for the query
+ SELECT f FROM t1 INTERSECT ALL SELECT f FROM t2
+
+ When send_data() is called for the rows of the first operand we put
+ the processed record into the temporary table if there was no such record
+ setting dup_cnt field to 1 and add_cnt field to 0 and increment the
+ counter in the dup_cnt field by one otherwise. We get
+
+ |add_cnt|dup_cnt| f |
+ |0 |2 |b |
+ |0 |3 |a |
+ |0 |1 |d |
+ |0 |2 |c |
+
+ The call of send_eof() for the first operand swaps the values stored in
+ dup_cnt and add_cnt. After this, we'll see the following rows in the
+ temporary table
+
+ |add_cnt|dup_cnt| f |
+ |2 |0 |b |
+ |3 |0 |a |
+ |1 |0 |d |
+ |2 |0 |c |
+
+ When send_data() is called for the rows of the second operand we increment
+ the counter in dup_cnt if the processed row is found in the table and do
+ nothing otherwise. As a result we get
+
+ |add_cnt|dup_cnt| f |
+ |2 |2 |b |
+ |3 |1 |a |
+ |1 |0 |d |
+ |2 |3 |c |
+
+ At the call of send_eof() for the second operand first we disable index.
+ Then for each record, the minimum of counters from dup_cnt and add_cnt m is
+ taken. If m == 0 then the record is deleted. Otherwise record is replaced
+ with m copies of it. Yet the counter in this copies are set to 1 for
+ dup_cnt and to 0 for add_cnt
+
+ |add_cnt|dup_cnt| f |
+ |0 |1 |b |
+ |0 |1 |b |
+ |0 |1 |a |
+ |0 |1 |c |
+ |0 |1 |c |
+
+ - Let's demonstrate how the the set operation EXCEPT ALL is proceesed
+ for the query
+ SELECT f FROM t1 EXCEPT ALL SELECT f FROM t3
+
+ Only one additional counter field dup_cnt is used for EXCEPT ALL.
+ After the first operand has been processed we have in the temporary table
+
+ |dup_cnt| f |
+ |2 |b |
+ |3 |a |
+ |1 |d |
+ |2 |c |
+
+ When send_data() is called for the rows of the second operand we decrement
+ the counter in dup_cnt if the processed row is found in the table and do
+ nothing otherwise. If the counter becomes 0 we delete the record
+
+ |dup_cnt| f |
+ |2 |a |
+ |1 |c |
+
+ Finally at the call of send_eof() for the second operand we disable index
+ unfold rows adding duplicates
+
+ |dup_cnt| f |
+ |1 |a |
+ |1 |a |
+ |1 |c |
+ */
+
+class select_unit_ext :public select_unit
+{
+public:
+ select_unit_ext(THD *thd_arg):
+ select_unit(thd_arg), increment(0), is_index_enabled(TRUE),
+ curr_op_type(UNSPECIFIED)
+ {
+ };
+ int send_data(List<Item> &items);
void change_select();
+ int unfold_record(ha_rows cnt);
+ bool send_eof();
+ bool force_enable_index_if_needed()
+ {
+ is_index_enabled= true;
+ return true;
+ }
+ bool disable_index_if_needed(SELECT_LEX *curr_sl);
+
+ /*
+ How to change increment/decrement the counter in duplicate_cnt field
+ when processing a record produced by the current operand in send_data().
+ The value can be 1 or -1
+ */
+ int increment;
+ /* TRUE <=> the index of the result temporary table is enabled */
+ bool is_index_enabled;
+ /* The type of the set operation currently executed */
+ enum set_op_type curr_op_type;
+ /*
+ Points to the extra field of the temporary table where
+ duplicate counters are stored
+ */
+ Field *duplicate_cnt;
+ /*
+ Points to the extra field of the temporary table where additional
+ counters used only for INTERSECT ALL operations are stored
+ */
+ Field *additional_cnt;
};
class select_union_recursive :public select_unit
@@ -5886,7 +6239,7 @@ public:
*/
DBUG_ASSERT(false); /* purecov: inspected */
}
- void reset_offset_limit_cnt()
+ void remove_offset_limit()
{
// EXPLAIN should never output to a select_union_direct
DBUG_ASSERT(false); /* purecov: inspected */
@@ -6086,8 +6439,53 @@ public:
/* Structs used when sorting */
struct SORT_FIELD_ATTR
{
- uint length; /* Length of sort field */
- uint suffix_length; /* Length suffix (0-4) */
+ /*
+ If using mem-comparable fixed-size keys:
+ length of the mem-comparable image of the field, in bytes.
+
+ If using packed keys: still the same? Not clear what is the use of it.
+ */
+ uint length;
+
+ /*
+ For most datatypes, this is 0.
+ The exception are the VARBINARY columns.
+ For those columns, the comparison actually compares
+
+ (value_prefix(N), suffix=length(value))
+
+ Here value_prefix is either the whole value or its prefix if it was too
+ long, and the suffix is the length of the original value.
+ (this way, for values X and Y: if X=prefix(Y) then X compares as less
+ than Y
+ */
+ uint suffix_length;
+
+ /*
+ If using packed keys, number of bytes that are used to store the length
+ of the packed key.
+
+ */
+ uint length_bytes;
+
+ /* Max. length of the original value, in bytes */
+ uint original_length;
+ enum Type { FIXED_SIZE, VARIABLE_SIZE } type;
+ /*
+ TRUE : if the item or field is NULLABLE
+ FALSE : otherwise
+ */
+ bool maybe_null;
+ CHARSET_INFO *cs;
+ uint pack_sort_string(uchar *to, const Binary_string *str,
+ CHARSET_INFO *cs) const;
+ int compare_packed_fixed_size_vals(uchar *a, size_t *a_len,
+ uchar *b, size_t *b_len);
+ int compare_packed_varstrings(uchar *a, size_t *a_len,
+ uchar *b, size_t *b_len);
+ bool check_if_packing_possible(THD *thd) const;
+ bool is_variable_sized() { return type == VARIABLE_SIZE; }
+ void set_length_and_original_length(THD *thd, uint length_arg);
};
@@ -6194,7 +6592,7 @@ class user_var_entry
double val_real(bool *null_value);
longlong val_int(bool *null_value) const;
- String *val_str(bool *null_value, String *str, uint decimals);
+ String *val_str(bool *null_value, String *str, uint decimals) const;
my_decimal *val_decimal(bool *null_value, my_decimal *result);
CHARSET_INFO *charset() const { return m_charset; }
void set_charset(CHARSET_INFO *cs) { m_charset= cs; }
@@ -6628,8 +7026,8 @@ 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);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
- { error= write_row(buf); })
+ TABLE_IO_WAIT(tracker, PSI_TABLE_WRITE_ROW, MAX_KEY, error,
+ { error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
return error;
}
@@ -6639,7 +7037,7 @@ inline int handler::ha_delete_tmp_row(uchar *buf)
int error;
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_delete_count);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_DELETE_ROW, MAX_KEY, 0,
+ TABLE_IO_WAIT(tracker, PSI_TABLE_DELETE_ROW, MAX_KEY, error,
{ error= delete_row(buf); })
MYSQL_DELETE_ROW_DONE(error);
return error;
@@ -6650,12 +7048,16 @@ 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);
- TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
- { error= update_row(old_data, new_data);})
+ TABLE_IO_WAIT(tracker, PSI_TABLE_UPDATE_ROW, active_index, error,
+ { error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
+inline bool handler::has_long_unique()
+{
+ return table->s->long_unique_table;
+}
extern pthread_attr_t *get_connection_attrib(void);
@@ -6743,6 +7145,62 @@ class Switch_to_definer_security_ctx
};
+class Sql_mode_instant_set: public Sql_mode_save
+{
+public:
+ Sql_mode_instant_set(THD *thd, sql_mode_t temporary_value)
+ :Sql_mode_save(thd)
+ {
+ thd->variables.sql_mode= temporary_value;
+ }
+};
+
+
+class Sql_mode_instant_remove: public Sql_mode_save
+{
+public:
+ Sql_mode_instant_remove(THD *thd, sql_mode_t temporary_remove_flags)
+ :Sql_mode_save(thd)
+ {
+ thd->variables.sql_mode&= ~temporary_remove_flags;
+ }
+};
+
+
+class Abort_on_warning_instant_set
+{
+ THD *m_thd;
+ bool m_save_abort_on_warning;
+public:
+ Abort_on_warning_instant_set(THD *thd, bool temporary_value)
+ :m_thd(thd), m_save_abort_on_warning(thd->abort_on_warning)
+ {
+ thd->abort_on_warning= temporary_value;
+ }
+ ~Abort_on_warning_instant_set()
+ {
+ m_thd->abort_on_warning= m_save_abort_on_warning;
+ }
+};
+
+
+class Check_level_instant_set
+{
+ THD *m_thd;
+ enum_check_fields m_check_level;
+public:
+ Check_level_instant_set(THD *thd, enum_check_fields temporary_value)
+ :m_thd(thd), m_check_level(thd->count_cuted_fields)
+ {
+ thd->count_cuted_fields= temporary_value;
+ }
+ ~Check_level_instant_set()
+ {
+ m_thd->count_cuted_fields= m_check_level;
+ }
+};
+
+
/**
This class resembles the SQL Standard schema qualified object name:
<schema qualified name> ::= [ <schema name> <period> ] <qualified identifier>
@@ -6770,17 +7228,15 @@ public:
bool eq(const Database_qualified_name *other) const
{
CHARSET_INFO *cs= lower_case_table_names ?
- &my_charset_utf8_general_ci :
- &my_charset_utf8_bin;
+ &my_charset_utf8mb3_general_ci :
+ &my_charset_utf8mb3_bin;
return
m_db.length == other->m_db.length &&
m_name.length == other->m_name.length &&
- !my_strnncoll(cs,
- (const uchar *) m_db.str, m_db.length,
- (const uchar *) other->m_db.str, other->m_db.length) &&
- !my_strnncoll(cs,
- (const uchar *) m_name.str, m_name.length,
- (const uchar *) other->m_name.str, other->m_name.length);
+ !cs->strnncoll(m_db.str, m_db.length,
+ other->m_db.str, other->m_db.length) &&
+ !cs->strnncoll(m_name.str, m_name.length,
+ other->m_name.str, other->m_name.length);
}
void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db,
const LEX_CSTRING &name);
@@ -6856,10 +7312,9 @@ public:
class Type_holder: public Sql_alloc,
public Item_args,
public Type_handler_hybrid_field_type,
- public Type_all_attributes,
- public Type_geometry_attributes
+ public Type_all_attributes
{
- TYPELIB *m_typelib;
+ const TYPELIB *m_typelib;
bool m_maybe_null;
public:
Type_holder()
@@ -6882,19 +7337,11 @@ public:
DBUG_ASSERT(0);
return 0;
}
- void set_geometry_type(uint type)
- {
- Type_geometry_attributes::set_geometry_type(type);
- }
- uint uint_geometry_type() const
- {
- return Type_geometry_attributes::get_geometry_type();
- }
- void set_typelib(TYPELIB *typelib)
+ void set_typelib(const TYPELIB *typelib)
{
m_typelib= typelib;
}
- TYPELIB *get_typelib() const
+ const TYPELIB *get_typelib() const
{
return m_typelib;
}
@@ -6927,13 +7374,13 @@ class Sp_eval_expr_state
{
m_thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
m_thd->abort_on_warning= m_thd->is_strict_mode();
- m_thd->transaction.stmt.modified_non_trans_table= false;
+ m_thd->transaction->stmt.modified_non_trans_table= false;
}
void stop()
{
m_thd->count_cuted_fields= m_count_cuted_fields;
m_thd->abort_on_warning= m_abort_on_warning;
- m_thd->transaction.stmt.modified_non_trans_table=
+ m_thd->transaction->stmt.modified_non_trans_table=
m_stmt_modified_non_trans_table;
}
public:
@@ -6941,7 +7388,7 @@ public:
:m_thd(thd),
m_count_cuted_fields(thd->count_cuted_fields),
m_abort_on_warning(thd->abort_on_warning),
- m_stmt_modified_non_trans_table(thd->transaction.stmt.
+ m_stmt_modified_non_trans_table(thd->transaction->stmt.
modified_non_trans_table)
{
start();
@@ -6979,11 +7426,8 @@ private:
/** THD registry */
-class THD_list
+class THD_list: public THD_list_iterator
{
- I_List<THD> threads;
- mutable mysql_rwlock_t lock;
-
public:
/**
Constructor replacement.
@@ -7031,28 +7475,6 @@ public:
thd->unlink();
mysql_rwlock_unlock(&lock);
}
-
- /**
- Iterates registered threads.
-
- @param action called for every element
- @param argument opque argument passed to action
-
- @return
- @retval 0 iteration completed successfully
- @retval 1 iteration was interrupted (action returned 1)
- */
- template <typename T> int iterate(my_bool (*action)(THD *thd, T *arg), T *arg= 0)
- {
- int res= 0;
- mysql_rwlock_rdlock(&lock);
- I_List_iterator<THD> it(threads);
- while (auto tmp= it++)
- if ((res= action(tmp, arg)))
- break;
- mysql_rwlock_unlock(&lock);
- return res;
- }
};
extern THD_list server_threads;
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 7f1fd06aa46..ce34852117f 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -38,7 +38,7 @@ enum enum_sql_command {
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_PROCESSLIST, SQLCOM_SHOW_BINLOG_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,
@@ -208,6 +208,26 @@ protected:
}
};
+class Sql_cmd_show_slave_status: public Sql_cmd
+{
+protected:
+ bool show_all_slaves_status;
+public:
+ Sql_cmd_show_slave_status()
+ :show_all_slaves_status(false)
+ {}
+
+ Sql_cmd_show_slave_status(bool status_all)
+ :show_all_slaves_status(status_all)
+ {}
+
+ enum_sql_command sql_command_code() const { return SQLCOM_SHOW_SLAVE_STAT; }
+
+ bool execute(THD *thd);
+ bool is_show_all_slaves_stat() { return show_all_slaves_status; }
+};
+
+
class Sql_cmd_create_table_like: public Sql_cmd,
public Storage_engine_name
{
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 643b7ee898a..479a310542b 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -28,6 +28,7 @@
#endif
#include "sql_audit.h"
#include "sql_connect.h"
+#include "thread_cache.h"
#include "probes_mysql.h"
#include "sql_parse.h" // sql_command_flags,
// execute_init_command,
@@ -35,7 +36,6 @@
#include "sql_db.h" // mysql_change_db
#include "hostname.h" // inc_host_errors, ip_to_hostname,
// reset_host_errors
-#include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL
#include "sql_callback.h"
#ifdef WITH_WSREP
@@ -43,6 +43,7 @@
#include "wsrep_mysqld.h"
#endif /* WITH_WSREP */
#include "proxy_protocol.h"
+#include <ssl_compat.h>
HASH global_user_stats, global_client_stats, global_table_stats;
HASH global_index_stats;
@@ -80,8 +81,8 @@ int get_or_create_user_conn(THD *thd, const char *user,
{
/* First connection for user; Create a user connection object */
if (!(uc= ((struct user_conn*)
- my_malloc(sizeof(struct user_conn) + temp_len+1,
- MYF(MY_WME)))))
+ my_malloc(key_memory_user_conn,
+ sizeof(struct user_conn) + temp_len+1, MYF(MY_WME)))))
{
/* MY_WME ensures an error is set in THD. */
return_val= 1;
@@ -139,7 +140,7 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
if (global_system_variables.max_user_connections &&
!uc->user_resources.user_conn &&
global_system_variables.max_user_connections < uc->connections &&
- !(thd->security_ctx->master_access & SUPER_ACL))
+ !(thd->security_ctx->master_access & PRIV_IGNORE_MAX_USER_CONNECTIONS))
{
my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
error=1;
@@ -321,9 +322,9 @@ extern "C" void free_user(struct user_conn *uc)
void init_max_user_conn(void)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- my_hash_init(&hash_user_connections, system_charset_info, max_connections,
- 0, 0, (my_hash_get_key) get_key_conn,
- (my_hash_free_key) free_user, 0);
+ my_hash_init(key_memory_user_conn, &hash_user_connections,
+ system_charset_info, max_connections, 0, 0, (my_hash_get_key)
+ get_key_conn, (my_hash_free_key) free_user, 0);
#endif
}
@@ -482,14 +483,14 @@ void init_user_stats(USER_STATS *user_stats,
void init_global_user_stats(void)
{
- my_hash_init(&global_user_stats, system_charset_info, max_connections,
+ my_hash_init(PSI_INSTRUMENT_ME, &global_user_stats, system_charset_info, max_connections,
0, 0, (my_hash_get_key) get_key_user_stats,
(my_hash_free_key) free_user_stats, 0);
}
void init_global_client_stats(void)
{
- my_hash_init(&global_client_stats, system_charset_info, max_connections,
+ my_hash_init(PSI_INSTRUMENT_ME, &global_client_stats, system_charset_info, max_connections,
0, 0, (my_hash_get_key) get_key_user_stats,
(my_hash_free_key) free_user_stats, 0);
}
@@ -508,8 +509,8 @@ extern "C" void free_table_stats(TABLE_STATS* table_stats)
void init_global_table_stats(void)
{
- my_hash_init(&global_table_stats, system_charset_info, max_connections,
- 0, 0, (my_hash_get_key) get_key_table_stats,
+ my_hash_init(PSI_INSTRUMENT_ME, &global_table_stats, system_charset_info,
+ max_connections, 0, 0, (my_hash_get_key) get_key_table_stats,
(my_hash_free_key) free_table_stats, 0);
}
@@ -527,8 +528,8 @@ extern "C" void free_index_stats(INDEX_STATS* index_stats)
void init_global_index_stats(void)
{
- my_hash_init(&global_index_stats, system_charset_info, max_connections,
- 0, 0, (my_hash_get_key) get_key_index_stats,
+ my_hash_init(PSI_INSTRUMENT_ME, &global_index_stats, system_charset_info,
+ max_connections, 0, 0, (my_hash_get_key) get_key_index_stats,
(my_hash_free_key) free_index_stats, 0);
}
@@ -570,7 +571,7 @@ static bool increment_count_by_name(const char *name, size_t name_length,
{
/* First connection for this user or client */
if (!(user_stats= ((USER_STATS*)
- my_malloc(sizeof(USER_STATS),
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(USER_STATS),
MYF(MY_WME | MY_ZEROFILL)))))
return TRUE; // Out of memory
@@ -879,7 +880,7 @@ int thd_set_peer_addr(THD *thd,
}
my_free((void *)thd->main_security_ctx.ip);
- if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME))))
+ if (!(thd->main_security_ctx.ip = my_strdup(PSI_INSTRUMENT_ME, ip, MYF(MY_WME))))
{
/*
No error accounting per IP in host_cache,
@@ -1105,17 +1106,9 @@ static int check_connection(THD *thd)
In this case we will close the connection and increment status
*/
-bool setup_connection_thread_globals(THD *thd)
+void setup_connection_thread_globals(THD *thd)
{
- if (thd->store_globals())
- {
- close_connection(thd, ER_OUT_OF_RESOURCES);
- statistic_increment(aborted_connects,&LOCK_status);
- statistic_increment(connection_errors_internal, &LOCK_status);
- thd->scheduler->end_thread(thd, 0);
- return 1; // Error
- }
- return 0;
+ thd->store_globals();
}
@@ -1246,7 +1239,8 @@ void prepare_new_connection_state(THD* thd)
thd->set_command(COM_SLEEP);
thd->init_for_queries();
- if (opt_init_connect.length && !(sctx->master_access & SUPER_ACL))
+ if (opt_init_connect.length &&
+ !(sctx->master_access & PRIV_IGNORE_INIT_CONNECT))
{
execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
if (unlikely(thd->is_error()))
@@ -1285,7 +1279,6 @@ void prepare_new_connection_state(THD* thd)
}
thd->proc_info=0;
- thd->init_for_queries();
}
}
@@ -1313,7 +1306,16 @@ pthread_handler_t handle_one_connection(void *arg)
mysql_thread_set_psi_id(connect->thread_id);
- do_handle_one_connection(connect);
+ if (init_new_connection_handler_thread())
+ connect->close_with_error(0, 0, ER_OUT_OF_RESOURCES);
+ else
+ do_handle_one_connection(connect, true);
+
+ DBUG_PRINT("info", ("killing thread"));
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ ERR_remove_state(0);
+#endif
+ my_thread_end();
return 0;
}
@@ -1347,16 +1349,13 @@ bool thd_is_connection_alive(THD *thd)
}
-void do_handle_one_connection(CONNECT *connect)
+void do_handle_one_connection(CONNECT *connect, bool put_in_cache)
{
ulonglong thr_create_utime= microsecond_interval_timer();
THD *thd;
- if (connect->scheduler->init_new_connection_thread() ||
- !(thd= connect->create_thd(NULL)))
+ if (!(thd= connect->create_thd(NULL)))
{
- scheduler_functions *scheduler= connect->scheduler;
- connect->close_with_error(0, 0, ER_OUT_OF_RESOURCES);
- scheduler->end_thread(0, 0);
+ connect->close_and_delete();
return;
}
@@ -1391,8 +1390,7 @@ void do_handle_one_connection(CONNECT *connect)
stack overruns.
*/
thd->thread_stack= (char*) &thd;
- if (setup_connection_thread_globals(thd))
- return;
+ setup_connection_thread_globals(thd);
for (;;)
{
@@ -1420,16 +1418,37 @@ end_thread:
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
- if (thd->scheduler->end_thread(thd, 1))
- return; // Probably no-threads
+ unlink_thd(thd);
+ if (IF_WSREP(thd->wsrep_applier, false) || !put_in_cache ||
+ !(connect= thread_cache.park()))
+ break;
+
+ /* Create new instrumentation for the new THD job */
+ PSI_CALL_set_thread(PSI_CALL_new_thread(key_thread_one_connection, thd,
+ thd->thread_id));
+
+ if (!(connect->create_thd(thd)))
+ {
+ /* Out of resources. Free thread to get more resources */
+ connect->close_and_delete();
+ break;
+ }
+ delete connect;
/*
- If end_thread() returns, this thread has been schedule to
- handle the next connection.
+ We have to call store_globals to update mysys_var->id and lock_info
+ with the new thread_id
*/
- thd= current_thd;
- thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+
+ /* reset abort flag for the thread */
+ thd->mysys_var->abort= 0;
+ thd->thr_create_utime= microsecond_interval_timer();
+ thd->start_utime= thd->thr_create_utime;
+
+ server_threads.insert(thd);
}
+ delete thd;
}
#endif /* EMBEDDED_LIBRARY */
@@ -1446,10 +1465,16 @@ void CONNECT::close_and_delete()
{
DBUG_ENTER("close_and_delete");
- if (vio)
- vio_close(vio);
- if (thread_count_incremented)
- dec_connection_count(scheduler);
+#if _WIN32
+ if (vio_type == VIO_TYPE_NAMEDPIPE)
+ CloseHandle(pipe);
+ else
+#endif
+ if (vio_type != VIO_CLOSED)
+ mysql_socket_close(sock);
+ vio_type= VIO_CLOSED;
+
+ --*scheduler->connection_count;
statistic_increment(connection_errors_internal, &LOCK_status);
statistic_increment(aborted_connects,&LOCK_status);
@@ -1469,7 +1494,7 @@ void CONNECT::close_with_error(uint sql_errno,
if (thd)
{
if (sql_errno)
- net_send_error(thd, sql_errno, message, NULL);
+ thd->protocol->net_send_error(thd, sql_errno, message, NULL);
close_connection(thd, close_error);
delete thd;
set_current_thd(0);
@@ -1478,18 +1503,12 @@ void CONNECT::close_with_error(uint sql_errno,
}
-CONNECT::~CONNECT()
-{
- if (vio)
- vio_delete(vio);
-}
-
-
/* Reuse or create a THD based on a CONNECT object */
THD *CONNECT::create_thd(THD *thd)
{
bool res, thd_reused= thd != 0;
+ Vio *vio;
DBUG_ENTER("create_thd");
DBUG_EXECUTE_IF("simulate_failed_connection_2", DBUG_RETURN(0); );
@@ -1508,9 +1527,23 @@ THD *CONNECT::create_thd(THD *thd)
else if (!(thd= new THD(thread_id)))
DBUG_RETURN(0);
+#if _WIN32
+ if (vio_type == VIO_TYPE_NAMEDPIPE)
+ vio= vio_new_win32pipe(pipe);
+ else
+#endif
+ vio= mysql_socket_vio_new(sock, vio_type, vio_type == VIO_TYPE_SOCKET ?
+ VIO_LOCALHOST : 0);
+ if (!vio)
+ {
+ if (!thd_reused)
+ delete thd;
+ DBUG_RETURN(0);
+ }
+
set_current_thd(thd);
res= my_net_init(&thd->net, vio, thd, MYF(MY_THREAD_SPECIFIC));
- vio= 0; // Vio now handled by thd
+ vio_type= VIO_CLOSED; // Vio now handled by thd
if (unlikely(res || thd->is_error()))
{
@@ -1522,9 +1555,20 @@ THD *CONNECT::create_thd(THD *thd)
init_net_server_extension(thd);
- thd->security_ctx->host= host;
- thd->extra_port= extra_port;
+ thd->security_ctx->host= thd->net.vio->type == VIO_TYPE_NAMEDPIPE ||
+ thd->net.vio->type == VIO_TYPE_SOCKET ?
+ my_localhost : 0;
+
thd->scheduler= scheduler;
- thd->real_id= real_id;
+ thd->real_id= pthread_self(); /* Duplicates THD::store_globals() setting. */
+
+ /* Attach PSI instrumentation to the new THD */
+
+ PSI_thread *psi= PSI_CALL_get_thread();
+ PSI_CALL_set_thread_os_id(psi);
+ PSI_CALL_set_thread_THD(psi, thd);
+ PSI_CALL_set_thread_id(psi, thd->thread_id);
+ thd->set_psi(psi);
+
DBUG_RETURN(thd);
}
diff --git a/sql/sql_connect.h b/sql/sql_connect.h
index 82ab4423b37..152bd71afd7 100644
--- a/sql/sql_connect.h
+++ b/sql/sql_connect.h
@@ -21,6 +21,7 @@
#include "structs.h"
#include <mysql/psi/mysql_socket.h>
#include <hash.h>
+#include "violite.h"
/*
Object to hold connect information to be given to the newly created thread
@@ -30,25 +31,24 @@ struct scheduler_functions;
class CONNECT : public ilink {
public:
- /* To be copied to THD */
- Vio *vio; /* Copied to THD with my_net_init() */
- const char *host;
+ MYSQL_SOCKET sock;
+#ifdef _WIN32
+ HANDLE pipe;
+ CONNECT(HANDLE pipe_arg): pipe(pipe_arg), vio_type(VIO_TYPE_NAMEDPIPE),
+ scheduler(thread_scheduler), thread_id(0), prior_thr_create_utime(0) {}
+#endif
+ enum enum_vio_type vio_type;
scheduler_functions *scheduler;
my_thread_id thread_id;
- pthread_t real_id;
- bool extra_port;
/* Own variables */
- bool thread_count_incremented;
ulonglong prior_thr_create_utime;
- CONNECT()
- :vio(0), host(0), scheduler(thread_scheduler), thread_id(0), real_id(0),
- extra_port(0),
- thread_count_incremented(0), prior_thr_create_utime(0)
- {
- };
- ~CONNECT();
+ CONNECT(MYSQL_SOCKET sock_arg, enum enum_vio_type vio_type_arg,
+ scheduler_functions *scheduler_arg): sock(sock_arg),
+ vio_type(vio_type_arg), scheduler(scheduler_arg), thread_id(0),
+ prior_thr_create_utime(0) {}
+ ~CONNECT() { DBUG_ASSERT(vio_type == VIO_CLOSED); }
void close_and_delete();
void close_with_error(uint sql_errno,
const char *message, uint close_error);
@@ -71,7 +71,7 @@ void free_global_index_stats(void);
void free_global_client_stats(void);
pthread_handler_t handle_one_connection(void *arg);
-void do_handle_one_connection(CONNECT *connect);
+void do_handle_one_connection(CONNECT *connect, bool put_in_cache);
bool init_new_connection_handler_thread();
void reset_mqh(LEX_USER *lu, bool get_them);
bool check_mqh(THD *thd, uint check_command);
@@ -82,7 +82,7 @@ void decrease_user_connections(USER_CONN *uc);
#define decrease_user_connections(X) do { } while(0) /* nothing */
#endif
bool thd_init_client_charset(THD *thd, uint cs_number);
-bool setup_connection_thread_globals(THD *thd);
+void setup_connection_thread_globals(THD *thd);
bool thd_prepare_connection(THD *thd);
bool thd_is_connection_alive(THD *thd);
int thd_set_peer_addr(THD *thd, sockaddr_storage *addr,
diff --git a/sql/sql_const.h b/sql/sql_const.h
index f7c820c727b..3f053a1606d 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -204,11 +204,12 @@
instead of reading with keys. The number says how many evaluation of the
WHERE clause is comparable to reading one extra row from a table.
*/
-#define TIME_FOR_COMPARE 5 // 5 compares == one read
-#define TIME_FOR_COMPARE_IDX 20
+#define TIME_FOR_COMPARE 5.0 // 5 WHERE compares == one read
+#define TIME_FOR_COMPARE_IDX 20.0
#define IDX_BLOCK_COPY_COST ((double) 1 / TIME_FOR_COMPARE)
#define IDX_LOOKUP_COST ((double) 1 / 8)
+#define MULTI_RANGE_READ_SETUP_COST (IDX_BLOCK_COPY_COST/10)
/**
Number of comparisons of table rowids equivalent to reading one row from a
@@ -309,4 +310,6 @@
#define QUERY_PRIOR 6
#endif /* __WIN92__ */
+#define SP_PSI_STATEMENT_INFO_COUNT 19
+
#endif /* SQL_CONST_INCLUDED */
diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc
index d4b6d815118..5bf9930ef21 100644
--- a/sql/sql_cte.cc
+++ b/sql/sql_cte.cc
@@ -21,6 +21,7 @@
#include "sql_view.h" // for make_valid_column_names
#include "sql_parse.h"
#include "sql_select.h"
+#include "sql_show.h" // append_definer, append_identifier
/**
@@ -833,6 +834,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd,
st_select_lex_unit *res= NULL;
Query_arena backup;
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+ bool has_tmp_tables;
if (!(lex= (LEX*) new(thd->mem_root) st_lex_local))
{
@@ -878,11 +880,12 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd,
spec_tables= lex->query_tables;
spec_tables_tail= 0;
+ has_tmp_tables= thd->has_temporary_tables();
for (TABLE_LIST *tbl= spec_tables;
tbl;
tbl= tbl->next_global)
{
- if (!tbl->derived && !tbl->schema_table &&
+ if (has_tmp_tables && !tbl->derived && !tbl->schema_table &&
thd->open_temporary_table(tbl))
goto err;
spec_tables_tail= tbl;
@@ -944,9 +947,9 @@ err:
false otherwise
*/
-bool
-With_element::rename_columns_of_derived_unit(THD *thd,
- st_select_lex_unit *unit)
+bool
+With_element::process_columns_of_derived_unit(THD *thd,
+ st_select_lex_unit *unit)
{
if (unit->columns_are_renamed)
return false;
@@ -956,7 +959,7 @@ With_element::rename_columns_of_derived_unit(THD *thd,
if (column_list.elements) // The column list is optional
{
List_iterator_fast<Item> it(select->item_list);
- List_iterator_fast<LEX_CSTRING> nm(column_list);
+ List_iterator_fast<Lex_ident_sys> nm(column_list);
Item *item;
LEX_CSTRING *name;
@@ -972,8 +975,8 @@ With_element::rename_columns_of_derived_unit(THD *thd,
/* Rename the columns of the first select in the unit */
while ((item= it++, name= nm++))
{
- item->set_name(thd, name->str, (uint) name->length, system_charset_info);
- item->is_autogenerated_name= false;
+ item->set_name(thd, *name);
+ item->common_flags&= ~IS_AUTO_GENERATED_NAME;
}
if (arena)
@@ -982,6 +985,43 @@ With_element::rename_columns_of_derived_unit(THD *thd,
else
make_valid_column_names(thd, select->item_list);
+ if (cycle_list)
+ {
+ List_iterator_fast<Item> it(select->item_list);
+ List_iterator_fast<Lex_ident_sys> nm(*cycle_list);
+ List_iterator_fast<Lex_ident_sys> nm_check(*cycle_list);
+ DBUG_ASSERT(cycle_list->elements != 0);
+ while (LEX_CSTRING *name= nm++)
+ {
+ Item *item;
+ /*
+ Check for uniqueness of each element in the cycle list:
+ It's sufficient to check that there is no duplicate of 'name'
+ among the elements that precede it.
+ */
+ LEX_CSTRING *check;
+ nm_check.rewind();
+ while ((check= nm_check++) && check != name)
+ {
+ if (check->length == name->length &&
+ strncmp(check->str, name->str, name->length) == 0)
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), check->str);
+ return true;
+ }
+ }
+ /* Check that 'name' is the name of a column of the processed CTE */
+ while ((item= it++) &&
+ (item->name.length != name->length ||
+ strncmp(item->name.str, name->str, name->length) != 0));
+ if (item == NULL)
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), name->str, "CYCLE clause");
+ return true;
+ }
+ item->common_flags|= IS_IN_WITH_CYCLE;
+ }
+ }
unit->columns_are_renamed= true;
return false;
@@ -1018,7 +1058,7 @@ bool With_element::prepare_unreferenced(THD *thd)
thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
if (!spec->prepared &&
(spec->prepare(spec->derived, 0, 0) ||
- rename_columns_of_derived_unit(thd, spec) ||
+ process_columns_of_derived_unit(thd, spec) ||
check_duplicate_names(thd, first_sl->item_list, 1)))
rc= true;
@@ -1395,16 +1435,17 @@ bool st_select_lex::check_subqueries_with_recursive_references()
/**
@brief
Print this with clause
-
+
+ @param thd Thread handle
@param str Where to print to
- @param query_type The mode of printing
-
+ @param query_type The mode of printing
+
@details
- The method prints a string representation of this clause in the
+ The method prints a string representation of this clause in the
string str. The parameter query_type specifies the mode of printing.
-*/
+*/
-void With_clause::print(String *str, enum_query_type query_type)
+void With_clause::print(THD *thd, String *str, enum_query_type query_type)
{
/*
Any with clause contains just definitions of CTE tables.
@@ -1421,7 +1462,22 @@ void With_clause::print(String *str, enum_query_type query_type)
{
if (with_elem != with_list.first)
str->append(", ");
- with_elem->print(str, query_type);
+ with_elem->print(thd, str, query_type);
+ }
+}
+
+
+static void list_strlex_print(THD *thd, String *str, List<Lex_ident_sys> *list)
+{
+ List_iterator_fast<Lex_ident_sys> li(*list);
+ bool first= TRUE;
+ while(Lex_ident_sys *col_name= li++)
+ {
+ if (first)
+ first= FALSE;
+ else
+ str->append(',');
+ append_identifier(thd, str, col_name);
}
}
@@ -1429,38 +1485,37 @@ void With_clause::print(String *str, enum_query_type query_type)
/**
@brief
Print this with element
-
+
+ @param thd Thread handle
@param str Where to print to
- @param query_type The mode of printing
-
+ @param query_type The mode of printing
+
@details
- The method prints a string representation of this with element in the
+ The method prints a string representation of this with element in the
string str. The parameter query_type specifies the mode of printing.
*/
-void With_element::print(String *str, enum_query_type query_type)
+void With_element::print(THD *thd, String *str, enum_query_type query_type)
{
str->append(query_name);
if (column_list.elements)
{
- List_iterator_fast<LEX_CSTRING> li(column_list);
+ List_iterator_fast<Lex_ident_sys> li(column_list);
str->append('(');
- for (LEX_CSTRING *col_name= li++; ; )
- {
- str->append(col_name);
- col_name= li++;
- if (!col_name)
- {
- str->append(')');
- break;
- }
- str->append(',');
- }
+ list_strlex_print(thd, str, &column_list);
+ str->append(')');
}
- str->append(STRING_WITH_LEN(" as "));
- str->append('(');
+ str->append(STRING_WITH_LEN(" as ("));
spec->print(str, query_type);
str->append(')');
+
+ if (cycle_list)
+ {
+ DBUG_ASSERT(cycle_list->elements != 0);
+ str->append(STRING_WITH_LEN(" CYCLE "));
+ list_strlex_print(thd, str, cycle_list);
+ str->append(STRING_WITH_LEN(" RESTRICT "));
+ }
}
@@ -1484,3 +1539,26 @@ bool With_element::instantiate_tmp_tables()
return false;
}
+void With_element::set_cycle_list(List<Lex_ident_sys> *cycle_list_arg)
+{
+ cycle_list= cycle_list_arg;
+
+ /*
+ If a CTE table with columns c1,...,cn is defined with a cycle
+ clause CYCLE(ci1,...,cik) then no two rows r1 and r2 from the
+ table shall have r1.ci1=r2.ci1 && ... && r1.cik=r2.cik.
+
+ If a cycle clause is used in the specification of a CTE then
+ each UNION ALL at the top level of the specification is interpreted
+ as a UNION DISTINCT over the cycle columns.
+ */
+ for (st_select_lex *sl= spec->first_select(); sl; sl= sl->next_select())
+ {
+ spec->union_distinct= sl;
+ if (sl != spec->first_select())
+ {
+ sl->distinct= TRUE;
+ sl->with_all_modifier= FALSE;
+ }
+ }
+}
diff --git a/sql/sql_cte.h b/sql/sql_cte.h
index 80d56644d7e..4c42dd23614 100644
--- a/sql/sql_cte.h
+++ b/sql/sql_cte.h
@@ -111,7 +111,8 @@ public:
inherited from the query that specified the table. Otherwise the list is
always empty.
*/
- List <LEX_CSTRING> column_list;
+ List <Lex_ident_sys> column_list;
+ List <Lex_ident_sys> *cycle_list;
/* The query that specifies the table introduced by this with element */
st_select_lex_unit *spec;
/*
@@ -163,13 +164,13 @@ public:
SQL_I_List<TABLE_LIST> derived_with_rec_ref;
With_element(LEX_CSTRING *name,
- List <LEX_CSTRING> list,
+ List <Lex_ident_sys> list,
st_select_lex_unit *unit)
: next(NULL), base_dep_map(0), derived_dep_map(0),
sq_dep_map(0), work_dep_map(0), mutually_recursive(0),
top_level_dep_map(0), sq_rec_ref(NULL),
next_mutually_recursive(NULL), references(0),
- query_name(name), column_list(list), spec(unit),
+ query_name(name), column_list(list), cycle_list(0), spec(unit),
is_recursive(false), rec_outer_references(0), with_anchor(false),
level(0), rec_result(NULL)
{ unit->with_element= this; }
@@ -206,7 +207,7 @@ public:
void inc_references() { references++; }
- bool rename_columns_of_derived_unit(THD *thd, st_select_lex_unit *unit);
+ bool process_columns_of_derived_unit(THD *thd, st_select_lex_unit *unit);
bool prepare_unreferenced(THD *thd);
@@ -214,7 +215,7 @@ public:
table_map &unrestricted,
table_map &encountered);
- void print(String *str, enum_query_type query_type);
+ void print(THD *thd, String *str, enum_query_type query_type);
With_clause *get_owner() { return owner; }
@@ -259,6 +260,8 @@ public:
void prepare_for_next_iteration();
+ void set_cycle_list(List<Lex_ident_sys> *cycle_list_arg);
+
friend class With_clause;
};
@@ -353,7 +356,7 @@ public:
void add_unrestricted(table_map map) { unrestricted|= map; }
- void print(String *str, enum_query_type query_type);
+ void print(THD *thd, String *str, enum_query_type query_type);
friend class With_element;
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 8f41fe7c70d..b995a841a74 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -284,12 +284,11 @@ int Materialized_cursor::send_result_set_metadata(
*/
while ((item_dst= it_dst++, item_org= it_org++))
{
- Send_field send_field;
Item_ident *ident= static_cast<Item_ident *>(item_dst);
- item_org->make_send_field(thd, &send_field);
+ Send_field send_field(thd, item_org);
- ident->db_name= thd->strdup(send_field.db_name);
- ident->table_name= thd->strdup(send_field.table_name);
+ ident->db_name= thd->strmake_lex_cstring(send_field.db_name);
+ ident->table_name= thd->strmake_lex_cstring(send_field.table_name);
}
/*
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index b2b591464f7..3447032f193 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -26,11 +26,12 @@
#include "lock.h" // lock_schema_name
#include "sql_table.h" // build_table_filename,
// filename_to_tablename
+ // validate_comment_length
#include "sql_rename.h" // mysql_rename_tables
#include "sql_acl.h" // SELECT_ACL, DB_ACLS,
// acl_get, check_grant_db
#include "log_event.h" // Query_log_event
-#include "sql_base.h" // lock_table_names, tdc_remove_table
+#include "sql_base.h" // lock_table_names
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sql_class.h"
#include <mysys_err.h>
@@ -60,7 +61,7 @@ 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);
static void mysql_change_db_impl(THD *thd,
LEX_CSTRING *new_db_name,
- ulong new_db_access,
+ privilege_t new_db_access,
CHARSET_INFO *new_db_charset);
static bool mysql_rm_db_internal(THD *thd, const LEX_CSTRING *db,
bool if_exists, bool silent);
@@ -77,6 +78,7 @@ typedef struct my_dbopt_st
char *name; /* Database name */
uint name_length; /* Database length name */
CHARSET_INFO *charset; /* Database default character set */
+ LEX_STRING comment; /* Database comment */
} my_dbopt_t;
@@ -184,9 +186,9 @@ bool my_dboptions_cache_init(void)
if (!dboptions_init)
{
dboptions_init= 1;
- error= my_hash_init(&dboptions, table_alias_charset,
- 32, 0, 0, (my_hash_get_key) dboptions_get_key,
- free_dbopt,0);
+ error= my_hash_init(key_memory_dboptions_hash, &dboptions,
+ table_alias_charset, 32, 0, 0, (my_hash_get_key)
+ dboptions_get_key, free_dbopt, 0);
}
return error;
}
@@ -216,9 +218,8 @@ void my_dbopt_cleanup(void)
{
mysql_rwlock_wrlock(&LOCK_dboptions);
my_hash_free(&dboptions);
- my_hash_init(&dboptions, table_alias_charset,
- 32, 0, 0, (my_hash_get_key) dboptions_get_key,
- free_dbopt,0);
+ my_hash_init(key_memory_dboptions_hash, &dboptions, table_alias_charset, 32,
+ 0, 0, (my_hash_get_key) dboptions_get_key, free_dbopt, 0);
mysql_rwlock_unlock(&LOCK_dboptions);
}
@@ -235,7 +236,8 @@ void my_dbopt_cleanup(void)
1 on error.
*/
-static my_bool get_dbopt(const char *dbname, Schema_specification_st *create)
+static my_bool get_dbopt(THD *thd, const char *dbname,
+ Schema_specification_st *create)
{
my_dbopt_t *opt;
uint length;
@@ -247,6 +249,11 @@ static my_bool get_dbopt(const char *dbname, Schema_specification_st *create)
if ((opt= (my_dbopt_t*) my_hash_search(&dboptions, (uchar*) dbname, length)))
{
create->default_table_charset= opt->charset;
+ if (opt->comment.length)
+ {
+ create->schema_comment= thd->make_clex_string(opt->comment.str,
+ opt->comment.length);
+ }
error= 0;
}
mysql_rwlock_unlock(&LOCK_dboptions);
@@ -274,15 +281,17 @@ static my_bool put_dbopt(const char *dbname, Schema_specification_st *create)
DBUG_ENTER("put_dbopt");
length= (uint) strlen(dbname);
-
+
mysql_rwlock_wrlock(&LOCK_dboptions);
if (!(opt= (my_dbopt_t*) my_hash_search(&dboptions, (uchar*) dbname,
length)))
{
/* Options are not in the hash, insert them */
char *tmp_name;
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ char *tmp_comment= NULL;
+ if (!my_multi_malloc(key_memory_dboptions_hash, MYF(MY_WME | MY_ZEROFILL),
&opt, (uint) sizeof(*opt), &tmp_name, (uint) length+1,
+ &tmp_comment, (uint) DATABASE_COMMENT_MAXLEN+1,
NullS))
{
error= 1;
@@ -292,7 +301,7 @@ static my_bool put_dbopt(const char *dbname, Schema_specification_st *create)
opt->name= tmp_name;
strmov(opt->name, dbname);
opt->name_length= length;
-
+ opt->comment.str= tmp_comment;
if (unlikely((error= my_hash_insert(&dboptions, (uchar*) opt))))
{
my_free(opt);
@@ -303,6 +312,12 @@ static my_bool put_dbopt(const char *dbname, Schema_specification_st *create)
/* Update / write options in hash */
opt->charset= create->default_table_charset;
+ if (create->schema_comment)
+ {
+ strmov(opt->comment.str, create->schema_comment->str);
+ opt->comment.length= create->schema_comment->length;
+ }
+
end:
mysql_rwlock_unlock(&LOCK_dboptions);
DBUG_RETURN(error);
@@ -328,7 +343,8 @@ static void del_dbopt(const char *path)
Create database options file:
DESCRIPTION
- Currently database default charset is only stored there.
+ Currently database default charset, default collation
+ and comment are stored there.
RETURN VALUES
0 ok
@@ -339,9 +355,34 @@ static bool write_db_opt(THD *thd, const char *path,
Schema_specification_st *create)
{
File file;
- char buf[256]; // Should be enough for one option
+ char buf[256+DATABASE_COMMENT_MAXLEN];
bool error=1;
+ if (create->schema_comment)
+ {
+ if (validate_comment_length(thd, create->schema_comment,
+ DATABASE_COMMENT_MAXLEN,
+ ER_TOO_LONG_DATABASE_COMMENT,
+ thd->lex->name.str))
+ return error;
+ }
+
+ if (thd->lex->sql_command == SQLCOM_ALTER_DB &&
+ (!create->schema_comment || !create->default_table_charset))
+ {
+ /* Use existing values of schema_comment and charset for
+ ALTER DATABASE queries */
+ Schema_specification_st tmp;
+ tmp.init();
+ load_db_opt(thd, path, &tmp);
+
+ if (!create->schema_comment)
+ create->schema_comment= tmp.schema_comment;
+
+ if (!create->default_table_charset)
+ create->default_table_charset= tmp.default_table_charset;
+ }
+
if (!create->default_table_charset)
create->default_table_charset= thd->variables.collation_server;
@@ -358,6 +399,11 @@ static bool write_db_opt(THD *thd, const char *path,
create->default_table_charset->name,
"\n", NullS) - buf);
+ if (create->schema_comment)
+ length= (ulong) (strxnmov(buf+length, sizeof(buf)-1-length,
+ "comment=", create->schema_comment->str,
+ "\n", NullS) - buf);
+
/* Error is written by mysql_file_write */
if (!mysql_file_write(file, (uchar*) buf, length, MYF(MY_NABP+MY_WME)))
error=0;
@@ -385,7 +431,7 @@ static bool write_db_opt(THD *thd, const char *path,
bool load_db_opt(THD *thd, const char *path, Schema_specification_st *create)
{
File file;
- char buf[256];
+ char buf[256+DATABASE_COMMENT_MAXLEN];
DBUG_ENTER("load_db_opt");
bool error=1;
size_t nbytes;
@@ -394,7 +440,7 @@ bool load_db_opt(THD *thd, const char *path, Schema_specification_st *create)
create->default_table_charset= thd->variables.collation_server;
/* Check if options for this database are already in the hash */
- if (!get_dbopt(path, create))
+ if (!get_dbopt(thd, path, create))
DBUG_RETURN(0);
/* Otherwise, load options from the .opt file */
@@ -444,6 +490,8 @@ bool load_db_opt(THD *thd, const char *path, Schema_specification_st *create)
create->default_table_charset= default_charset_info;
}
}
+ else if (!strncmp(buf, "comment", (pos-buf)))
+ create->schema_comment= thd->make_clex_string(pos+1, strlen(pos+1));
}
}
/*
@@ -544,7 +592,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
Create a database
SYNOPSIS
- mysql_create_db_iternal()
+ mysql_create_db_internal()
thd Thread handler
db Name of database to create
Function assumes that this is already validated.
@@ -1039,8 +1087,8 @@ exit:
*/
if (unlikely(thd->db.str && cmp_db_names(&thd->db, db) && !error))
{
- mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
- SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL);
+ mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
+ thd->session_tracker.current_schema.mark_as_changed(thd);
}
my_dirend(dirp);
DBUG_RETURN(error);
@@ -1099,9 +1147,9 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
(char*) table_list->table_name.str);
table_list->alias= table_list->table_name; // If lower_case_table_names=2
- table_list->mdl_request.init(MDL_key::TABLE, table_list->db.str,
- table_list->table_name.str, MDL_EXCLUSIVE,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table_list->mdl_request, MDL_key::TABLE,
+ table_list->db.str, table_list->table_name.str,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
/* Link into list */
(*tot_list_next_local)= table_list;
(*tot_list_next_global)= table_list;
@@ -1303,7 +1351,7 @@ err:
static void mysql_change_db_impl(THD *thd,
LEX_CSTRING *new_db_name,
- ulong new_db_access,
+ privilege_t new_db_access,
CHARSET_INFO *new_db_charset)
{
/* 1. Change current database in THD. */
@@ -1452,7 +1500,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
LEX_CSTRING new_db_file_name;
Security_context *sctx= thd->security_ctx;
- ulong db_access= sctx->db_access;
+ privilege_t db_access(sctx->db_access);
CHARSET_INFO *db_default_cl;
DBUG_ENTER("mysql_change_db");
@@ -1469,7 +1517,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
new_db_name->length == 0.
*/
- mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
goto done;
}
@@ -1498,8 +1546,8 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
TODO: fix check_db_name().
*/
- new_db_file_name.str= my_strndup(new_db_name->str, new_db_name->length,
- MYF(MY_WME));
+ new_db_file_name.str= my_strndup(key_memory_THD_db, new_db_name->str,
+ new_db_name->length, MYF(MY_WME));
new_db_file_name.length= new_db_name->length;
if (new_db_file_name.str == NULL)
@@ -1521,7 +1569,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
my_free(const_cast<char*>(new_db_file_name.str));
if (force_switch)
- mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
DBUG_RETURN(ER_WRONG_DB_NAME);
}
@@ -1573,7 +1621,7 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
/* Change db to NULL. */
- mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
+ mysql_change_db_impl(thd, NULL, NO_ACL, thd->variables.collation_server);
/* The operation succeed. */
goto done;
@@ -1601,8 +1649,8 @@ uint mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name,
mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
done:
- SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL);
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.current_schema.mark_as_changed(thd);
+ thd->session_tracker.state_change.mark_as_changed(thd);
DBUG_RETURN(0);
}
@@ -1757,7 +1805,7 @@ bool mysql_upgrade_db(THD *thd, const LEX_CSTRING *old_db)
}
if ((table_list= thd->lex->query_tables) &&
- (error= mysql_rename_tables(thd, table_list, 1)))
+ (error= mysql_rename_tables(thd, table_list, 1, 0)))
{
/*
Failed to move all tables from the old database to the new one.
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index a4ef698590c..7586b2831ae 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -30,7 +30,6 @@
#include "lock.h" // unlock_table_name
#include "sql_view.h" // check_key_in_view, mysql_frm_type
#include "sql_parse.h" // mysql_init_select
-#include "sql_acl.h" // *_ACL
#include "filesort.h" // filesort
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sql_select.h"
@@ -199,23 +198,12 @@ bool Update_plan::save_explain_data_intern(MEM_ROOT *mem_root,
&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))
+ if (unit->explainable())
explain->add_child(unit->first_select()->select_number);
}
return 0;
@@ -325,10 +313,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
order_list->first : NULL);
SELECT_LEX *select_lex= thd->lex->first_select_lex();
+ SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0;
killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
bool binlog_is_row;
- bool with_select= !select_lex->item_list.is_empty();
Explain_delete *explain;
Delete_plan query_plan(thd->mem_root);
Unique * deltempfile= NULL;
@@ -368,18 +356,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->map=1;
query_plan.select_lex= thd->lex->first_select_lex();
query_plan.table= table;
- query_plan.updating_a_view= MY_TEST(table_list->view);
- if (mysql_prepare_delete(thd, table_list, select_lex->with_wild,
- select_lex->item_list, &conds,
- &delete_while_scanning))
+ if (mysql_prepare_delete(thd, table_list, &conds, &delete_while_scanning))
DBUG_RETURN(TRUE);
if (delete_history)
table->vers_write= false;
- if (with_select)
- (void) result->prepare(select_lex->item_list, NULL);
+ if (returning)
+ (void) result->prepare(returning->item_list, NULL);
if (thd->lex->current_select->first_cond_optimization)
{
@@ -397,8 +382,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
tables.table = table;
tables.alias = table_list->alias;
- if (select_lex->setup_ref_array(thd, order_list->elements) ||
- setup_order(thd, select_lex->ref_pointer_array, &tables,
+ if (select_lex->setup_ref_array(thd, order_list->elements) ||
+ setup_order(thd, select_lex->ref_pointer_array, &tables,
fields, all_fields, order))
{
free_underlaid_joins(thd, thd->lex->first_select_lex());
@@ -444,9 +429,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
has_triggers= table->triggers && table->triggers->has_delete_triggers();
- if (!with_select && !using_limit && const_cond_result &&
- (!thd->is_current_stmt_binlog_format_row() &&
- !has_triggers)
+ if (!returning && !using_limit && const_cond_result &&
+ (!thd->is_current_stmt_binlog_format_row() && !has_triggers)
&& !table->versioned(VERS_TIMESTAMP) && !table_list->has_period())
{
/* Update the table->file->stats.records number */
@@ -511,7 +495,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
set_statistics_for_table(thd, table);
table->covering_keys.clear_all();
- table->quick_keys.clear_all(); // Can't use 'only index'
+ table->opt_range_keys.clear_all();
select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error);
if (unlikely(error))
@@ -537,7 +521,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())
+ if (table->opt_range_keys.is_clear_all())
{
thd->set_status_no_index_used();
if (safe_update && !using_limit)
@@ -566,10 +550,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
else
{
ha_rows scanned_limit= query_plan.scanned_rows;
+ table->no_keyread= 1;
query_plan.index= get_index_for_order(order, table, select, limit,
&scanned_limit,
&query_plan.using_filesort,
&reverse);
+ table->no_keyread= 0;
if (!query_plan.using_filesort)
query_plan.scanned_rows= scanned_limit;
}
@@ -588,7 +574,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(explain= query_plan.save_explain_delete_data(thd->mem_root, thd)))
goto got_error;
- ANALYZE_START_TRACKING(&explain->command_tracker);
+ ANALYZE_START_TRACKING(thd, &explain->command_tracker);
DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start",
dbug_serve_apcs(thd, 1););
@@ -617,7 +603,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
*/
if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) &&
- !has_triggers && !binlog_is_row && !with_select &&
+ !has_triggers && !binlog_is_row && !returning &&
!table_list->has_period())
{
table->mark_columns_needed_for_delete();
@@ -667,7 +653,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DELETE ... RETURNING we can't, because the RETURNING part may have
a subquery in it)
*/
- if (!with_select)
+ if (!returning)
free_underlaid_joins(thd, select_lex);
select= 0;
}
@@ -702,11 +688,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
!table->prepare_triggers_for_delete_stmt_or_event())
will_batch= !table->file->start_bulk_delete();
- if (with_select)
+ if (returning)
{
- if (unlikely(result->send_result_set_metadata(select_lex->item_list,
- Protocol::SEND_NUM_ROWS |
- Protocol::SEND_EOF)))
+ if (result->send_result_set_metadata(returning->item_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
goto cleanup;
}
@@ -771,6 +756,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
&& !table->versioned()
&& table->file->has_transactions();
+ if (table->versioned(VERS_TIMESTAMP) || (table_list->has_period()))
+ table->file->prepare_for_insert(1);
+ DBUG_ASSERT(table->file->inited != handler::NONE);
+
THD_STAGE_INFO(thd, stage_updating);
while (likely(!(error=info.read_record())) && likely(!thd->killed) &&
likely(!thd->is_error()))
@@ -788,7 +777,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
break;
}
- if (with_select && result->send_data(select_lex->item_list) < 0)
+ // no LIMIT / OFFSET
+ if (returning && result->send_data(returning->item_list) < 0)
{
error=1;
break;
@@ -866,7 +856,7 @@ terminate_delete:
table->file->ha_release_auto_increment();
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
- ANALYZE_STOP_TRACKING(&explain->command_tracker);
+ ANALYZE_STOP_TRACKING(thd, &explain->command_tracker);
cleanup:
/*
@@ -888,14 +878,14 @@ cleanup:
deltempfile=NULL;
delete select;
select= NULL;
- transactional_table= table->file->has_transactions();
+ transactional_table= table->file->has_transactions_and_rollback();
if (!transactional_table && deleted > 0)
- thd->transaction.stmt.modified_non_trans_table=
- thd->transaction.all.modified_non_trans_table= TRUE;
+ thd->transaction->stmt.modified_non_trans_table=
+ thd->transaction->all.modified_non_trans_table= TRUE;
/* See similar binlogging code in sql_update.cc, for comments */
- if (likely((error < 0) || thd->transaction.stmt.modified_non_trans_table))
+ if (likely((error < 0) || thd->transaction->stmt.modified_non_trans_table))
{
if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
@@ -924,7 +914,7 @@ cleanup:
}
}
}
- DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
+ DBUG_ASSERT(transactional_table || !deleted || thd->transaction->stmt.modified_non_trans_table);
if (likely(error < 0) ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
@@ -932,7 +922,7 @@ cleanup:
if (thd->lex->analyze_stmt)
goto send_nothing_and_leave;
- if (with_select)
+ if (returning)
result->send_eof();
else
my_ok(thd, deleted);
@@ -982,16 +972,13 @@ got_error:
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,
- uint wild_num, List<Item> &field_list, Item **conds,
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds,
bool *delete_while_scanning)
{
Item *fake_conds= 0;
@@ -1001,12 +988,9 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
*delete_while_scanning= true;
thd->lex->allow_sum_func.clear_all();
- if (setup_tables_and_check_access(thd,
- &thd->lex->first_select_lex()->context,
- &thd->lex->first_select_lex()->
- top_join_list,
- table_list,
- select_lex->leaf_tables, FALSE,
+ if (setup_tables_and_check_access(thd, &select_lex->context,
+ &select_lex->top_join_list, table_list,
+ select_lex->leaf_tables, FALSE,
DELETE_ACL, SELECT_ACL, TRUE))
DBUG_RETURN(TRUE);
@@ -1037,10 +1021,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
*conds= select_lex->where;
- if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num,
- &select_lex->hidden_bit_fields)) ||
- setup_fields(thd, Ref_ptr_array(),
- field_list, MARK_COLUMNS_READ, NULL, NULL, 0) ||
+ if (setup_returning_fields(thd, table_list) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
setup_ftfuncs(select_lex))
DBUG_RETURN(TRUE);
@@ -1064,7 +1045,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
DBUG_RETURN(TRUE);
- select_lex->fix_prepare_information(thd, conds, &fake_conds);
+ select_lex->fix_prepare_information(thd, conds, &fake_conds);
DBUG_RETURN(FALSE);
}
@@ -1265,6 +1246,9 @@ multi_delete::initialize_tables(JOIN *join)
normal_tables= 1;
tbl->prepare_triggers_for_delete_stmt_or_event();
tbl->prepare_for_position();
+
+ if (tbl->versioned(VERS_TIMESTAMP))
+ tbl->file->prepare_for_insert(1);
}
else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
walk == delete_tables)
@@ -1351,7 +1335,7 @@ int multi_delete::send_data(List<Item> &values)
{
deleted++;
if (!table->file->has_transactions())
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
@@ -1387,17 +1371,17 @@ void multi_delete::abort_result_set()
/* the error was handled or nothing deleted and no side effects return */
if (error_handled ||
- (!thd->transaction.stmt.modified_non_trans_table && !deleted))
+ (!thd->transaction->stmt.modified_non_trans_table && !deleted))
DBUG_VOID_RETURN;
/* Something already deleted so we have to invalidate cache */
if (deleted)
query_cache_invalidate3(thd, delete_tables, 1);
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
/*
If rows from the first table only has been deleted and it is
@@ -1407,7 +1391,7 @@ void multi_delete::abort_result_set()
*/
if (do_delete && normal_tables &&
(table_being_deleted != delete_tables ||
- !table_being_deleted->table->file->has_transactions()))
+ !table_being_deleted->table->file->has_transactions_and_rollback()))
{
/*
We have to execute the recorded do_deletes() and write info into the
@@ -1419,7 +1403,7 @@ void multi_delete::abort_result_set()
DBUG_VOID_RETURN;
}
- if (thd->transaction.stmt.modified_non_trans_table)
+ if (thd->transaction->stmt.modified_non_trans_table)
{
/*
there is only side effects; to binlog with the error
@@ -1556,8 +1540,8 @@ int multi_delete::do_table_deletes(TABLE *table, SORT_INFO *sort_info,
table->file->print_error(local_error, MYF(0));
}
}
- if (last_deleted != deleted && !table->file->has_transactions())
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ if (last_deleted != deleted && !table->file->has_transactions_and_rollback())
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
end_read_record(&info);
@@ -1585,10 +1569,10 @@ bool multi_delete::send_eof()
/* reset used flags */
THD_STAGE_INFO(thd, stage_end);
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
/*
We must invalidate the query cache before binlog writing and
@@ -1599,7 +1583,7 @@ bool multi_delete::send_eof()
query_cache_invalidate3(thd, delete_tables, 1);
}
if (likely((local_error == 0) ||
- thd->transaction.stmt.modified_non_trans_table))
+ thd->transaction->stmt.modified_non_trans_table))
{
if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index 7af8564abf9..520524c72cc 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -26,8 +26,7 @@ class select_result;
typedef class Item COND;
template <typename T> class SQL_I_List;
-int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
- uint wild_num, List<Item> &field_list, Item **conds,
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds,
bool *delete_while_scanning);
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SQL_I_List<ORDER> *order, ha_rows rows,
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 3b312225937..428e7b1d261 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -816,8 +816,8 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
if ((res= unit->prepare(derived, derived->derived_result, 0)))
goto exit;
if (derived->with &&
- (res= derived->with->rename_columns_of_derived_unit(thd, unit)))
- goto exit;
+ (res= derived->with->process_columns_of_derived_unit(thd, unit)))
+ goto exit;
lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
if ((res= check_duplicate_names(thd, unit->types, 0)))
goto exit;
@@ -1248,13 +1248,12 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX *first_select= unit->first_select();
unit->set_limit(unit->global_parameters());
- if (unit->select_limit_cnt == HA_POS_ERROR)
+ if (unit->lim.is_unlimited())
first_select->options&= ~OPTION_FOUND_ROWS;
lex->current_select= first_select;
res= mysql_select(thd,
first_select->table_list.first,
- first_select->with_wild,
first_select->item_list, first_select->where,
(first_select->order_list.elements+
first_select->group_list.elements),
diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc
index 10a9547d80f..5ca855c9608 100644
--- a/sql/sql_digest.cc
+++ b/sql/sql_digest.cc
@@ -188,7 +188,7 @@ void compute_digest_text(const sql_digest_storage* digest_storage,
/* 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;
+ const CHARSET_INFO *to_cs= &my_charset_utf8mb3_bin;
if (from_cs == NULL)
{
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index a11a0f454a2..b3ef0d89a98 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -506,7 +506,7 @@ void Warning_info::init()
{
/* Initialize sub structures */
DBUG_ASSERT(initialized == 0);
- init_sql_alloc(&m_warn_root, "Warning_info", WARN_ALLOC_BLOCK_SIZE,
+ init_sql_alloc(PSI_INSTRUMENT_ME, &m_warn_root, WARN_ALLOC_BLOCK_SIZE,
WARN_ALLOC_PREALLOC_SIZE, MYF(MY_THREAD_SPECIFIC));
initialized= 1;
}
@@ -744,7 +744,7 @@ void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
DBUG_ASSERT(format != NULL);
va_start(args,format);
- my_vsnprintf_ex(&my_charset_utf8_general_ci, warning,
+ my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, warning,
sizeof(warning), format, args);
va_end(args);
push_warning(thd, level, code, warning);
@@ -783,7 +783,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
const Sql_condition *err;
SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ulonglong idx= 0;
+ ha_rows idx;
Protocol *protocol=thd->protocol;
DBUG_ENTER("mysqld_show_warnings");
@@ -808,23 +808,22 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
- while ((err= it++))
+ for (idx= 0; (err= it++) ; idx++)
{
/* Skip levels that the user is not interested in */
if (!(levels_to_show & ((ulong) 1 << err->get_level())))
continue;
- if (++idx <= unit->offset_limit_cnt)
- continue;
- if (idx > unit->select_limit_cnt)
+ if (unit->lim.check_offset(idx))
+ continue; // using limit offset,count
+ if (idx >= unit->lim.get_select_limit())
break;
protocol->prepare_for_resend();
protocol->store(warning_level_names[err->get_level()].str,
warning_level_names[err->get_level()].length,
system_charset_info);
protocol->store((uint32) err->get_sql_errno());
- protocol->store(err->get_message_text(),
- err->get_message_octet_length(),
- system_charset_info);
+ protocol->store_warning(err->get_message_text(),
+ err->get_message_octet_length());
if (protocol->write())
DBUG_RETURN(TRUE);
}
@@ -837,6 +836,26 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
/**
+ This replaces U+0000 to '\0000', so the result error message string:
+ - is a good null-terminated string
+ - presents the entire data
+ For example:
+ SELECT CAST(_latin1 0x610062 AS SIGNED);
+ returns a warning:
+ Truncated incorrect INTEGER value: 'a\0000b'
+ Notice, the 0x00 byte is replaced to a 5-byte long string '\0000',
+ while 'a' and 'b' are printed as is.
+*/
+extern "C" int my_wc_mb_utf8_null_terminated(CHARSET_INFO *cs,
+ my_wc_t wc, uchar *r, uchar *e)
+{
+ return wc == '\0' ?
+ cs->wc_to_printable(wc, r, e) :
+ my_charset_utf8mb3_handler.wc_mb(cs, wc, r, e);
+}
+
+
+/**
Convert value for dispatch to error message(see WL#751).
@param to buffer for converted string
@@ -894,8 +913,11 @@ char *err_conv(char *buff, uint to_length, const char *from,
else
{
uint errors;
- res= copy_and_convert(to, to_length, system_charset_info,
- from, from_length, from_cs, &errors);
+ res= my_convert_using_func(to, to_length, system_charset_info,
+ my_wc_mb_utf8_null_terminated,
+ from, from_length, from_cs,
+ from_cs->cset->mb_wc,
+ &errors);
to[res]= 0;
}
return buff;
@@ -921,64 +943,21 @@ size_t convert_error_message(char *to, size_t to_length, CHARSET_INFO *to_cs,
const char *from, size_t 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;
- my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
- my_charset_conv_wc_mb wc_mb;
- uint error_count= 0;
- size_t length;
-
DBUG_ASSERT(to_length > 0);
/* Make room for the null terminator. */
to_length--;
- to_end= (uchar*) (to + to_length);
-
- if (!to_cs || from_cs == to_cs || to_cs == &my_charset_bin)
- {
- length= MY_MIN(to_length, from_length);
- memmove(to, from, length);
- to[length]= 0;
- return length;
- }
- wc_mb= to_cs->cset->wc_mb;
- while (1)
- {
- if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from, from_end)) > 0)
- {
- if (!wc)
- break;
- from+= cnvres;
- }
- else if (cnvres == MY_CS_ILSEQ)
- {
- wc= (ulong) (uchar) *from;
- from+=1;
- }
- else
- break;
-
- if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
- to+= cnvres;
- else if (cnvres == MY_CS_ILUNI)
- {
- length= (wc <= 0xFFFF) ? 6/* '\1234' format*/ : 9 /* '\+123456' format*/;
- if ((uchar*)(to + length) >= to_end)
- break;
- cnvres= (int)my_snprintf(to, 9,
- (wc <= 0xFFFF) ? "\\%04X" : "\\+%06X", (uint) wc);
- to+= cnvres;
- }
- else
- break;
- }
-
- *to= 0;
- *errors= error_count;
- return (size_t) (to - to_start);
+ if (!to_cs || to_cs == &my_charset_bin)
+ to_cs= system_charset_info;
+ uint32 cnv_length= my_convert_using_func(to, to_length,
+ to_cs,
+ to_cs->cset->wc_to_printable,
+ from, from_length,
+ from_cs, from_cs->cset->mb_wc,
+ errors);
+ DBUG_ASSERT(to_length >= cnv_length);
+ to[cnv_length]= '\0';
+ return cnv_length;
}
@@ -1009,3 +988,13 @@ bool is_sqlstate_valid(const LEX_CSTRING *sqlstate)
return true;
}
+
+
+void convert_error_to_warning(THD *thd)
+{
+ DBUG_ASSERT(thd->is_error());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
+ thd->clear_error();
+}
diff --git a/sql/sql_error.h b/sql/sql_error.h
index bb83d8af800..a0497af78cb 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -307,16 +307,16 @@ protected:
String m_cursor_name;
Sql_condition_items()
- :m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
- m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
- m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8_bin),
- m_constraint_schema((const char*) NULL, 0, & my_charset_utf8_bin),
- m_constraint_name((const char*) NULL, 0, & my_charset_utf8_bin),
- m_catalog_name((const char*) NULL, 0, & my_charset_utf8_bin),
- m_schema_name((const char*) NULL, 0, & my_charset_utf8_bin),
- m_table_name((const char*) NULL, 0, & my_charset_utf8_bin),
- m_column_name((const char*) NULL, 0, & my_charset_utf8_bin),
- m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin)
+ :m_class_origin((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_subclass_origin((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_constraint_schema((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_constraint_name((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_catalog_name((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_schema_name((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_table_name((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_column_name((const char*) NULL, 0, & my_charset_utf8mb3_bin),
+ m_cursor_name((const char*) NULL, 0, & my_charset_utf8mb3_bin)
{ }
void clear()
@@ -1260,6 +1260,7 @@ private:
///////////////////////////////////////////////////////////////////////////
+void convert_error_to_warning(THD *thd);
void push_warning(THD *thd, Sql_condition::enum_warning_level level,
uint code, const char *msg);
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 645c1618a8a..353217982e2 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -1811,10 +1811,11 @@ void Explain_table_access::print_explain_json(Explain_query *query,
double total_time= op_tracker.get_time_ms();
if (rowid_filter)
total_time+= rowid_filter->tracker->get_time_fill_container_ms();
- writer->add_member("r_total_time_ms").add_double(total_time);
+ writer->add_member("r_table_time_ms").add_double(total_time);
+ writer->add_member("r_other_time_ms").add_double(extra_time_tracker.get_time_ms());
}
}
-
+
/* `filtered` */
if (filtered_set)
writer->add_member("filtered").add_double(filtered);
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index ce3f3ef06e1..9090416847f 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -344,7 +344,7 @@ class Explain_union : public Explain_node
{
public:
Explain_union(MEM_ROOT *root, bool is_analyze) :
- Explain_node(root),
+ Explain_node(root), union_members(PSI_INSTRUMENT_MEM),
is_recursive_cte(false),
fake_select_lex_explain(root, is_analyze)
{}
@@ -837,6 +837,8 @@ public:
/* Tracker for reading the table */
Table_access_tracker tracker;
Exec_time_tracker op_tracker;
+ Gap_time_tracker extra_time_tracker;
+
Table_access_tracker jbuf_tracker;
Explain_rowid_filter *rowid_filter;
diff --git a/sql/sql_expression_cache.h b/sql/sql_expression_cache.h
index 61e0c4c69b3..031773adb9f 100644
--- a/sql/sql_expression_cache.h
+++ b/sql/sql_expression_cache.h
@@ -152,7 +152,7 @@ private:
Item *val;
/* hit/miss counters */
ulong hit, miss;
- /* Set on if the object has been succesfully initialized with init() */
+ /* Set on if the object has been successfully initialized with init() */
bool inited;
};
diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc
index b3ae423b914..197bf5e7a00 100644
--- a/sql/sql_get_diagnostics.cc
+++ b/sql/sql_get_diagnostics.cc
@@ -266,8 +266,8 @@ Condition_information::aggregate(THD *thd, const Diagnostics_area *da)
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;
+ /* Default is utf8 character set and utf8mb3_general_ci collation. */
+ CHARSET_INFO *to_cs= &my_charset_utf8mb3_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);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 12119997430..a6f93f6dfee 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -289,10 +289,11 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
/*
HASH entries are of type SQL_HANDLER
*/
- if (my_hash_init(&thd->handler_tables_hash, &my_charset_latin1,
- HANDLER_TABLES_HASH_SIZE, 0, 0,
- (my_hash_get_key) mysql_ha_hash_get_key,
- (my_hash_free_key) mysql_ha_hash_free, 0))
+ if (my_hash_init(key_memory_THD_handler_tables_hash,
+ &thd->handler_tables_hash, &my_charset_latin1,
+ HANDLER_TABLES_HASH_SIZE, 0, 0, (my_hash_get_key)
+ mysql_ha_hash_get_key, (my_hash_free_key)
+ mysql_ha_hash_free, 0))
{
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
@@ -332,8 +333,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
right from the start as open_tables() can't handle properly
back-off for such locks.
*/
- tables->mdl_request.init(MDL_key::TABLE, tables->db.str, tables->table_name.str,
- MDL_SHARED_READ, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&tables->mdl_request, MDL_key::TABLE, tables->db.str,
+ tables->table_name.str, MDL_SHARED_READ, MDL_TRANSACTION);
mdl_savepoint= thd->mdl_context.mdl_savepoint();
/* for now HANDLER can be used only for real TABLES */
@@ -384,14 +385,14 @@ 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, "sql_handler", 1024, 0,
+ init_alloc_root(PSI_INSTRUMENT_ME, &sql_handler->mem_root, 1024, 0,
MYF(MY_THREAD_SPECIFIC));
sql_handler->db.length= tables->db.length;
sql_handler->table_name.length= tables->table_name.length;
sql_handler->handler_name.length= tables->alias.length;
- if (!(my_multi_malloc(MY_WME,
+ if (!(my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
&sql_handler->base_data,
(uint) sql_handler->db.length + 1,
&sql_handler->table_name.str,
@@ -479,7 +480,7 @@ err:
If called with reopen flag, no need to rollback either,
it will be done at statement end.
*/
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->set_open_tables(backup_open_tables);
@@ -640,7 +641,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
if ((handler->keyno= find_type(keyname, &table->s->keynames,
FIND_TYPE_NO_PREFIX) - 1) < 0)
{
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname,
+ my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0), keyname,
handler->handler_name.str);
return 1;
}
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index e5f1e958d99..e31e51d0316 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -92,14 +92,12 @@ static bool init_fields(THD *thd, TABLE_LIST *tables,
context->resolve_in_table_list_only(tables);
for (; count-- ; find_fields++)
{
- LEX_CSTRING field_name= {find_fields->field_name,
- strlen(find_fields->field_name) };
/* We have to use 'new' here as field will be re_linked on free */
Item_field *field= (new (thd->mem_root)
Item_field(thd, context,
- "mysql",
- find_fields->table_name,
- &field_name));
+ {STRING_WITH_LEN("mysql")},
+ Lex_cstring_strlen(find_fields->table_name),
+ Lex_cstring_strlen(find_fields->field_name)));
if (!(find_fields->field= find_field_in_tables(thd, field, tables, NULL,
0, REPORT_ALL_ERRORS, 1,
TRUE)))
@@ -673,7 +671,7 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, size_t mlen,
RETURN VALUES
FALSE Success
- TRUE Error and send_error already commited
+ TRUE Error and send_error already committed
*/
static bool mysqld_help_internal(THD *thd, const char *mask)
@@ -711,8 +709,9 @@ static bool mysqld_help_internal(THD *thd, const char *mask)
Reset and backup the current open tables state to
make it possible.
*/
- Open_tables_backup open_tables_state_backup;
- if (open_system_tables_for_read(thd, tables, &open_tables_state_backup))
+ start_new_trans new_trans(thd);
+
+ if (open_system_tables_for_read(thd, tables))
goto error2;
/*
@@ -845,11 +844,13 @@ static bool mysqld_help_internal(THD *thd, const char *mask)
}
my_eof(thd);
- close_system_tables(thd, &open_tables_state_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
DBUG_RETURN(FALSE);
error:
- close_system_tables(thd, &open_tables_state_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
error2:
DBUG_RETURN(TRUE);
@@ -858,9 +859,7 @@ error2:
bool mysqld_help(THD *thd, const char *mask)
{
- sql_mode_t sql_mode_backup= thd->variables.sql_mode;
- thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+ Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
bool rc= mysqld_help_internal(thd, mask);
- thd->variables.sql_mode= sql_mode_backup;
return rc;
}
diff --git a/sql/sql_hset.h b/sql/sql_hset.h
index 7834349a2f7..b3d8165f6f6 100644
--- a/sql/sql_hset.h
+++ b/sql/sql_hset.h
@@ -31,12 +31,20 @@ public:
Constructs an empty hash. Does not allocate memory, it is done upon
the first insert. Thus does not cause or return errors.
*/
- Hash_set(uchar *(*K)(const T *, size_t *, my_bool),
+ Hash_set(PSI_memory_key psi_key, uchar *(*K)(const T *, size_t *, my_bool),
CHARSET_INFO *cs= &my_charset_bin)
{
my_hash_clear(&m_hash);
m_hash.get_key= (my_hash_get_key)K;
m_hash.charset= cs;
+ m_hash.array.m_psi_key= psi_key;
+ }
+ Hash_set(PSI_memory_key psi_key, CHARSET_INFO *charset, ulong default_array_elements,
+ size_t key_offset, size_t key_length, my_hash_get_key get_key,
+ void (*free_element)(void*), uint flags)
+ {
+ my_hash_init(psi_key, &m_hash, charset, default_array_elements, key_offset,
+ key_length, get_key, free_element, flags);
}
/**
Destroy the hash by freeing the buckets table. Does
@@ -57,14 +65,9 @@ public:
*/
bool insert(T *value)
{
- my_hash_init_opt(&m_hash, m_hash.charset, START_SIZE, 0, 0,
- m_hash.get_key, 0, MYF(0));
- size_t key_len;
- 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;
+ my_hash_init_opt(m_hash.array.m_psi_key, &m_hash, m_hash.charset,
+ START_SIZE, 0, 0, m_hash.get_key, 0, HASH_UNIQUE);
+ return my_hash_insert(&m_hash, reinterpret_cast<const uchar*>(value));
}
bool remove(T *value)
{
@@ -78,6 +81,8 @@ public:
bool is_empty() const { return m_hash.records == 0; }
/** Returns the number of unique elements. */
size_t size() const { return static_cast<size_t>(m_hash.records); }
+ /** Erases all elements from the container */
+ void clear() { my_hash_reset(&m_hash); }
const T* at(size_t i) const
{
return reinterpret_cast<T*>(my_hash_element(const_cast<HASH*>(&m_hash), i));
diff --git a/sql/sql_i_s.h b/sql/sql_i_s.h
new file mode 100644
index 00000000000..b9a768f1452
--- /dev/null
+++ b/sql/sql_i_s.h
@@ -0,0 +1,360 @@
+#ifndef SQL_I_S_INCLUDED
+#define SQL_I_S_INCLUDED
+/* Copyright (c) 2000, 2017, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2019, 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
+ 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-1335 USA */
+
+#include "sql_const.h" // MAX_FIELD_VARCHARLENGTH
+#include "sql_basic_types.h" // enum_nullability
+#include "sql_string.h" // strlen, MY_CS_NAME_SIZE
+#include "lex_string.h" // LEX_CSTRING
+#include "mysql_com.h" // enum_field_types
+#include "my_time.h" // TIME_SECOND_PART_DIGITS
+#include "sql_type.h" // Type_handler_xxx
+
+struct TABLE_LIST;
+struct TABLE;
+typedef class Item COND;
+
+#ifdef MYSQL_CLIENT
+#error MYSQL_CLIENT must not be defined
+#endif // MYSQL_CLIENT
+
+
+bool schema_table_store_record(THD *thd, TABLE *table);
+COND *make_cond_for_info_schema(THD *thd, COND *cond, TABLE_LIST *table);
+
+
+enum enum_show_open_table
+{
+ SKIP_OPEN_TABLE= 0U, // do not open table
+ OPEN_FRM_ONLY= 1U, // open FRM file only
+ OPEN_FULL_TABLE= 2U // open FRM,MYD, MYI files
+};
+
+
+enum enum_show_default
+{
+ DEFAULT_TYPE_IMPLICIT= 0,
+ DEFAULT_NONE
+};
+
+
+namespace Show {
+class Type
+{
+ /**
+ This denotes data type for the column. For the most part, there seems to
+ be one entry in the enum for each SQL data type, although there seem to
+ be a number of additional entries in the enum.
+ */
+ const Type_handler *m_type_handler;
+ /**
+ For string-type columns, this is the maximum number of
+ characters. Otherwise, it is the 'display-length' for the column.
+ */
+ uint m_char_length;
+ uint m_unsigned_flag;
+ const Typelib *m_typelib;
+public:
+ Type(const Type_handler *th, uint length, uint unsigned_flag,
+ const Typelib *typelib= NULL)
+ :m_type_handler(th), m_char_length(length), m_unsigned_flag(unsigned_flag),
+ m_typelib(typelib)
+ { }
+ const Type_handler *type_handler() const { return m_type_handler; }
+ uint char_length() const { return m_char_length; }
+ uint decimal_precision() const { return (m_char_length / 100) % 100; }
+ uint decimal_scale() const { return m_char_length % 10; }
+ uint fsp() const
+ {
+ DBUG_ASSERT(m_char_length <= TIME_SECOND_PART_DIGITS);
+ return m_char_length;
+ }
+ uint unsigned_flag() const { return m_unsigned_flag; }
+ const Typelib *typelib() const { return m_typelib; }
+};
+} // namespace Show
+
+
+
+class ST_FIELD_INFO: public Show::Type
+{
+protected:
+ LEX_CSTRING m_name; // I_S column name
+ enum_nullability m_nullability; // NULLABLE or NOT NULL
+ enum_show_default m_def; // Whether has a DEFAULT value
+ LEX_CSTRING m_old_name; // SHOW column name
+ enum_show_open_table m_open_method;
+public:
+ ST_FIELD_INFO(const LEX_CSTRING &name, const Type &type,
+ enum_nullability nullability,
+ enum_show_default def,
+ LEX_CSTRING &old_name,
+ enum_show_open_table open_method)
+ :Type(type), m_name(name),
+ m_nullability(nullability),
+ m_def(def),
+ m_old_name(old_name),
+ m_open_method(open_method)
+ { }
+ ST_FIELD_INFO(const char *name, const Type &type,
+ enum_nullability nullability,
+ enum_show_default def,
+ const char *old_name,
+ enum_show_open_table open_method)
+ :Type(type),
+ m_nullability(nullability),
+ m_def(def),
+ m_open_method(open_method)
+ {
+ m_name.str= name;
+ m_name.length= safe_strlen(name);
+ m_old_name.str= old_name;
+ m_old_name.length= safe_strlen(old_name);
+ }
+ const LEX_CSTRING &name() const { return m_name; }
+ bool nullable() const { return m_nullability == NULLABLE; }
+ enum_show_default def() const { return m_def; }
+ const LEX_CSTRING &old_name() const { return m_old_name; }
+ enum_show_open_table open_method() const { return m_open_method; }
+ bool end_marker() const { return m_name.str == NULL; }
+};
+
+
+namespace Show
+{
+
+
+class Enum: public Type
+{
+public:
+ Enum(const Typelib *typelib) :Type(&type_handler_enum, 0, false, typelib) { }
+};
+
+
+class Blob: public Type
+{
+public:
+ Blob(uint length) :Type(&type_handler_blob, length, false) { }
+};
+
+
+class Varchar: public Type
+{
+public:
+ Varchar(uint length) :Type(&type_handler_varchar, length, false)
+ {
+ DBUG_ASSERT(length * 3 <= MAX_FIELD_VARCHARLENGTH);
+ }
+};
+
+
+class Longtext: public Type
+{
+public:
+ Longtext(uint length) :Type(&type_handler_varchar, length, false) { }
+};
+
+
+class Yesno: public Varchar
+{
+public:
+ Yesno(): Varchar(3) { }
+};
+
+
+class Catalog: public Varchar
+{
+public:
+ Catalog(): Varchar(FN_REFLEN) { }
+};
+
+
+class Name: public Varchar
+{
+public:
+ Name(): Varchar(NAME_CHAR_LEN) { }
+};
+
+
+class Definer: public Varchar
+{
+public:
+ Definer(): Varchar(DEFINER_CHAR_LENGTH) { }
+};
+
+
+class Userhost: public Varchar
+{
+public:
+ Userhost(): Varchar(USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2) { }
+};
+
+
+class CSName: public Varchar
+{
+public:
+ CSName(): Varchar(MY_CS_NAME_SIZE) { }
+};
+
+
+class SQLMode: public Varchar
+{
+public:
+ SQLMode(): Varchar(32*256) { }
+};
+
+
+class Datetime: public Type
+{
+public:
+ Datetime(uint dec) :Type(&type_handler_datetime2, dec, false) { }
+};
+
+
+class Decimal: public Type
+{
+public:
+ Decimal(uint length) :Type(&type_handler_newdecimal, length, false) { }
+};
+
+
+class ULonglong: public Type
+{
+public:
+ ULonglong(uint length) :Type(&type_handler_ulonglong, length, true) { }
+ ULonglong() :ULonglong(MY_INT64_NUM_DECIMAL_DIGITS) { }
+};
+
+
+class ULong: public Type
+{
+public:
+ ULong(uint length) :Type(&type_handler_ulong, length, true) { }
+ ULong() :ULong(MY_INT32_NUM_DECIMAL_DIGITS) { }
+};
+
+
+class SLonglong: public Type
+{
+public:
+ SLonglong(uint length) :Type(&type_handler_slonglong, length, false) { }
+ SLonglong() :SLonglong(MY_INT64_NUM_DECIMAL_DIGITS) { }
+};
+
+
+class SLong: public Type
+{
+public:
+ SLong(uint length) :Type(&type_handler_slong, length, false) { }
+ SLong() :SLong(MY_INT32_NUM_DECIMAL_DIGITS) { }
+};
+
+
+class SShort: public Type
+{
+public:
+ SShort(uint length) :Type(&type_handler_sshort, length, false) { }
+};
+
+
+class STiny: public Type
+{
+public:
+ STiny(uint length) :Type(&type_handler_stiny, length, false) { }
+};
+
+
+class Double: public Type
+{
+public:
+ Double(uint length) :Type(&type_handler_double, length, false) { }
+};
+
+
+class Float: public Type
+{
+public:
+ Float(uint length) :Type(&type_handler_float, length, false) { }
+};
+
+
+
+class Column: public ST_FIELD_INFO
+{
+public:
+ Column(const char *name, const Type &type,
+ enum_nullability nullability,
+ enum_show_default def,
+ const char *old_name,
+ enum_show_open_table open_method= SKIP_OPEN_TABLE)
+ :ST_FIELD_INFO(name, type, nullability, def, old_name, open_method)
+ { }
+ Column(const char *name, const Type &type, enum_nullability nullability,
+ enum_show_default def,
+ enum_show_open_table open_method= SKIP_OPEN_TABLE)
+ :ST_FIELD_INFO(name, type, nullability, def, NullS, open_method)
+ { }
+ Column(const char *name, const Type &type,
+ enum_nullability nullability,
+ const char *old_name,
+ enum_show_open_table open_method= SKIP_OPEN_TABLE)
+ :ST_FIELD_INFO(name, type, nullability, DEFAULT_TYPE_IMPLICIT,
+ old_name, open_method)
+ { }
+ Column(const char *name, const Type &type,
+ enum_nullability nullability,
+ enum_show_open_table open_method= SKIP_OPEN_TABLE)
+ :ST_FIELD_INFO(name, type, nullability, DEFAULT_TYPE_IMPLICIT,
+ NullS, open_method)
+ { }
+};
+
+
+// End marker
+class CEnd: public Column
+{
+public:
+ CEnd() :Column(NullS, Varchar(0), NOT_NULL, NullS, SKIP_OPEN_TABLE) { }
+};
+
+
+} // namespace Show
+
+
+struct TABLE_LIST;
+typedef class Item COND;
+
+typedef struct st_schema_table
+{
+ const char *table_name;
+ ST_FIELD_INFO *fields_info;
+ /* for FLUSH table_name */
+ int (*reset_table) ();
+ /* Fill table with data */
+ int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond);
+ /* Handle fileds for old SHOW */
+ int (*old_format) (THD *thd, struct st_schema_table *schema_table);
+ int (*process_table) (THD *thd, TABLE_LIST *tables, TABLE *table,
+ bool res, const LEX_CSTRING *db_name,
+ const LEX_CSTRING *table_name);
+ int idx_field1, idx_field2;
+ bool hidden;
+ uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
+} ST_SCHEMA_TABLE;
+
+
+#endif // SQL_I_S_INCLUDED
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 7dd83d625e8..7833059438e 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -67,7 +67,6 @@
#include "sp_head.h"
#include "sql_view.h" // check_key_in_view, insert_view_fields
#include "sql_table.h" // mysql_create_table_no_lock
-#include "sql_acl.h" // *_ACL, check_grant_all_columns
#include "sql_trigger.h"
#include "sql_select.h"
#include "sql_show.h"
@@ -96,6 +95,8 @@ pthread_handler_t handle_delayed_insert(void *arg);
static void unlink_blobs(TABLE *table);
#endif
static bool check_view_insertability(THD *thd, TABLE_LIST *view);
+static int binlog_show_create_table(THD *thd, TABLE *table,
+ Table_specification_st *create_info);
/*
Check that insert/update fields are from the same single table of a view.
@@ -554,8 +555,8 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
if (thd->has_read_only_protection())
DBUG_RETURN(TRUE);
- protection_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_DML,
- MDL_STATEMENT);
+ MDL_REQUEST_INIT(&protection_request, MDL_key::BACKUP, "", "",
+ MDL_BACKUP_DML, MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&protection_request,
thd->variables.lock_wait_timeout))
@@ -657,24 +658,12 @@ static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list)
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->first_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))
+ if (unit->explainable())
explain->add_child(unit->first_select()->select_number);
}
}
@@ -689,18 +678,19 @@ Field **TABLE::field_to_fill()
/**
INSERT statement implementation
+ SYNOPSIS
+ mysql_insert()
+ result NULL if the insert is not outputing results
+ via 'RETURNING' clause.
+
@note Like implementations of other DDL/DML in MySQL, this function
relies on the caller to close the thread tables. This is done in the
end of dispatch_command().
*/
-
-bool mysql_insert(THD *thd,TABLE_LIST *table_list,
- List<Item> &fields,
- List<List_item> &values_list,
- List<Item> &update_fields,
- List<Item> &update_values,
- enum_duplicates duplic,
- bool ignore)
+bool mysql_insert(THD *thd, TABLE_LIST *table_list,
+ List<Item> &fields, List<List_item> &values_list,
+ List<Item> &update_fields, List<Item> &update_values,
+ enum_duplicates duplic, bool ignore, select_result *result)
{
bool retval= true;
int error, res;
@@ -719,6 +709,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
List_item *values;
Name_resolution_context *context;
Name_resolution_context_state ctx_state;
+ SELECT_LEX *returning= thd->lex->has_returning() ? thd->lex->returning() : 0;
+
#ifndef EMBEDDED_LIBRARY
char *query= thd->query();
/*
@@ -732,6 +724,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
Item *unused_conds= 0;
DBUG_ENTER("mysql_insert");
+ bzero((char*) &info,sizeof(info));
create_explain_query(thd->lex, thd->mem_root);
/*
Upgrade lock type if the requested lock is incompatible with
@@ -744,8 +737,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
this will lead to a deadlock, since the delayed thread will
never be able to get a lock on the table.
*/
- if (table_list->lock_type == TL_WRITE_DELAYED &&
- thd->locked_tables_mode &&
+ if (table_list->lock_type == TL_WRITE_DELAYED && thd->locked_tables_mode &&
find_locked_table(thd->open_tables, table_list->db.str,
table_list->table_name.str))
{
@@ -773,14 +765,29 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
value_count= values->elements;
- if (mysql_prepare_insert(thd, table_list, table, fields, values,
- update_fields, update_values, duplic, &unused_conds,
- FALSE))
+ if ((res= mysql_prepare_insert(thd, table_list, fields, values,
+ update_fields, update_values, duplic,
+ &unused_conds, FALSE)))
+ {
+ retval= thd->is_error();
+ if (res < 0)
+ {
+ /*
+ Insert should be ignored but we have to log the query in statement
+ format in the binary log
+ */
+ if (thd->binlog_current_query_unfiltered())
+ retval= 1;
+ }
goto abort;
-
+ }
/* mysql_prepare_insert sets table_list->table if it was not set */
table= table_list->table;
+ /* Prepares LEX::returing_list if it is not empty */
+ if (returning)
+ result->prepare(returning->item_list, NULL);
+
context= &thd->lex->first_select_lex()->context;
/*
These three asserts test the hypothesis that the resetting of the name
@@ -834,7 +841,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/*
Fill in the given fields and dump it to the table file
*/
- bzero((char*) &info,sizeof(info));
info.ignore= ignore;
info.handle_duplicates=duplic;
info.update_fields= &update_fields;
@@ -884,13 +890,18 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (lock_type != TL_WRITE_DELAYED)
#endif /* EMBEDDED_LIBRARY */
{
+ bool create_lookup_handler= duplic != DUP_ERROR;
if (duplic != DUP_ERROR || ignore)
{
+ create_lookup_handler= true;
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (table->file->ha_table_flags() & HA_DUPLICATE_POS &&
- table->file->ha_rnd_init_with_error(0))
- goto abort;
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ if (table->file->ha_rnd_init_with_error(0))
+ goto abort;
+ }
}
+ table->file->prepare_for_insert(create_lookup_handler);
/**
This is a simple check for the case when the table has a trigger
that reads from it, or when the statement invokes a stored function
@@ -949,8 +960,19 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
goto values_loop_end;
}
}
+ /*
+ If statement returns result set, we need to send the result set metadata
+ to the client so that it knows that it has to expect an EOF or ERROR.
+ At this point we have all the required information to send the result set
+ metadata.
+ */
+ if (returning &&
+ result->send_result_set_metadata(returning->item_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ goto values_loop_end;
THD_STAGE_INFO(thd, stage_update);
+ thd->decide_logging_format_low(table);
do
{
DBUG_PRINT("info", ("iteration %llu", iteration));
@@ -1063,7 +1085,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
break;
}
- thd->decide_logging_format_low(table);
#ifndef EMBEDDED_LIBRARY
if (lock_type == TL_WRITE_DELAYED)
{
@@ -1075,7 +1096,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
else
#endif
- error=write_record(thd, table ,&info);
+ error= write_record(thd, table, &info, result);
if (unlikely(error))
break;
thd->get_stmt_da()->inc_current_row_for_warning();
@@ -1089,7 +1110,7 @@ values_loop_end:
joins_freed= TRUE;
/*
- Now all rows are inserted. Time to update logs and sends response to
+ Now all rows are inserted. Time to update logs and sends response to
user
*/
#ifndef EMBEDDED_LIBRARY
@@ -1134,7 +1155,7 @@ values_loop_end:
table->file->ha_rnd_end();
}
- transactional_table= table->file->has_transactions();
+ transactional_table= table->file->has_transactions_and_rollback();
if (likely(changed= (info.copied || info.deleted || info.updated)))
{
@@ -1146,13 +1167,13 @@ values_loop_end:
query_cache_invalidate3(thd, table_list, 1);
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
if (error <= 0 ||
- thd->transaction.stmt.modified_non_trans_table ||
+ thd->transaction->stmt.modified_non_trans_table ||
was_insert_delayed)
{
if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
@@ -1190,7 +1211,7 @@ values_loop_end:
such case the flag is ignored for constructing binlog event.
*/
DBUG_ASSERT(thd->killed != KILL_BAD_DATA || error > 0);
- if (was_insert_delayed && table_list->lock_type == TL_WRITE)
+ if (was_insert_delayed && table_list->lock_type == TL_WRITE)
{
/* Binlog INSERT DELAYED as INSERT without DELAYED. */
String log_query;
@@ -1215,7 +1236,7 @@ values_loop_end:
}
}
DBUG_ASSERT(transactional_table || !changed ||
- thd->transaction.stmt.modified_non_trans_table);
+ thd->transaction->stmt.modified_non_trans_table);
}
THD_STAGE_INFO(thd, stage_end);
/*
@@ -1245,7 +1266,7 @@ values_loop_end:
goto abort;
if (thd->lex->analyze_stmt)
{
- retval= thd->lex->explain->send_explain(thd);
+ retval= 0;
goto abort;
}
DBUG_PRINT("info", ("touched: %llu copied: %llu updated: %llu deleted: %llu",
@@ -1255,26 +1276,39 @@ values_loop_end:
if ((iteration * values_list.elements) == 1 &&
(!(thd->variables.option_bits & OPTION_WARNINGS) || !thd->cuted_fields))
{
- my_ok(thd, info.copied + info.deleted +
+ /*
+ Client expects an EOF/OK packet if result set metadata was sent. If
+ LEX::has_returning and the statement returns result set
+ we send EOF which is the indicator of the end of the row stream.
+ Oherwise we send an OK packet i.e when the statement returns only the
+ status information
+ */
+ if (returning)
+ result->send_eof();
+ else
+ my_ok(thd, info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
- info.touched : info.updated),
- id);
+ info.touched : info.updated), id);
}
else
{
char buff[160];
ha_rows updated=((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
info.touched : info.updated);
+
if (ignore)
sprintf(buff, ER_THD(thd, ER_INSERT_INFO), (ulong) info.records,
- (lock_type == TL_WRITE_DELAYED) ? (ulong) 0 :
- (ulong) (info.records - info.copied),
+ (lock_type == TL_WRITE_DELAYED) ? (ulong) 0 :
+ (ulong) (info.records - info.copied),
(long) thd->get_stmt_da()->current_statement_warn_count());
else
sprintf(buff, ER_THD(thd, ER_INSERT_INFO), (ulong) info.records,
- (ulong) (info.deleted + updated),
+ (ulong) (info.deleted + updated),
(long) thd->get_stmt_da()->current_statement_warn_count());
- ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
+ if (returning)
+ result->send_eof();
+ else
+ ::my_ok(thd, info.copied + info.deleted + updated, id, buff);
}
thd->abort_on_warning= 0;
if (thd->lex->current_select->first_cond_optimization)
@@ -1282,7 +1316,7 @@ values_loop_end:
thd->lex->current_select->save_leaf_tables(thd);
thd->lex->current_select->first_cond_optimization= 0;
}
-
+
DBUG_RETURN(FALSE);
abort:
@@ -1389,6 +1423,33 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
}
+/**
+ TODO remove when MDEV-17395 will be closed
+
+ Checks if REPLACE or ON DUPLICATE UPDATE was executed on table containing
+ WITHOUT OVERLAPS key.
+
+ @return
+ 0 if no error
+ ER_NOT_SUPPORTED_YET if the above condidion was met
+ */
+int check_duplic_insert_without_overlaps(THD *thd, TABLE *table,
+ enum_duplicates duplic)
+{
+ if (duplic == DUP_REPLACE || duplic == DUP_UPDATE)
+ {
+ for (uint k = 0; k < table->s->keys; k++)
+ {
+ if (table->key_info[k].without_overlaps)
+ {
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "WITHOUT OVERLAPS");
+ return ER_NOT_SUPPORTED_YET;
+ }
+ }
+ }
+ return 0;
+}
+
/*
Check if table can be updated
@@ -1483,12 +1544,10 @@ static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables)
SYNOPSIS
mysql_prepare_insert()
- thd Thread handler
- table_list Global/local table list
- table Table to insert into (can be NULL if table should
- be taken from table_list->table)
- where Where clause (for insert ... select)
- select_insert TRUE if INSERT ... SELECT statement
+ thd Thread handler
+ table_list Global/local table list
+ where Where clause (for insert ... select)
+ select_insert TRUE if INSERT ... SELECT statement
TODO (in far future)
In cases of:
@@ -1499,17 +1558,18 @@ static void prepare_for_positional_update(TABLE *table, TABLE_LIST *tables)
WARNING
You MUST set table->insert_values to 0 after calling this function
before releasing the table object.
-
+
RETURN VALUE
- FALSE OK
- TRUE error
+ 0 OK
+ >0 error
+ <0 insert should be ignored
*/
-bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
- TABLE *table, List<Item> &fields, List_item *values,
- List<Item> &update_fields, List<Item> &update_values,
- enum_duplicates duplic, COND **where,
- bool select_insert)
+int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
+ List<Item> &fields, List_item *values,
+ List<Item> &update_fields, List<Item> &update_values,
+ enum_duplicates duplic, COND **where,
+ bool select_insert)
{
SELECT_LEX *select_lex= thd->lex->first_select_lex();
Name_resolution_context *context= &select_lex->context;
@@ -1517,48 +1577,34 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
bool insert_into_view= (table_list->view != 0);
bool res= 0;
table_map map= 0;
+ TABLE *table;
DBUG_ENTER("mysql_prepare_insert");
- DBUG_PRINT("enter", ("table_list: %p table: %p view: %d",
- table_list, table,
- (int)insert_into_view));
+ DBUG_PRINT("enter", ("table_list: %p view: %d",
+ table_list, (int) insert_into_view));
/* INSERT should have a SELECT or VALUES clause */
DBUG_ASSERT (!select_insert || !values);
if (mysql_handle_derived(thd->lex, DT_INIT))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE))
- DBUG_RETURN(TRUE);
- /*
- For subqueries in VALUES() we should not see the table in which we are
- inserting (for INSERT ... SELECT this is done by changing table_list,
- because INSERT ... SELECT share SELECT_LEX it with SELECT.
- */
- if (!select_insert)
- {
- for (SELECT_LEX_UNIT *un= select_lex->first_inner_unit();
- un;
- un= un->next_unit())
- {
- for (SELECT_LEX *sl= un->first_select();
- sl;
- sl= sl->next_select())
- {
- sl->context.outer_context= 0;
- }
- }
- }
+ DBUG_RETURN(1);
if (duplic == DUP_UPDATE)
{
/* it should be allocated before Item::fix_fields() */
if (table_list->set_insert_values(thd->mem_root))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
+ table= table_list->table;
+
+ if (table->file->check_if_updates_are_ignored("INSERT"))
+ DBUG_RETURN(-1);
+
if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
/* Prepare the fields in the statement. */
if (values)
@@ -1576,10 +1622,11 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
table_list->next_local= 0;
context->resolve_in_table_list_only(table_list);
- res= (setup_fields(thd, Ref_ptr_array(),
- *values, MARK_COLUMNS_READ, 0, NULL, 0) ||
+ res= setup_returning_fields(thd, table_list) ||
+ setup_fields(thd, Ref_ptr_array(),
+ *values, MARK_COLUMNS_READ, 0, NULL, 0) ||
check_insert_fields(thd, context->table_list, fields, *values,
- !insert_into_view, 0, &map));
+ !insert_into_view, 0, &map);
if (!res)
res= setup_fields(thd, Ref_ptr_array(),
@@ -1600,14 +1647,14 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
if (res)
DBUG_RETURN(res);
- if (!table)
- table= table_list->table;
+ if (check_duplic_insert_without_overlaps(thd, table, duplic) != 0)
+ DBUG_RETURN(true);
if (table->versioned(VERS_TIMESTAMP) && duplic == DUP_REPLACE)
{
// Additional memory may be required to create historical items.
if (table_list->set_insert_values(thd->mem_root))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
if (!select_insert)
@@ -1618,7 +1665,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
CHECK_DUP_ALLOW_DIFFERENT_ALIAS)))
{
update_non_unique_table_error(table_list, "INSERT", duplicate);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
select_lex->fix_prepare_information(thd, &fake_conds, &fake_conds);
}
@@ -1628,7 +1675,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
*/
if (duplic == DUP_UPDATE || duplic == DUP_REPLACE)
prepare_for_positional_update(table, table_list);
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(0);
}
@@ -1700,6 +1747,7 @@ int vers_insert_history_row(TABLE *table)
info - COPY_INFO structure describing handling of duplicates
and which is used for counting number of records inserted
and deleted.
+ sink - result sink for the RETURNING clause
NOTE
Once this record will be written to table after insert trigger will
@@ -1707,8 +1755,8 @@ int vers_insert_history_row(TABLE *table)
then both on update triggers will work instead. Similarly both on
delete triggers will be invoked if we will delete conflicting records.
- Sets thd->transaction.stmt.modified_non_trans_table to TRUE if table which is updated didn't have
- transactions.
+ Sets thd->transaction.stmt.modified_non_trans_table to TRUE if table which
+ is updated didn't have transactions.
RETURN VALUE
0 - success
@@ -1716,7 +1764,7 @@ int vers_insert_history_row(TABLE *table)
*/
-int write_record(THD *thd, TABLE *table,COPY_INFO *info)
+int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
{
int error, trg_error= 0;
char *key=0;
@@ -1727,7 +1775,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
DBUG_ENTER("write_record");
info->records++;
- save_read_set= table->read_set;
+ save_read_set= table->read_set;
save_write_set= table->write_set;
if (info->handle_duplicates == DUP_REPLACE ||
@@ -1749,7 +1797,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->file->insert_id_for_cur_row= insert_id_for_cur_row;
bool is_duplicate_key_error;
if (table->file->is_fatal_error(error, HA_CHECK_ALL))
- goto err;
+ goto err;
is_duplicate_key_error=
table->file->is_fatal_error(error, HA_CHECK_ALL & ~HA_CHECK_DUP);
if (!is_duplicate_key_error)
@@ -1762,7 +1810,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (info->ignore)
{
table->file->print_error(error, MYF(ME_WARNING));
- goto ok_or_after_trg_err; /* Ignoring a not fatal error, return 0 */
+ goto after_trg_or_ignored_err; /* Ignoring a not fatal error */
}
goto err;
}
@@ -1866,7 +1914,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
res= info->table_list->view_check_option(table->in_use, info->ignore);
if (res == VIEW_CHECK_SKIP)
- goto ok_or_after_trg_err;
+ goto after_trg_or_ignored_err;
if (res == VIEW_CHECK_ERROR)
goto before_trg_err;
@@ -1884,7 +1932,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
if (!(thd->variables.old_behavior &
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
table->file->print_error(error, MYF(ME_WARNING));
- goto ok_or_after_trg_err;
+ goto after_trg_or_ignored_err;
}
goto err;
}
@@ -1903,7 +1951,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->file->print_error(error, MYF(0));
trg_error= 1;
restore_record(table, record[2]);
- goto ok_or_after_trg_err;
+ goto after_trg_or_ignored_err;
}
restore_record(table, record[2]);
}
@@ -1944,7 +1992,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
{
table->file->restore_auto_increment(prev_insert_id_for_cur_row);
}
- goto ok_or_after_trg_err;
+ goto ok;
}
else /* DUP_REPLACE */
{
@@ -1961,8 +2009,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
tables which have ON UPDATE but have no ON DELETE triggers,
we just should not expose this fact to users by invoking
ON UPDATE triggers.
- For system versioning wa also use path through delete since we would
- save nothing through this cheating.
*/
if (last_uniq_key(table,key_nr) &&
!table->file->referenced_by_foreign_key() &&
@@ -2022,14 +2068,14 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
info->deleted++;
else
info->updated++;
- if (!table->file->has_transactions())
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ if (!table->file->has_transactions_and_rollback())
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, TRUE))
{
trg_error= 1;
- goto ok_or_after_trg_err;
+ goto after_trg_or_ignored_err;
}
/* Let us attempt do write_row() once more */
}
@@ -2065,7 +2111,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
table->file->print_error(error, MYF(ME_WARNING));
table->file->restore_auto_increment();
- goto ok_or_after_trg_err;
+ goto after_trg_or_ignored_err;
}
after_trg_n_copied_inc:
@@ -2075,11 +2121,21 @@ after_trg_n_copied_inc:
table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
TRG_ACTION_AFTER, TRUE));
-ok_or_after_trg_err:
+ok:
+ /*
+ We send the row after writing it to the table so that the
+ correct values are sent to the client. Otherwise it won't show
+ autoinc values (generated inside the handler::ha_write()) and
+ values updated in ON DUPLICATE KEY UPDATE.
+ */
+ if (sink && sink->send_data(thd->lex->returning()->item_list) < 0)
+ trg_error= 1;
+
+after_trg_or_ignored_err:
if (key)
my_safe_afree(key,table->s->max_unique_length);
- if (!table->file->has_transactions())
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ if (!table->file->has_transactions_and_rollback())
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
DBUG_RETURN(trg_error);
err:
@@ -2394,7 +2450,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
di->thd.variables.binlog_annotate_row_events= 0;
di->thd.set_db(&table_list->db);
- di->thd.set_query(my_strndup(table_list->table_name.str,
+ di->thd.set_query(my_strndup(PSI_INSTRUMENT_ME,
+ table_list->table_name.str,
table_list->table_name.length,
MYF(MY_WME | ME_FATAL)),
table_list->table_name.length, system_charset_info);
@@ -2413,8 +2470,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
We need the tickets so that they can be cloned in
handle_delayed_insert
*/
- di->grl_protection.init(MDL_key::BACKUP, "", "",
- MDL_BACKUP_DML, MDL_STATEMENT);
+ MDL_REQUEST_INIT(&di->grl_protection, MDL_key::BACKUP, "", "",
+ MDL_BACKUP_DML, MDL_STATEMENT);
di->grl_protection.ticket= grl_protection_request->ticket;
init_mdl_requests(&di->table_list);
di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
@@ -2494,7 +2551,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
end_create:
mysql_mutex_unlock(&LOCK_delayed_create);
- DBUG_PRINT("exit", ("is_error: %d", thd->is_error()));
+ DBUG_PRINT("exit", ("is_error(): %d", thd->is_error()));
DBUG_RETURN(thd->is_error());
}
@@ -2530,6 +2587,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
uchar *bitmap;
char *copy_tmp;
uint bitmaps_used;
+ Field **default_fields, **virtual_fields;
+ uchar *record;
DBUG_ENTER("Delayed_insert::get_local_table");
/* First request insert thread to get a lock */
@@ -2576,18 +2635,29 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
share= table->s;
/*
- Allocate memory for the TABLE object, the field pointers array, and
- one record buffer of reclength size. Normally a table has three
- record buffers of rec_buff_length size, which includes alignment
- bytes. Since the table copy is used for creating one record only,
- the other record buffers and alignment are unnecessary.
+ Allocate memory for the TABLE object, the field pointers array,
+ and one record buffer of reclength size.
+ Normally a table has three record buffers of rec_buff_length size,
+ which includes alignment bytes. Since the table copy is used for
+ creating one record only, the other record buffers and alignment
+ are unnecessary.
+ As the table will also need to calculate default values and
+ expresions, we have to allocate own version of fields. keys and key
+ parts. The key and key parts are needed as parse_vcol_defs() changes
+ them in case of long hash keys.
*/
THD_STAGE_INFO(client_thd, stage_allocating_local_table);
- copy_tmp= (char*) client_thd->alloc(sizeof(*copy)+
- (share->fields+1)*sizeof(Field**)+
- share->reclength +
- share->column_bitmap_size*4);
- if (!copy_tmp)
+ if (!multi_alloc_root(client_thd->mem_root,
+ &copy_tmp, sizeof(*table),
+ &field, (uint) (share->fields+1)*sizeof(Field**),
+ &default_fields,
+ (share->default_fields +
+ share->default_expressions + 1) * sizeof(Field*),
+ &virtual_fields,
+ (share->virtual_fields + 1) * sizeof(Field*),
+ &record, (uint) share->reclength,
+ &bitmap, (uint) share->column_bitmap_size*4,
+ NullS))
goto error;
/* Copy the TABLE object. */
@@ -2596,27 +2666,14 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
/* We don't need to change the file handler here */
/* Assign the pointers for the field pointers array and the record. */
- field= copy->field= (Field**) (copy + 1);
- bitmap= (uchar*) (field + share->fields + 1);
- copy->record[0]= (bitmap + share->column_bitmap_size*4);
+ copy->field= field;
+ copy->record[0]= record;
memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
if (share->default_fields || share->default_expressions)
- {
- copy->default_field= (Field**)
- client_thd->alloc((share->default_fields +
- share->default_expressions + 1)*
- sizeof(Field*));
- if (!copy->default_field)
- goto error;
- }
-
+ copy->default_field= default_fields;
if (share->virtual_fields)
- {
- copy->vfield= (Field **) client_thd->alloc((share->virtual_fields+1)*
- sizeof(Field*));
- if (!copy->vfield)
- goto error;
- }
+ copy->vfield= virtual_fields;
+
copy->expr_arena= NULL;
/* Ensure we don't use the table list of the original table */
@@ -2639,6 +2696,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
(*field)->invisible= (*org_field)->invisible;
(*field)->orig_table= copy; // Remove connection
(*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0]
+ (*field)->flags|= ((*org_field)->flags & LONG_UNIQUE_HASH_FIELD);
+ (*field)->invisible= (*org_field)->invisible;
memdup_vcol(client_thd, (*field)->vcol_info);
memdup_vcol(client_thd, (*field)->default_value);
memdup_vcol(client_thd, (*field)->check_constraint);
@@ -2647,6 +2706,9 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
}
*field=0;
+ if (copy_keys_from_share(copy, client_thd->mem_root))
+ goto error;
+
if (share->virtual_fields || share->default_expressions ||
share->default_fields)
{
@@ -2724,7 +2786,8 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
if (query.str)
{
char *str;
- if (!(str= my_strndup(query.str, query.length, MYF(MY_WME))))
+ if (!(str= my_strndup(PSI_INSTRUMENT_ME, query.str, query.length,
+ MYF(MY_WME))))
goto err;
query.str= str;
}
@@ -2747,7 +2810,8 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
ip_len= strlen(thd->security_ctx->ip) + 1;
}
/* This can't be THREAD_SPECIFIC as it's freed in delayed thread */
- if (!(row->record= (char*) my_malloc(table->s->reclength +
+ if (!(row->record= (char*) my_malloc(PSI_INSTRUMENT_ME,
+ table->s->reclength +
user_len + host_len + ip_len,
MYF(MY_WME))))
goto err;
@@ -2992,6 +3056,8 @@ bool Delayed_insert::open_and_lock_table()
return TRUE;
}
table->copy_blobs= 1;
+
+ table->file->prepare_for_row_logging();
return FALSE;
}
@@ -3035,15 +3101,16 @@ pthread_handler_t handle_delayed_insert(void *arg)
{
DBUG_ENTER("handle_delayed_insert");
thd->thread_stack= (char*) &thd;
- if (init_thr_lock() || thd->store_globals())
+ if (init_thr_lock())
{
- /* Can't use my_error since store_globals has perhaps failed */
thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES);
di->handler_thread_initialized= TRUE;
thd->fatal_error();
goto err;
}
+ thd->store_globals();
+
thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
/*
@@ -3051,6 +3118,8 @@ pthread_handler_t handle_delayed_insert(void *arg)
at which rows are inserted cannot be determined in mixed mode.
*/
thd->set_current_stmt_binlog_format_row_if_mixed();
+ /* Don't annotate insert delayed binlog events */
+ thd->variables.binlog_annotate_row_events= 0;
/*
Clone tickets representing protection against GRL and the lock on
@@ -3249,8 +3318,19 @@ pthread_handler_t handle_delayed_insert(void *arg)
trans_commit_stmt(thd);
di->group_count=0;
mysql_audit_release(thd);
+ /*
+ Reset binlog. We can't call ha_reset() for the table as this will
+ reset the table maps we have calculated earlier.
+ */
mysql_mutex_lock(&di->mutex);
}
+
+ /*
+ Reset binlog. We can't call ha_reset() for the table as this will
+ reset the table maps we have calculated earlier.
+ */
+ thd->reset_binlog_for_next_statement();
+
if (di->tables_in_use)
mysql_cond_broadcast(&di->cond_client); // If waiting clients
}
@@ -3342,9 +3422,7 @@ bool Delayed_insert::handle_inserts(void)
{
int error;
ulong max_rows;
- bool has_trans = TRUE;
- bool using_ignore= 0, using_opt_replace= 0,
- using_bin_log= mysql_bin_log.is_open();
+ bool using_ignore= 0, using_opt_replace= 0, using_bin_log;
delayed_row *row;
DBUG_ENTER("handle_inserts");
@@ -3378,6 +3456,13 @@ bool Delayed_insert::handle_inserts(void)
if (table->file->ha_rnd_init_with_error(0))
goto err;
+ /*
+ We have to call prepare_for_row_logging() as the second call to
+ handler_writes() will not have called decide_logging_format.
+ */
+ table->file->prepare_for_row_logging();
+ table->file->prepare_for_insert(1);
+ using_bin_log= table->file->row_logging;
/*
We can't use row caching when using the binary log because if
@@ -3386,6 +3471,7 @@ bool Delayed_insert::handle_inserts(void)
*/
if (!using_bin_log)
table->file->extra(HA_EXTRA_WRITE_CACHE);
+
mysql_mutex_lock(&mutex);
while ((row=rows.get()))
@@ -3414,8 +3500,8 @@ bool Delayed_insert::handle_inserts(void)
Guaranteed that the INSERT DELAYED STMT will not be here
in SBR when mysql binlog is enabled.
*/
- DBUG_ASSERT(!(mysql_bin_log.is_open() &&
- !thd.is_current_stmt_binlog_format_row()));
+ DBUG_ASSERT(!mysql_bin_log.is_open() ||
+ thd.is_current_stmt_binlog_format_row());
/*
This is the first value of an INSERT statement.
@@ -3478,7 +3564,7 @@ bool Delayed_insert::handle_inserts(void)
VCOL_UPDATE_FOR_WRITE);
}
- if (unlikely(tmp_error) || unlikely(write_record(&thd, table, &info)))
+ if (unlikely(tmp_error || write_record(&thd, table, &info, NULL)))
{
info.error_count++; // Ignore errors
thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status);
@@ -3573,10 +3659,9 @@ bool Delayed_insert::handle_inserts(void)
TODO: Move the logging to last in the sequence of rows.
*/
- has_trans= thd.lex->sql_command == SQLCOM_CREATE_TABLE ||
- table->file->has_transactions();
- if (thd.is_current_stmt_binlog_format_row() &&
- thd.binlog_flush_pending_rows_event(TRUE, has_trans))
+ if (table->file->row_logging &&
+ thd.binlog_flush_pending_rows_event(TRUE,
+ table->file->row_logging_has_trans))
goto err;
if (unlikely((error=table->file->extra(HA_EXTRA_NO_CACHE))))
@@ -3630,27 +3715,35 @@ bool Delayed_insert::handle_inserts(void)
thd thread handler
RETURN
- FALSE OK
- TRUE Error
+ 0 OK
+ > 0 Error
+ < 0 Ok, ignore insert
*/
-bool mysql_insert_select_prepare(THD *thd)
+int mysql_insert_select_prepare(THD *thd, select_result *sel_res)
{
+ int res;
LEX *lex= thd->lex;
SELECT_LEX *select_lex= lex->first_select_lex();
DBUG_ENTER("mysql_insert_select_prepare");
-
/*
SELECT_LEX do not belong to INSERT statement, so we can't add WHERE
clause if table is VIEW
*/
-
- if (mysql_prepare_insert(thd, lex->query_tables,
- lex->query_tables->table, lex->field_list, 0,
- lex->update_list, lex->value_list, lex->duplicates,
- &select_lex->where, TRUE))
- DBUG_RETURN(TRUE);
+
+ if ((res= mysql_prepare_insert(thd, lex->query_tables, lex->field_list, 0,
+ lex->update_list, lex->value_list,
+ lex->duplicates,
+ &select_lex->where, TRUE)))
+ DBUG_RETURN(res);
+
+ /*
+ If sel_res is not empty, it means we have items in returing_list.
+ So we prepare the list now
+ */
+ if (sel_res)
+ sel_res->prepare(lex->returning()->item_list, NULL);
DBUG_ASSERT(select_lex->leaf_tables.elements != 0);
List_iterator<TABLE_LIST> ti(select_lex->leaf_tables);
@@ -3684,7 +3777,7 @@ bool mysql_insert_select_prepare(THD *thd)
while ((table= ti++) && insert_tables--)
ti.remove();
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(0);
}
@@ -3694,8 +3787,10 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par,
List<Item> *update_fields,
List<Item> *update_values,
enum_duplicates duplic,
- bool ignore_check_option_errors):
+ bool ignore_check_option_errors,
+ select_result *result):
select_result_interceptor(thd_arg),
+ sel_result(result),
table_list(table_list_par), table(table_par), fields(fields_par),
autoinc_value_of_last_inserted_row(0),
insert_into_view(table_list_par && table_list_par->view != 0)
@@ -3714,7 +3809,7 @@ int
select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
{
LEX *lex= thd->lex;
- int res;
+ int res= 0;
table_map map= 0;
SELECT_LEX *lex_current_select_save= lex->current_select;
DBUG_ENTER("select_insert::prepare");
@@ -3728,17 +3823,18 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
*/
lex->current_select= lex->first_select_lex();
- res= (setup_fields(thd, Ref_ptr_array(),
- values, MARK_COLUMNS_READ, 0, NULL, 0) ||
+ res= (setup_returning_fields(thd, table_list) ||
+ setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, 0,
+ 0) ||
check_insert_fields(thd, table_list, *fields, values,
!insert_into_view, 1, &map));
if (!res && fields->elements)
{
- bool saved_abort_on_warning= thd->abort_on_warning;
- 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;
+ Abort_on_warning_instant_set aws(thd,
+ !info.ignore && thd->is_strict_mode());
+ res= check_that_all_fields_are_given_values(thd, table_list->table,
+ table_list);
}
if (info.handle_duplicates == DUP_UPDATE && !res)
@@ -3855,13 +3951,18 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
#endif
thd->cuted_fields=0;
+ bool create_lookup_handler= info.handle_duplicates != DUP_ERROR;
if (info.ignore || info.handle_duplicates != DUP_ERROR)
{
+ create_lookup_handler= true;
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (table->file->ha_table_flags() & HA_DUPLICATE_POS &&
- table->file->ha_rnd_init_with_error(0))
- DBUG_RETURN(1);
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ if (table->file->ha_rnd_init_with_error(0))
+ DBUG_RETURN(1);
+ }
}
+ table->file->prepare_for_insert(create_lookup_handler);
if (info.handle_duplicates == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -3892,7 +3993,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
If the result table is the same as one of the source tables
(INSERT SELECT), the result table is not finally prepared at the
join prepair phase. Do the final preparation now.
-
+
RETURN
0 OK
*/
@@ -3900,11 +4001,18 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
int select_insert::prepare2(JOIN *)
{
DBUG_ENTER("select_insert::prepare2");
+ if (table->validate_default_values_of_unset_fields(thd))
+ DBUG_RETURN(1);
+ if (thd->lex->describe)
+ DBUG_RETURN(0);
if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
- thd->locked_tables_mode <= LTM_LOCK_TABLES &&
- !thd->lex->describe)
+ thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_start_bulk_insert((ha_rows) 0);
- if (table->validate_default_values_of_unset_fields(thd))
+
+ /* Same as the other variants of INSERT */
+ if (sel_result &&
+ sel_result->send_result_set_metadata(thd->lex->returning()->item_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
DBUG_RETURN(0);
}
@@ -3919,6 +4027,7 @@ void select_insert::cleanup()
select_insert::~select_insert()
{
DBUG_ENTER("~select_insert");
+ sel_result= NULL;
if (table && table->is_created())
{
table->next_number_field=0;
@@ -3936,14 +4045,6 @@ int select_insert::send_data(List<Item> &values)
DBUG_ENTER("select_insert::send_data");
bool error=0;
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (unlikely(thd->killed == ABORT_QUERY))
- DBUG_RETURN(0);
-
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
if (table->default_field &&
@@ -3967,10 +4068,10 @@ int select_insert::send_data(List<Item> &values)
}
}
- error= write_record(thd, table, &info);
+ error= write_record(thd, table, &info, sel_result);
table->vers_write= table->versioned();
table->auto_increment_field_not_null= FALSE;
-
+
if (likely(!error))
{
if (table->triggers || info.handle_duplicates == DUP_UPDATE)
@@ -4022,13 +4123,13 @@ void select_insert::store_values(List<Item> &values)
bool select_insert::prepare_eof()
{
int error;
- bool const trans_table= table->file->has_transactions();
+ bool const trans_table= table->file->has_transactions_and_rollback();
bool changed;
bool binary_logged= 0;
killed_state killed_status= thd->killed;
DBUG_ENTER("select_insert::prepare_eof");
- DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'",
+ DBUG_PRINT("enter", ("trans_table: %d, table_type: '%s'",
trans_table, table->file->table_type()));
#ifdef WITH_WSREP
@@ -4057,13 +4158,13 @@ bool select_insert::prepare_eof()
query_cache_invalidate3(thd, table, 1);
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
DBUG_ASSERT(trans_table || !changed ||
- thd->transaction.stmt.modified_non_trans_table);
+ thd->transaction->stmt.modified_non_trans_table);
/*
Write to binlog before commiting transaction. No statement will
@@ -4072,7 +4173,7 @@ bool select_insert::prepare_eof()
ha_autocommit_or_rollback() is issued below.
*/
if ((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
- (likely(!error) || thd->transaction.stmt.modified_non_trans_table))
+ (likely(!error) || thd->transaction->stmt.modified_non_trans_table))
{
int errcode= 0;
int res;
@@ -4106,7 +4207,6 @@ bool select_insert::send_ok_packet() {
char message[160]; /* status message */
ulonglong row_count; /* rows affected */
ulonglong id; /* last insert-id */
-
DBUG_ENTER("select_insert::send_ok_packet");
if (info.ignore)
@@ -4128,7 +4228,14 @@ bool select_insert::send_ok_packet() {
thd->first_successful_insert_id_in_prev_stmt :
(info.copied ? autoinc_value_of_last_inserted_row : 0));
- ::my_ok(thd, row_count, id, message);
+ /*
+ Client expects an EOF/OK packet If LEX::has_returning and if result set
+ meta was sent. See explanation for other variants of INSERT.
+ */
+ if (sel_result)
+ sel_result->send_eof();
+ else
+ ::my_ok(thd, row_count, id, message);
DBUG_RETURN(false);
}
@@ -4155,7 +4262,7 @@ void select_insert::abort_result_set()
table will be assigned with view table structure, but that table will
not be opened really (it is dummy to check fields types & Co).
*/
- if (table && table->file->get_table())
+ if (table && table->file->is_open())
{
bool changed, transactional_table;
/*
@@ -4185,12 +4292,12 @@ void select_insert::abort_result_set()
zero, so no check for that is made.
*/
changed= (info.copied || info.deleted || info.updated);
- transactional_table= table->file->has_transactions();
- if (thd->transaction.stmt.modified_non_trans_table ||
+ transactional_table= table->file->has_transactions_and_rollback();
+ if (thd->transaction->stmt.modified_non_trans_table ||
thd->log_current_statement)
{
if (!can_rollback_data())
- thd->transaction.all.modified_non_trans_table= TRUE;
+ thd->transaction->all.modified_non_trans_table= TRUE;
if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
@@ -4206,7 +4313,7 @@ void select_insert::abort_result_set()
query_cache_invalidate3(thd, table, 1);
}
DBUG_ASSERT(transactional_table || !changed ||
- thd->transaction.stmt.modified_non_trans_table);
+ thd->transaction->stmt.modified_non_trans_table);
table->s->table_creation_was_logged|= binary_logged;
table->file->ha_release_auto_increment();
@@ -4220,11 +4327,11 @@ void select_insert::abort_result_set()
CREATE TABLE (SELECT) ...
***************************************************************************/
-Field *Item::create_field_for_create_select(TABLE *table)
+Field *Item::create_field_for_create_select(MEM_ROOT *root, TABLE *table)
{
static Tmp_field_param param(false, false, false, false);
Tmp_field_src src;
- return create_tmp_field_ex(table, &src, &param);
+ return create_tmp_field_ex(root, table, &src, &param);
}
@@ -4296,7 +4403,8 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items,
while ((item=it++))
{
- Field *tmp_field= item->create_field_for_create_select(&tmp_table);
+ Field *tmp_field= item->create_field_for_create_select(thd->mem_root,
+ &tmp_table);
if (!tmp_field)
DBUG_RETURN(NULL);
@@ -4466,6 +4574,18 @@ TABLE *select_create::create_table_from_items(THD *thd, List<Item> *items,
/* purecov: end */
}
table->s->table_creation_was_logged= save_table_creation_was_logged;
+ if (!table->s->tmp_table)
+ table->file->prepare_for_row_logging();
+
+ /*
+ If slave is converting a statement event to row events, log the original
+ create statement as an annotated row
+ */
+#ifdef HAVE_REPLICATION
+ if (thd->slave_thread && opt_replicate_annotate_row_events &&
+ thd->is_current_stmt_binlog_format_row())
+ thd->variables.binlog_annotate_row_events= 1;
+#endif
DBUG_RETURN(table);
}
@@ -4523,13 +4643,9 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u)
return error;
TABLE const *const table = *tables;
- if (thd->is_current_stmt_binlog_format_row() &&
+ if (thd->is_current_stmt_binlog_format_row() &&
!table->s->tmp_table)
- {
- int error;
- if (unlikely((error= ptr->binlog_show_create_table(tables, count))))
- return error;
- }
+ return binlog_show_create_table(thd, *tables, ptr->create_info);
return 0;
}
select_create *ptr;
@@ -4604,13 +4720,18 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u)
restore_record(table,s->default_values); // Get empty record
thd->cuted_fields=0;
+ bool create_lookup_handler= info.handle_duplicates != DUP_ERROR;
if (info.ignore || info.handle_duplicates != DUP_ERROR)
{
+ create_lookup_handler= true;
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- if (table->file->ha_table_flags() & HA_DUPLICATE_POS &&
- table->file->ha_rnd_init_with_error(0))
- DBUG_RETURN(1);
+ if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
+ {
+ if (table->file->ha_rnd_init_with_error(0))
+ DBUG_RETURN(1);
+ }
}
+ table->file->prepare_for_insert(create_lookup_handler);
if (info.handle_duplicates == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -4628,8 +4749,9 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u)
DBUG_RETURN(0);
}
-int
-select_create::binlog_show_create_table(TABLE **tables, uint count)
+
+static int binlog_show_create_table(THD *thd, TABLE *table,
+ Table_specification_st *create_info)
{
/*
Note 1: In RBR mode, we generate a CREATE TABLE statement for the
@@ -4648,14 +4770,12 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
statement transaction cache.
*/
DBUG_ASSERT(thd->is_current_stmt_binlog_format_row());
- DBUG_ASSERT(tables && *tables && count > 0);
-
StringBuffer<2048> query(system_charset_info);
int result;
TABLE_LIST tmp_table_list;
tmp_table_list.reset();
- tmp_table_list.table = *tables;
+ tmp_table_list.table = table;
result= show_create_table(thd, &tmp_table_list, &query,
create_info, WITH_DB_NAME);
@@ -4684,6 +4804,88 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
return result;
}
+
+/**
+ Log CREATE TABLE to binary log
+
+ @param thd Thread handler
+ @param table Log create statement for this table
+
+ This function is called from ALTER TABLE for a shared table converted
+ to a not shared table.
+*/
+
+bool binlog_create_table(THD *thd, TABLE *table, bool replace)
+{
+ Table_specification_st create_info;
+ bool result;
+ ulonglong save_option_bits;
+
+ /* Don't log temporary tables in row format */
+ if (thd->variables.binlog_format == BINLOG_FORMAT_ROW &&
+ table->s->tmp_table)
+ return 0;
+ if (!thd->binlog_table_should_be_logged(&table->s->db))
+ return 0;
+
+ /*
+ We have to use ROW format to ensure that future row inserts will be
+ logged
+ */
+ thd->set_current_stmt_binlog_format_row();
+ table->file->prepare_for_row_logging();
+
+ create_info.lex_start();
+ save_option_bits= thd->variables.option_bits;
+ if (replace)
+ create_info.set(DDL_options_st::OPT_OR_REPLACE);
+ /* Ensure we write ENGINE=xxx and CHARSET=... to binary log */
+ create_info.used_fields|= (HA_CREATE_USED_ENGINE |
+ HA_CREATE_USED_DEFAULT_CHARSET);
+ /* Ensure we write all engine options to binary log */
+ create_info.used_fields|= HA_CREATE_PRINT_ALL_OPTIONS;
+ result= binlog_show_create_table(thd, table, &create_info) != 0;
+ thd->variables.option_bits= save_option_bits;
+ return result;
+}
+
+
+/**
+ Log DROP TABLE to binary log
+
+ @param thd Thread handler
+ @param table Log create statement for this table
+
+ This function is called from ALTER TABLE for a shared table converted
+ to a not shared table.
+*/
+
+bool binlog_drop_table(THD *thd, TABLE *table)
+{
+ StringBuffer<2048> query(system_charset_info);
+ /* Don't log temporary tables in row format */
+ if (!table->s->table_creation_was_logged)
+ return 0;
+ if (!thd->binlog_table_should_be_logged(&table->s->db))
+ return 0;
+
+ query.append("DROP ");
+ if (table->s->tmp_table)
+ query.append("TEMPORARY ");
+ query.append("TABLE IF EXISTS ");
+ append_identifier(thd, &query, &table->s->db);
+ query.append(".");
+ append_identifier(thd, &query, &table->s->table_name);
+
+ return thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query.ptr(), query.length(),
+ /* is_trans */ TRUE,
+ /* direct */ FALSE,
+ /* suppress_use */ TRUE,
+ 0) > 0;
+}
+
+
void select_create::store_values(List<Item> &values)
{
fill_record_n_invoke_before_triggers(thd, table, field, values, 1,
@@ -4701,7 +4903,10 @@ bool select_create::send_eof()
mark the flag at this point.
*/
if (table->s->tmp_table)
- thd->transaction.stmt.mark_created_temp_table();
+ thd->transaction->stmt.mark_created_temp_table();
+
+ if (thd->slave_thread)
+ thd->variables.binlog_annotate_row_events= 0;
if (prepare_eof())
{
@@ -4863,7 +5068,7 @@ void select_create::abort_result_set()
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;
+ thd->transaction->stmt.modified_non_trans_table= FALSE;
thd->variables.option_bits= save_option_bits;
/* possible error of writing binary log is ignored deliberately */
diff --git a/sql/sql_insert.h b/sql/sql_insert.h
index a37ed1f31e5..80666a81c50 100644
--- a/sql/sql_insert.h
+++ b/sql/sql_insert.h
@@ -23,23 +23,28 @@
typedef List<Item> List_item;
typedef struct st_copy_info COPY_INFO;
-bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
- List<Item> &fields, List_item *values,
- List<Item> &update_fields,
- List<Item> &update_values, enum_duplicates duplic,
- COND **where, bool select_insert);
+int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
+ List<Item> &fields, List_item *values,
+ List<Item> &update_fields,
+ List<Item> &update_values, enum_duplicates duplic,
+ COND **where, bool select_insert);
bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
List<List_item> &values, List<Item> &update_fields,
List<Item> &update_values, enum_duplicates flag,
- bool ignore);
+ bool ignore, select_result* result);
void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
enum_duplicates duplic,
bool is_multi_insert);
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
int vers_insert_history_row(TABLE *table);
-int write_record(THD *thd, TABLE *table, COPY_INFO *info);
+int check_duplic_insert_without_overlaps(THD *thd, TABLE *table,
+ enum_duplicates duplic);
+int write_record(THD *thd, TABLE *table, COPY_INFO *info,
+ select_result *returning= NULL);
void kill_delayed_threads(void);
+bool binlog_create_table(THD *thd, TABLE *table, bool replace);
+bool binlog_drop_table(THD *thd, TABLE *table);
#ifdef EMBEDDED_LIBRARY
inline void kill_delayed_threads(void) {}
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index e9ad53850dd..5a243cd0a6d 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -937,7 +937,8 @@ int JOIN_CACHE::alloc_buffer()
{
size_t next_buff_size;
- if ((buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))))
+ if ((buff= (uchar*) my_malloc(key_memory_JOIN_CACHE, buff_size,
+ MYF(MY_THREAD_SPECIFIC))))
break;
next_buff_size= buff_size > buff_size_decr ? buff_size-buff_size_decr : 0;
@@ -1013,11 +1014,11 @@ bool JOIN_CACHE::shrink_join_buffer_in_ratio(ulonglong n, ulonglong d)
int JOIN_CACHE::realloc_buffer()
{
- int rc;
free();
- rc= MY_TEST(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))));
+ buff= (uchar*) my_malloc(key_memory_JOIN_CACHE, buff_size,
+ MYF(MY_THREAD_SPECIFIC));
reset(TRUE);
- return rc;
+ return buff == NULL;
}
@@ -2084,8 +2085,13 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last)
if (!join_tab->first_unmatched)
{
+ bool pfs_batch_update= join_tab->pfs_batch_update(join);
+ if (pfs_batch_update)
+ join_tab->table->file->start_psi_batch_mode();
/* Find all records from join_tab that match records from join buffer */
rc= join_matching_records(skip_last);
+ if (pfs_batch_update)
+ join_tab->table->file->end_psi_batch_mode();
if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
goto finish;
if (outer_join_first_inner)
@@ -2811,12 +2817,12 @@ int JOIN_CACHE_HASHED::init_hash_table()
int JOIN_CACHE_HASHED::realloc_buffer()
{
- int rc;
free();
- rc= MY_TEST(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))));
+ buff= (uchar*) my_malloc(key_memory_JOIN_CACHE, buff_size,
+ MYF(MY_THREAD_SPECIFIC));
init_hash_table();
reset(TRUE);
- return rc;
+ return buff == NULL;
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index cc66ae6b853..61e3b89c9a3 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -52,6 +52,540 @@ const LEX_CSTRING empty_clex_str= {"", 0};
const LEX_CSTRING star_clex_str= {"*", 1};
const LEX_CSTRING param_clex_str= {"?", 1};
+
+/**
+ Helper action for a case expression statement (the expr in 'CASE expr').
+ This helper is used for 'searched' cases only.
+ @param lex the parser lex context
+ @param expr the parsed expression
+ @return 0 on success
+*/
+
+int sp_expr_lex::case_stmt_action_expr()
+{
+ int case_expr_id= spcont->register_case_expr();
+ sp_instr_set_case_expr *i;
+
+ if (spcont->push_case_expr_id(case_expr_id))
+ return 1;
+
+ i= new (thd->mem_root)
+ sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id,
+ get_item(), this);
+
+ sphead->add_cont_backpatch(i);
+ return sphead->add_instr(i);
+}
+
+/**
+ Helper action for a case when condition.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+ @param when the parsed expression for the WHEN clause
+ @param simple true for simple cases, false for searched cases
+*/
+
+int sp_expr_lex::case_stmt_action_when(bool simple)
+{
+ uint ip= sphead->instructions();
+ sp_instr_jump_if_not *i;
+ Item_case_expr *var;
+ Item *expr;
+
+ if (simple)
+ {
+ var= new (thd->mem_root)
+ Item_case_expr(thd, spcont->get_current_case_expr_id());
+
+#ifdef DBUG_ASSERT_EXISTS
+ if (var)
+ {
+ var->m_sp= sphead;
+ }
+#endif
+
+ expr= new (thd->mem_root) Item_func_eq(thd, var, get_item());
+ i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, expr, this);
+ }
+ else
+ i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, get_item(), this);
+
+ /*
+ BACKPATCH: Registering forward jump from
+ "case_stmt_action_when" to "case_stmt_action_then"
+ (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
+ */
+
+ return
+ !MY_TEST(i) ||
+ sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0)) ||
+ sphead->add_cont_backpatch(i) ||
+ sphead->add_instr(i);
+}
+
+/**
+ Helper action for a case then statements.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+*/
+
+int LEX::case_stmt_action_then()
+{
+ uint ip= sphead->instructions();
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont);
+ if (!MY_TEST(i) || sphead->add_instr(i))
+ return 1;
+
+ /*
+ BACKPATCH: Resolving forward jump from
+ "case_stmt_action_when" to "case_stmt_action_then"
+ (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
+ */
+
+ sphead->backpatch(spcont->pop_label());
+
+ /*
+ BACKPATCH: Registering forward jump from
+ "case_stmt_action_then" to after END CASE
+ (jump from instruction 4 to 12, 7 to 12 ... in the example)
+ */
+
+ return sphead->push_backpatch(thd, i, spcont->last_label());
+}
+
+
+/**
+ Helper action for a SET statement.
+ Used to push a system variable into the assignment list.
+
+ @param tmp the system variable with base name
+ @param var_type the scope of the variable
+ @param val the value being assigned to the variable
+
+ @return TRUE if error, FALSE otherwise.
+*/
+
+bool
+LEX::set_system_variable(enum enum_var_type var_type,
+ sys_var *sysvar, const Lex_ident_sys_st *base_name,
+ Item *val)
+{
+ set_var *setvar;
+
+ /* No AUTOCOMMIT from a stored function or trigger. */
+ if (spcont && sysvar == Sys_autocommit_ptr)
+ sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+
+ if (val && val->type() == Item::FIELD_ITEM &&
+ ((Item_field*)val)->table_name.str)
+ {
+ my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str);
+ return TRUE;
+ }
+
+ if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar,
+ base_name, val)))
+ return TRUE;
+
+ return var_list.push_back(setvar, thd->mem_root);
+}
+
+
+/**
+ Helper action for a SET statement.
+ Used to SET a field of NEW row.
+
+ @param name the field name
+ @param val the value being assigned to the row
+
+ @return TRUE if error, FALSE otherwise.
+*/
+
+bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val)
+{
+ Item_trigger_field *trg_fld;
+ sp_instr_set_trigger_field *sp_fld;
+
+ /* QQ: Shouldn't this be field's default value ? */
+ if (! val)
+ val= new (thd->mem_root) Item_null(thd);
+
+ DBUG_ASSERT(trg_chistics.action_time == TRG_ACTION_BEFORE &&
+ (trg_chistics.event == TRG_EVENT_INSERT ||
+ trg_chistics.event == TRG_EVENT_UPDATE));
+
+ trg_fld= new (thd->mem_root)
+ Item_trigger_field(thd, current_context(),
+ Item_trigger_field::NEW_ROW,
+ *name, UPDATE_ACL, FALSE);
+
+ if (unlikely(trg_fld == NULL))
+ return TRUE;
+
+ sp_fld= new (thd->mem_root)
+ sp_instr_set_trigger_field(sphead->instructions(),
+ spcont, trg_fld, val, this);
+
+ if (unlikely(sp_fld == NULL))
+ return TRUE;
+
+ /*
+ Let us add this item to list of all Item_trigger_field
+ objects in trigger.
+ */
+ trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field);
+
+ return sphead->add_instr(sp_fld);
+}
+
+
+/**
+ Create an object to represent a SP variable in the Item-hierarchy.
+
+ @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.
+*/
+Item_splocal*
+LEX::create_item_for_sp_var(const Lex_ident_cli_st *cname, sp_variable *spvar)
+{
+ const Sp_rcontext_handler *rh;
+ Item_splocal *item;
+ const char *start_in_q= cname->pos();
+ const char *end_in_q= cname->end();
+ uint pos_in_q, len_in_q;
+ Lex_ident_sys name(thd, cname);
+
+ if (name.is_null())
+ return NULL; // EOM
+
+ /* If necessary, look for the variable. */
+ if (spcont && !spvar)
+ spvar= find_variable(&name, &rh);
+
+ if (!spvar)
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str);
+ return NULL;
+ }
+
+ DBUG_ASSERT(spcont && spvar);
+
+ /* Position and length of the SP variable name in the query. */
+ pos_in_q= (uint)(start_in_q - sphead->m_tmp_query);
+ len_in_q= (uint)(end_in_q - start_in_q);
+
+ item= new (thd->mem_root)
+ Item_splocal(thd, rh, &name, spvar->offset, spvar->type_handler(),
+ pos_in_q, len_in_q);
+
+#ifdef DBUG_ASSERT_EXISTS
+ if (item)
+ item->m_sp= 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
+ <code>left [NOT] IN ( expr )</code>
+ @param thd the current thread
+ @param left the in predicand
+ @param equal true for IN predicates, false for NOT IN predicates
+ @param expr first and only expression of the in value list
+ @return an expression representing the IN predicate.
+*/
+Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
+ Item *expr)
+{
+ /*
+ Relevant references for this issue:
+ - SQL:2003, Part 2, section 8.4 <in predicate>, page 383,
+ - SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
+ - SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
+ - SQL:2003, Part 2, section 7.15 <subquery>, page 370,
+ - SQL:2003 Feature F561, "Full value expressions".
+
+ The exception in SQL:2003 Note 184 means:
+ Item_singlerow_subselect, which corresponds to a <scalar subquery>,
+ should be re-interpreted as an Item_in_subselect, which corresponds
+ to a <table subquery> when used inside an <in predicate>.
+
+ Our reading of Note 184 is reccursive, so that all:
+ - IN (( <subquery> ))
+ - IN ((( <subquery> )))
+ - IN '('^N <subquery> ')'^N
+ - etc
+ should be interpreted as a <table subquery>, no matter how deep in the
+ expression the <subquery> is.
+ */
+
+ Item *result;
+
+ DBUG_ENTER("handle_sql2003_note184_exception");
+
+ if (expr->type() == Item::SUBSELECT_ITEM)
+ {
+ Item_subselect *expr2 = (Item_subselect*) expr;
+
+ if (expr2->substype() == Item_subselect::SINGLEROW_SUBS)
+ {
+ Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2;
+ st_select_lex *subselect;
+
+ /*
+ Implement the mandated change, by altering the semantic tree:
+ left IN Item_singlerow_subselect(subselect)
+ is modified to
+ left IN (subselect)
+ which is represented as
+ Item_in_subselect(left, subselect)
+ */
+ subselect= expr3->invalidate_and_restore_select_lex();
+ result= new (thd->mem_root) Item_in_subselect(thd, left, subselect);
+
+ if (! equal)
+ result = negate_expression(thd, result);
+
+ DBUG_RETURN(result);
+ }
+ }
+
+ if (equal)
+ result= new (thd->mem_root) Item_func_eq(thd, left, expr);
+ else
+ result= new (thd->mem_root) Item_func_ne(thd, left, expr);
+
+ DBUG_RETURN(result);
+}
+
+/**
+ 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 pos The position in the raw SQL buffer
+*/
+
+
+bool sp_create_assignment_lex(THD *thd, const char *pos)
+{
+ if (thd->lex->sphead)
+ {
+ sp_lex_local *new_lex;
+ if (!(new_lex= new (thd->mem_root) sp_lex_set_var(thd, thd->lex)) ||
+ new_lex->main_select_push())
+ return true;
+ new_lex->sphead->m_tmp_query= pos;
+ return thd->lex->sphead->reset_lex(thd, new_lex);
+ }
+ else
+ if (thd->lex->main_select_push(false))
+ return true;
+ return false;
+}
+
+
+/**
+ 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
+ @param need_set_keyword - if a SET statement "SET a=10",
+ or a direct assignment overwise "a:=10"
+ @return false if success, true otherwise.
+*/
+
+bool sp_create_assignment_instr(THD *thd, bool no_lookahead,
+ bool need_set_keyword)
+{
+ LEX *lex= thd->lex;
+
+ if (lex->sphead)
+ {
+ if (!lex->var_list.is_empty())
+ {
+ /*
+ - Every variable assignment from the same SET command, e.g.:
+ SET @var1=expr1, @var2=expr2;
+ produce each own sp_create_assignment_instr() call
+ lex->var_list.elements is 1 in this case.
+ - This query:
+ SET TRANSACTION READ ONLY, ISOLATION LEVEL SERIALIZABLE;
+ in translated to:
+ SET tx_read_only=1, tx_isolation=ISO_SERIALIZABLE;
+ but produces a single sp_create_assignment_instr() call
+ which includes the query fragment covering both options.
+ */
+ DBUG_ASSERT(lex->var_list.elements >= 1 && lex->var_list.elements <= 2);
+ /*
+ sql_mode=ORACLE's direct assignment of a global variable
+ is not possible by the grammar.
+ */
+ DBUG_ASSERT(lex->option_type != OPT_GLOBAL || need_set_keyword);
+ /*
+ We have assignment to user or system variable or
+ option setting, so we should construct sp_instr_stmt
+ for it.
+ */
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lip->ptr, if there was no lookahead,
+ lip->tok_end otherwise.
+ */
+ static const LEX_CSTRING setlc= { STRING_WITH_LEN("SET ") };
+ static const LEX_CSTRING setgl= { STRING_WITH_LEN("SET GLOBAL ") };
+ const char *qend= no_lookahead ? lip->get_ptr() : lip->get_tok_end();
+ Lex_cstring qbuf(lex->sphead->m_tmp_query, qend);
+ if (lex->new_sp_instr_stmt(thd,
+ lex->option_type == OPT_GLOBAL ? setgl :
+ need_set_keyword ? setlc :
+ null_clex_str,
+ qbuf))
+ return true;
+ }
+ lex->pop_select();
+ if (lex->check_main_unit_semantics())
+ {
+ /*
+ "lex" can be referrenced by:
+ - sp_instr_set SET a= expr;
+ - sp_instr_set_row_field SET r.a= expr;
+ - sp_instr_stmt (just generated above) SET @a= expr;
+ In this case, "lex" is fully owned by sp_instr_xxx and it will
+ be deleted by the destructor ~sp_instr_xxx().
+ So we should remove "lex" from the stack sp_head::m_lex,
+ to avoid double free.
+ Note, in case "lex" is not owned by any sp_instr_xxx,
+ it's also safe to remove it from the stack right now.
+ So we can remove it unconditionally, without testing lex->sp_lex_in_use.
+ */
+ lex->sphead->restore_lex(thd);
+ 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;
+ }
+ else
+ lex->pop_select();
+ return false;
+}
+
+
+void LEX::add_key_to_list(LEX_CSTRING *field_name,
+ enum Key::Keytype type, bool check_exists)
+{
+ Key *key;
+ MEM_ROOT *mem_root= thd->mem_root;
+ key= new (mem_root)
+ Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, false,
+ DDL_options(check_exists ?
+ DDL_options::OPT_IF_NOT_EXISTS :
+ DDL_options::OPT_NONE));
+ key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0),
+ mem_root);
+ alter_info.key_list.push_back(key, mem_root);
+}
+
+
+bool LEX::add_alter_list(LEX_CSTRING name, Virtual_column_info *expr,
+ bool exists)
+{
+ MEM_ROOT *mem_root= thd->mem_root;
+ Alter_column *ac= new (mem_root) Alter_column(name, expr, exists);
+ if (unlikely(ac == NULL))
+ return true;
+ alter_info.alter_list.push_back(ac, mem_root);
+ alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
+ return false;
+}
+
+
+bool LEX::add_alter_list(LEX_CSTRING name, LEX_CSTRING new_name, bool exists)
+{
+ Alter_column *ac= new (thd->mem_root) Alter_column(name, new_name, exists);
+ if (unlikely(ac == NULL))
+ return true;
+ alter_info.alter_list.push_back(ac, thd->mem_root);
+ alter_info.flags|= ALTER_RENAME_COLUMN;
+ return false;
+}
+
+
+void LEX::init_last_field(Column_definition *field,
+ const LEX_CSTRING *field_name,
+ const CHARSET_INFO *cs)
+{
+ last_field= field;
+
+ field->field_name= *field_name;
+
+ /* reset LEX fields that are used in Create_field::set_and_check() */
+ charset= cs;
+}
+
+
+bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
+{
+ /*
+ if charset is NULL - we're parsing a field declaration.
+ we cannot call find_bin_collation for a field here, because actual
+ field charset is determined in get_sql_field_charset() much later.
+ so we only set a flag.
+ */
+ if (!charset)
+ {
+ charset= cs;
+ last_field->flags|= bin ? BINCMP_FLAG : 0;
+ return false;
+ }
+
+ charset= bin ? find_bin_collation(cs ? cs : charset)
+ : cs ? cs : charset;
+ return charset == NULL;
+}
+
+
+Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
+{
+ Virtual_column_info *v= new (thd->mem_root) Virtual_column_info();
+ if (unlikely(!v))
+ return 0;
+ v->expr= expr;
+ v->utf8= 0; /* connection charset */
+ return v;
+}
+
+
+
/**
@note The order of the elements of this array must correspond to
the order of elements in enum_binlog_stmt_unsafe.
@@ -351,7 +885,7 @@ size_t Lex_input_stream::get_body_utf8_maximum_length(THD *thd)
"2" should be a reasonable multiplier that safely covers escaping needs.
*/
return (m_buf_length / thd->variables.character_set_client->mbminlen) *
- my_charset_utf8_bin.mbmaxlen * 2/*for escaping*/;
+ my_charset_utf8mb3_bin.mbmaxlen * 2/*for escaping*/;
}
@@ -456,14 +990,14 @@ extern "C" {
@param end - the end of the destination string
@returns - a code according to the wc_mb() convension.
*/
-int my_wc_mb_utf8_with_escape(CHARSET_INFO *cs, my_wc_t escape, my_wc_t wc,
- uchar *str, uchar *end)
+int my_wc_mb_utf8mb3_with_escape(CHARSET_INFO *cs, my_wc_t escape, my_wc_t wc,
+ uchar *str, uchar *end)
{
DBUG_ASSERT(escape > 0);
if (str + 1 >= end)
return MY_CS_TOOSMALL2; // Not enough space, need at least two bytes.
*str= (uchar)escape;
- int cnvres= my_charset_utf8_handler.wc_mb(cs, wc, str + 1, end);
+ int cnvres= my_charset_utf8mb3_handler.wc_mb(cs, wc, str + 1, end);
if (cnvres > 0)
return cnvres + 1; // The character was normally put
if (cnvres == MY_CS_ILUNI)
@@ -485,12 +1019,12 @@ int my_wc_mb_utf8_with_escape(CHARSET_INFO *cs, my_wc_t escape, my_wc_t wc,
@param end - the end of the destination string
@returns - a code according to the wc_mb() conversion.
*/
-int my_wc_mb_utf8_opt_escape(CHARSET_INFO *cs,
- my_wc_t wc, my_wc_t escape, my_wc_t ewc,
- uchar *str, uchar *end)
+int my_wc_mb_utf8mb3_opt_escape(CHARSET_INFO *cs,
+ my_wc_t wc, my_wc_t escape, my_wc_t ewc,
+ uchar *str, uchar *end)
{
- return escape ? my_wc_mb_utf8_with_escape(cs, escape, ewc, str, end) :
- my_charset_utf8_handler.wc_mb(cs, wc, str, end);
+ return escape ? my_wc_mb_utf8mb3_with_escape(cs, escape, ewc, str, end) :
+ my_charset_utf8mb3_handler.wc_mb(cs, wc, str, end);
}
/**
@@ -509,54 +1043,55 @@ int my_wc_mb_utf8_opt_escape(CHARSET_INFO *cs,
@param escape - the escape character (backslash, or 0)
@returns - a code according to the wc_mb() convension.
*/
-int my_wc_mb_utf8_escape(CHARSET_INFO *cs, my_wc_t wc, uchar *str, uchar *end,
- my_wc_t sep, my_wc_t escape)
+int my_wc_mb_utf8mb3_escape(CHARSET_INFO *cs, my_wc_t wc,
+ uchar *str, uchar *end,
+ my_wc_t sep, my_wc_t escape)
{
DBUG_ASSERT(escape == 0 || escape == '\\');
DBUG_ASSERT(sep == '"' || sep == '\'');
switch (wc) {
- case 0: return my_wc_mb_utf8_opt_escape(cs, wc, escape, '0', str, end);
- case '\t': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 't', str, end);
- case '\r': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 'r', str, end);
- case '\n': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 'n', str, end);
- case '\032': return my_wc_mb_utf8_opt_escape(cs, wc, escape, 'Z', str, end);
+ case 0: return my_wc_mb_utf8mb3_opt_escape(cs, wc, escape, '0', str, end);
+ case '\t': return my_wc_mb_utf8mb3_opt_escape(cs, wc, escape, 't', str, end);
+ case '\r': return my_wc_mb_utf8mb3_opt_escape(cs, wc, escape, 'r', str, end);
+ case '\n': return my_wc_mb_utf8mb3_opt_escape(cs, wc, escape, 'n', str, end);
+ case '\032': return my_wc_mb_utf8mb3_opt_escape(cs, wc, escape, 'Z', str, end);
case '\'':
case '\"':
if (wc == sep)
- return my_wc_mb_utf8_with_escape(cs, wc, wc, str, end);
+ return my_wc_mb_utf8mb3_with_escape(cs, wc, wc, str, end);
}
- return my_charset_utf8_handler.wc_mb(cs, wc, str, end); // No escaping needed
+ return my_charset_utf8mb3_handler.wc_mb(cs, wc, str, end); // No escaping needed
}
/** wc_mb() compatible routines for all sql_mode and delimiter combinations */
-int my_wc_mb_utf8_escape_single_quote_and_backslash(CHARSET_INFO *cs,
+int my_wc_mb_utf8mb3_escape_single_quote_and_backslash(CHARSET_INFO *cs,
my_wc_t wc,
uchar *str, uchar *end)
{
- return my_wc_mb_utf8_escape(cs, wc, str, end, '\'', '\\');
+ return my_wc_mb_utf8mb3_escape(cs, wc, str, end, '\'', '\\');
}
-int my_wc_mb_utf8_escape_double_quote_and_backslash(CHARSET_INFO *cs,
+int my_wc_mb_utf8mb3_escape_double_quote_and_backslash(CHARSET_INFO *cs,
my_wc_t wc,
uchar *str, uchar *end)
{
- return my_wc_mb_utf8_escape(cs, wc, str, end, '"', '\\');
+ return my_wc_mb_utf8mb3_escape(cs, wc, str, end, '"', '\\');
}
-int my_wc_mb_utf8_escape_single_quote(CHARSET_INFO *cs, my_wc_t wc,
+int my_wc_mb_utf8mb3_escape_single_quote(CHARSET_INFO *cs, my_wc_t wc,
uchar *str, uchar *end)
{
- return my_wc_mb_utf8_escape(cs, wc, str, end, '\'', 0);
+ return my_wc_mb_utf8mb3_escape(cs, wc, str, end, '\'', 0);
}
-int my_wc_mb_utf8_escape_double_quote(CHARSET_INFO *cs, my_wc_t wc,
+int my_wc_mb_utf8mb3_escape_double_quote(CHARSET_INFO *cs, my_wc_t wc,
uchar *str, uchar *end)
{
- return my_wc_mb_utf8_escape(cs, wc, str, end, '"', 0);
+ return my_wc_mb_utf8mb3_escape(cs, wc, str, end, '"', 0);
}
}; // End of extern "C"
@@ -570,10 +1105,10 @@ my_charset_conv_wc_mb
Lex_input_stream::get_escape_func(THD *thd, my_wc_t sep) const
{
return thd->backslash_escapes() ?
- (sep == '"' ? my_wc_mb_utf8_escape_double_quote_and_backslash:
- my_wc_mb_utf8_escape_single_quote_and_backslash) :
- (sep == '"' ? my_wc_mb_utf8_escape_double_quote:
- my_wc_mb_utf8_escape_single_quote);
+ (sep == '"' ? my_wc_mb_utf8mb3_escape_double_quote_and_backslash:
+ my_wc_mb_utf8mb3_escape_single_quote_and_backslash) :
+ (sep == '"' ? my_wc_mb_utf8mb3_escape_double_quote:
+ my_wc_mb_utf8mb3_escape_single_quote);
}
@@ -613,7 +1148,7 @@ void Lex_input_stream::body_utf8_append_escape(THD *thd,
DBUG_ASSERT(m_body_utf8 + get_body_utf8_maximum_length(thd) >=
m_body_utf8_ptr + txt->length * 2);
uint32 cnv_length= my_convert_using_func(m_body_utf8_ptr, txt->length * 2,
- &my_charset_utf8_general_ci,
+ &my_charset_utf8mb3_general_ci,
get_escape_func(thd, sep),
txt->str, txt->length,
cs, cs->cset->mb_wc,
@@ -706,7 +1241,6 @@ void LEX::start(THD *thd_arg)
set_var_list.empty();
param_list.empty();
view_list.empty();
- with_column_list.empty();
with_persistent_for_clause= FALSE;
column_list= NULL;
index_list= NULL;
@@ -946,6 +1480,9 @@ bool is_native_function(THD *thd, const LEX_CSTRING *name)
if (is_lex_native_function(name))
return true;
+ if (Type_handler::handler_by_name(thd, *name))
+ return true;
+
return false;
}
@@ -1015,7 +1552,7 @@ my_unescape(CHARSET_INFO *cs, char *to, const char *str, const char *end,
{
#ifdef USE_MB
int l;
- if (use_mb(cs) && (l= my_ismbchar(cs, str, end)))
+ if (cs->use_mb() && (l= my_ismbchar(cs, str, end)))
{
while (l--)
*to++ = *str++;
@@ -1093,7 +1630,7 @@ bool Lex_input_stream::get_text(Lex_string_with_metadata_st *dst, uint sep,
#ifdef USE_MB
{
int l;
- if (use_mb(cs) &&
+ if (cs->use_mb() &&
(l = my_ismbchar(cs,
get_ptr() -1,
get_end_of_query()))) {
@@ -2039,7 +2576,7 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
next_state= MY_LEX_HOSTNAME;
break;
}
- yylval->lex_str.str= (char*) get_ptr();
+ yylval->lex_str.str= (char*) get_ptr() - 1;
yylval->lex_str.length= 1;
return((int) '@');
case MY_LEX_HOSTNAME: // end '@' of user@hostname
@@ -2137,12 +2674,12 @@ int Lex_input_stream::scan_ident_start(THD *thd, Lex_ident_cli_st *str)
const uchar *const ident_map= cs->ident_map;
DBUG_ASSERT(m_tok_start <= m_ptr);
- if (use_mb(cs))
+ if (cs->use_mb())
{
is_8bit= true;
while (ident_map[c= yyGet()])
{
- int char_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ int char_length= cs->charlen(get_ptr() - 1, get_end_of_query());
if (char_length <= 0)
break;
skip_binary(char_length - 1);
@@ -2180,10 +2717,10 @@ int Lex_input_stream::scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
bool resolve_introducer= true;
DBUG_ASSERT(m_ptr == m_tok_start + 1); // m_ptr points to the second byte
- if (use_mb(cs))
+ if (cs->use_mb())
{
is_8bit= true;
- int char_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ int char_length= cs->charlen(get_ptr() - 1, get_end_of_query());
if (char_length <= 0)
{
*st= MY_LEX_CHAR;
@@ -2193,7 +2730,7 @@ int Lex_input_stream::scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
while (ident_map[c= yyGet()])
{
- char_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ char_length= cs->charlen(get_ptr() - 1, get_end_of_query());
if (char_length <= 0)
break;
if (char_length > 1 || (c & 0x80))
@@ -2291,7 +2828,7 @@ int Lex_input_stream::scan_ident_delimited(THD *thd,
m_ptr= (char *) m_tok_start + 1;
return quote_char;
}
- int var_length= my_charlen(cs, get_ptr() - 1, get_end_of_query());
+ int var_length= cs->charlen(get_ptr() - 1, get_end_of_query());
if (var_length == 1)
{
if (c == quote_char)
@@ -2370,10 +2907,10 @@ void st_select_lex_unit::init_query()
{
init_query_common();
set_linkage(GLOBAL_OPTIONS_TYPE);
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
+ lim.set_unlimited();
union_distinct= 0;
prepared= optimized= optimized_2= executed= 0;
+ bag_set_op_optimized= 0;
optimize_started= 0;
item= 0;
union_result= 0;
@@ -2389,8 +2926,8 @@ void st_select_lex_unit::init_query()
with_clause= 0;
with_element= 0;
columns_are_renamed= false;
- intersect_mark= NULL;
with_wrapped_tvc= false;
+ have_except_all_or_intersect_all= false;
}
void st_select_lex::init_query()
@@ -2490,6 +3027,7 @@ void st_select_lex::init_select()
curr_tvc_name= 0;
in_tvc= false;
versioned_tables= 0;
+ nest_flags= 0;
}
/*
@@ -2628,7 +3166,7 @@ st_select_lex_node *st_select_lex_node:: insert_chain_before(
{
end_chain_node->link_next= *ptr_pos_to_insert;
(*ptr_pos_to_insert)->link_prev= &end_chain_node->link_next;
- this->link_prev= ptr_pos_to_insert;
+ link_prev= ptr_pos_to_insert;
return this;
}
@@ -2814,7 +3352,7 @@ bool st_select_lex::mark_as_dependent(THD *thd, st_select_lex *last,
return TRUE;
} while ((s= s->outer_select()) != last && s != 0);
is_correlated= TRUE;
- this->master_unit()->item->is_correlated= TRUE;
+ master_unit()->item->is_correlated= TRUE;
return FALSE;
}
@@ -3007,11 +3545,77 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
}
+/*
+ @brief
+ Print the whole statement
+
+ @param str Print into this string
+ @param query_type Flags describing how to print
+
+ @detail
+ The intent is to allow to eventually print back any query.
+
+ This is useful e.g. for storage engines that take over diferrent kinds of
+ queries
+*/
+
+void LEX::print(String *str, enum_query_type query_type)
+{
+ if (sql_command == SQLCOM_UPDATE)
+ {
+ SELECT_LEX *sel= first_select_lex();
+ str->append(STRING_WITH_LEN("UPDATE "));
+ if (ignore)
+ str->append(STRING_WITH_LEN("IGNORE "));
+ // table name
+ str->append(query_tables->alias);
+ str->append(STRING_WITH_LEN(" SET "));
+ // print item assignments
+ List_iterator<Item> it(sel->item_list);
+ List_iterator<Item> it2(value_list);
+ Item *col_ref, *value;
+ bool first= true;
+ while ((col_ref= it++) && (value= it2++))
+ {
+ if (first)
+ first= false;
+ else
+ str->append(STRING_WITH_LEN(", "));
+ col_ref->print(str, query_type);
+ str->append(STRING_WITH_LEN("="));
+ value->print(str, query_type);
+ }
+
+ if (sel->where)
+ {
+ str->append(STRING_WITH_LEN(" WHERE "));
+ sel->where->print(str, query_type);
+ }
+
+ if (sel->order_list.elements)
+ {
+ str->append(STRING_WITH_LEN(" ORDER BY "));
+ for (ORDER *ord= sel->order_list.first; ord; ord= ord->next)
+ {
+ if (ord != sel->order_list.first)
+ str->append(STRING_WITH_LEN(", "));
+ (*ord->item)->print(str, query_type);
+ }
+ }
+ if (sel->select_limit)
+ {
+ str->append(STRING_WITH_LEN(" LIMIT "));
+ sel->select_limit->print(str, query_type);
+ }
+ }
+ else
+ DBUG_ASSERT(0); // Not implemented yet
+}
+
void st_select_lex_unit::print(String *str, enum_query_type query_type)
{
- bool union_all= !union_distinct;
if (with_clause)
- with_clause->print(str, query_type);
+ with_clause->print(thd, str, query_type);
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
if (sl != first_select())
@@ -3023,8 +3627,6 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type)
/* fall through */
case UNION_TYPE:
str->append(STRING_WITH_LEN(" union "));
- if (union_all)
- str->append(STRING_WITH_LEN("all "));
break;
case INTERSECT_TYPE:
str->append(STRING_WITH_LEN(" intersect "));
@@ -3033,8 +3635,8 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN(" except "));
break;
}
- if (sl == union_distinct)
- union_all= TRUE;
+ if (!sl->distinct)
+ str->append(STRING_WITH_LEN("all "));
}
if (sl->braces)
str->append('(');
@@ -3258,12 +3860,12 @@ LEX::LEX()
default_used(0), is_lex_started(0), limit_rows_examined_cnt(ULONGLONG_MAX)
{
- init_dynamic_array2(&plugins, sizeof(plugin_ref), plugins_static_buffer,
- INITIAL_LEX_PLUGIN_LIST_SIZE,
+ init_dynamic_array2(PSI_INSTRUMENT_ME, &plugins, sizeof(plugin_ref),
+ plugins_static_buffer, INITIAL_LEX_PLUGIN_LIST_SIZE,
INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
reset_query_tables_list(TRUE);
mi.init();
- init_dynamic_array2(&delete_gtid_domain, sizeof(uint32),
+ init_dynamic_array2(PSI_INSTRUMENT_ME, &delete_gtid_domain, sizeof(uint32),
gtid_domain_static_buffer,
initial_gtid_domain_buffer_size,
initial_gtid_domain_buffer_size, 0);
@@ -3520,12 +4122,7 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
- offset_limit_cnt= sl->get_offset();
- select_limit_cnt= sl->get_limit();
- if (select_limit_cnt + offset_limit_cnt >= select_limit_cnt)
- select_limit_cnt+= offset_limit_cnt;
- else
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_limit(sl->get_limit(), sl->get_offset());
}
@@ -3549,6 +4146,8 @@ bool st_select_lex_unit::union_needs_tmp_table()
with_wrapped_tvc= true;
break;
}
+ if (sl != first_select() && sl->linkage != UNION_TYPE)
+ return true;
}
}
if (with_wrapped_tvc)
@@ -3918,7 +4517,7 @@ void LEX::reset_n_backup_query_tables_list(Query_tables_list *backup)
We have to perform full initialization here since otherwise we
will damage backed up state.
*/
- this->reset_query_tables_list(TRUE);
+ reset_query_tables_list(TRUE);
}
@@ -3932,8 +4531,8 @@ void LEX::reset_n_backup_query_tables_list(Query_tables_list *backup)
void LEX::restore_backup_query_tables_list(Query_tables_list *backup)
{
- this->destroy_query_tables_list();
- this->set_query_tables_list(backup);
+ destroy_query_tables_list();
+ set_query_tables_list(backup);
}
@@ -4164,7 +4763,7 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
}
if (subquery_predicate->substype() == Item_subselect::IN_SUBS)
{
- Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate;
+ Item_in_subselect *in_subs= subquery_predicate->get_IN_subquery();
if (in_subs->is_jtbm_merged)
continue;
}
@@ -4592,7 +5191,7 @@ void SELECT_LEX::update_used_tables()
*/
if (tl->jtbm_subselect)
{
- Item *left_expr= tl->jtbm_subselect->left_expr;
+ Item *left_expr= tl->jtbm_subselect->left_exp();
left_expr->walk(&Item::update_table_bitmaps_processor, FALSE, NULL);
}
@@ -4749,7 +5348,7 @@ void st_select_lex::set_explain_type(bool on_the_fly)
if ((parent_item= master_unit()->item) &&
parent_item->substype() == Item_subselect::IN_SUBS)
{
- Item_in_subselect *in_subs= (Item_in_subselect*)parent_item;
+ Item_in_subselect *in_subs= parent_item->get_IN_subquery();
/*
Surprisingly, in_subs->is_set_strategy() can return FALSE here,
even for the last invocation of this function for the select.
@@ -5038,9 +5637,10 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
sl=sl->outer_select())
{
Item *subs= sl->master_unit()->item;
- if (subs && subs->type() == Item::SUBSELECT_ITEM &&
+ Item_in_subselect *in_subs= (subs ? subs->get_IN_subquery() : NULL);
+ if (in_subs &&
((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS &&
- ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN))
+ in_subs->test_strategy(SUBS_SEMI_JOIN))
{
continue;
}
@@ -5097,8 +5697,8 @@ bool LEX::set_arena_for_set_stmt(Query_arena *backup)
mem_root_for_set_stmt= new MEM_ROOT();
if (unlikely(!(mem_root_for_set_stmt)))
DBUG_RETURN(1);
- init_sql_alloc(mem_root_for_set_stmt, "set_stmt",
- ALLOC_ROOT_SET, ALLOC_ROOT_SET, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(PSI_INSTRUMENT_ME, mem_root_for_set_stmt, ALLOC_ROOT_SET,
+ ALLOC_ROOT_SET, MYF(MY_THREAD_SPECIFIC));
}
if (unlikely(!(arena_for_set_stmt= new(mem_root_for_set_stmt)
Query_arena_memroot(mem_root_for_set_stmt,
@@ -5225,7 +5825,7 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
/*
Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we
- dont ever set EXPLAIN_NODE_NON_MERGED_SJ.
+ don't ever set EXPLAIN_NODE_NON_MERGED_SJ.
*/
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
eu->add_select(sl->select_number);
@@ -5256,10 +5856,8 @@ int st_select_lex_unit::save_union_explain_part2(Explain_query *output)
for (SELECT_LEX_UNIT *unit= fake_select_lex->first_inner_unit();
unit; unit= unit->next_unit())
{
- if (!(unit->item && unit->item->eliminated))
- {
+ if (unit->explainable())
eu->add_child(unit->first_select()->select_number);
- }
}
fake_select_lex->join->explain= &eu->fake_select_lex_explain;
}
@@ -5420,7 +6018,7 @@ LEX::wrap_unit_into_derived(SELECT_LEX_UNIT *unit)
Name_resolution_context *context= &wrapping_sel->context;
context->init();
wrapping_sel->automatic_brackets= FALSE;
-
+ wrapping_sel->mark_as_unit_nest();
wrapping_sel->register_unit(unit, context);
/* stuff dummy SELECT * FROM (...) */
@@ -5430,8 +6028,7 @@ LEX::wrap_unit_into_derived(SELECT_LEX_UNIT *unit)
/* add SELECT list*/
{
- Item *item= new (thd->mem_root)
- Item_field(thd, context, NULL, NULL, &star_clex_str);
+ Item *item= new (thd->mem_root) Item_field(thd, context, star_clex_str);
if (item == NULL)
goto err;
if (add_item_to_list(thd, item))
@@ -5493,8 +6090,7 @@ SELECT_LEX *LEX::wrap_select_chain_into_derived(SELECT_LEX *sel)
/* add SELECT list*/
{
- Item *item= new (thd->mem_root)
- Item_field(thd, context, NULL, NULL, &star_clex_str);
+ Item *item= new (thd->mem_root) Item_field(thd, context, star_clex_str);
if (item == NULL)
goto err;
if (add_item_to_list(thd, item))
@@ -5615,9 +6211,22 @@ sp_variable *LEX::sp_param_init(LEX_CSTRING *name)
}
-bool LEX::sp_param_fill_definition(sp_variable *spvar)
+bool LEX::sp_param_fill_definition(sp_variable *spvar,
+ const Lex_field_type_st &def)
+{
+ return
+ last_field->set_attributes(thd, def, charset,
+ COLUMN_DEFINITION_ROUTINE_PARAM) ||
+ sphead->fill_spvar_definition(thd, last_field, &spvar->name);
+}
+
+
+bool LEX::sf_return_fill_definition(const Lex_field_type_st &def)
{
- return sphead->fill_spvar_definition(thd, last_field, &spvar->name);
+ return
+ last_field->set_attributes(thd, def, charset,
+ COLUMN_DEFINITION_FUNCTION_RETURN) ||
+ sphead->fill_field_definition(thd, last_field);
}
@@ -5627,6 +6236,7 @@ void LEX::set_stmt_init()
mysql_init_select(this);
option_type= OPT_SESSION;
autocommit= 0;
+ var_list.empty();
};
@@ -5684,7 +6294,7 @@ static bool is_old(const char *str)
bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name) const
{
// "name" is not necessarily NULL-terminated!
- return sphead && sphead->m_handler->type() == TYPE_ENUM_TRIGGER &&
+ return sphead && sphead->m_handler->type() == SP_TYPE_TRIGGER &&
name->length == 3 && (is_new(name->str) || is_old(name->str));
}
@@ -5713,7 +6323,7 @@ bool LEX::sp_variable_declarations_set_default(THD *thd, int nvars,
bool last= i + 1 == (uint) nvars;
spvar->default_value= dflt_value_item;
/* The last instruction is responsible for freeing LEX. */
- sp_instr_set *is= new (this->thd->mem_root)
+ sp_instr_set *is= new (thd->mem_root)
sp_instr_set(sphead->instructions(),
spcont, &sp_rcontext_handler_local,
spvar->offset, dflt_value_item,
@@ -6006,14 +6616,14 @@ sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name,
sp_variable *spvar= spcont->add_variable(thd, name);
spcont->declare_var_boundary(1);
spvar->field_def.field_name= spvar->name;
- spvar->field_def.set_handler(&type_handler_longlong);
- type_handler_longlong.Column_definition_prepare_stage2(&spvar->field_def,
- NULL, HA_CAN_GEOMETRY);
+ spvar->field_def.set_handler(&type_handler_slonglong);
+ type_handler_slonglong.Column_definition_prepare_stage2(&spvar->field_def,
+ NULL, HA_CAN_GEOMETRY);
if (!value && unlikely(!(value= new (thd->mem_root) Item_null(thd))))
return NULL;
spvar->default_value= value;
- sp_instr_set *is= new (this->thd->mem_root)
+ sp_instr_set *is= new (thd->mem_root)
sp_instr_set(sphead->instructions(),
spcont, &sp_rcontext_handler_local,
spvar->offset, value,
@@ -6051,7 +6661,7 @@ bool LEX::sp_for_loop_implicit_cursor_statement(THD *thd,
SELECT rec.a, rec.b;
END FOR;
*/
- if (!(item= new (thd->mem_root) Item_field(thd, NULL, NullS, NullS, &name)))
+ if (!(item= new (thd->mem_root) Item_field(thd, NULL, name)))
return true;
bounds->m_index->set_item_and_free_list(item, NULL);
if (thd->lex->sphead->restore_lex(thd))
@@ -6205,7 +6815,7 @@ bool LEX::sp_for_loop_cursor_declarations(THD *thd,
name= item_splocal->m_name;
else if ((item_field= item->type() == Item::FIELD_ITEM ?
static_cast<Item_field *>(item) : NULL) &&
- item_field->table_name == NULL)
+ item_field->table_name.str == NULL)
name= item_field->field_name;
else if (item->type() == Item::FUNC_ITEM &&
static_cast<Item_func*>(item)->functype() == Item_func::FUNC_SP &&
@@ -6837,7 +7447,7 @@ bool LEX::sp_exit_block(THD *thd, sp_label *lab, Item *when)
sp_instr_jump_if_not *i= new (thd->mem_root)
sp_instr_jump_if_not(sphead->instructions(),
spcont,
- when, thd->lex);
+ when, this);
if (unlikely(i == NULL) ||
unlikely(sphead->add_instr(i)) ||
unlikely(sp_exit_block(thd, lab)))
@@ -6900,17 +7510,40 @@ bool LEX::sp_continue_loop(THD *thd, sp_label *lab)
}
-bool LEX::sp_continue_loop(THD *thd, sp_label *lab, Item *when)
+bool LEX::sp_continue_statement(THD *thd)
{
- if (!when)
- return sp_continue_loop(thd, lab);
+ sp_label *lab= spcont->find_label_current_loop_start();
+ if (unlikely(!lab))
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", "");
+ return true;
+ }
+ DBUG_ASSERT(lab->type == sp_label::ITERATION);
+ return sp_continue_loop(thd, lab);
+}
+
+
+bool LEX::sp_continue_statement(THD *thd, const LEX_CSTRING *label_name)
+{
+ sp_label *lab= spcont->find_label(label_name);
+ if (!lab || lab->type != sp_label::ITERATION)
+ {
+ my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", label_name->str);
+ return true;
+ }
+ return sp_continue_loop(thd, lab);
+}
+
+bool LEX::sp_continue_loop(THD *thd, sp_label *lab, Item *when)
+{
+ DBUG_ASSERT(when);
DBUG_ASSERT(sphead == thd->lex->sphead);
DBUG_ASSERT(spcont == thd->lex->spcont);
sp_instr_jump_if_not *i= new (thd->mem_root)
sp_instr_jump_if_not(sphead->instructions(),
spcont,
- when, thd->lex);
+ when, this);
if (unlikely(i == NULL) ||
unlikely(sphead->add_instr(i)) ||
unlikely(sp_continue_loop(thd, lab)))
@@ -6920,7 +7553,7 @@ bool LEX::sp_continue_loop(THD *thd, sp_label *lab, Item *when)
}
-bool LEX::sp_continue_statement(THD *thd, Item *when)
+bool sp_expr_lex::sp_continue_when_statement(THD *thd)
{
sp_label *lab= spcont->find_label_current_loop_start();
if (unlikely(!lab))
@@ -6929,12 +7562,12 @@ bool LEX::sp_continue_statement(THD *thd, Item *when)
return true;
}
DBUG_ASSERT(lab->type == sp_label::ITERATION);
- return sp_continue_loop(thd, lab, when);
+ return sp_continue_loop(thd, lab, get_item());
}
-bool LEX::sp_continue_statement(THD *thd, const LEX_CSTRING *label_name,
- Item *when)
+bool sp_expr_lex::sp_continue_when_statement(THD *thd,
+ const LEX_CSTRING *label_name)
{
sp_label *lab= spcont->find_label(label_name);
if (!lab || lab->type != sp_label::ITERATION)
@@ -6942,7 +7575,7 @@ bool LEX::sp_continue_statement(THD *thd, const LEX_CSTRING *label_name,
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", label_name->str);
return true;
}
- return sp_continue_loop(thd, lab, when);
+ return sp_continue_loop(thd, lab, get_item());
}
@@ -7007,10 +7640,10 @@ void LEX::sp_pop_loop_empty_label(THD *thd)
}
-bool LEX::sp_while_loop_expression(THD *thd, Item *expr)
+bool LEX::sp_while_loop_expression(THD *thd, Item *item)
{
sp_instr_jump_if_not *i= new (thd->mem_root)
- sp_instr_jump_if_not(sphead->instructions(), spcont, expr, this);
+ sp_instr_jump_if_not(sphead->instructions(), spcont, item, this);
return (unlikely(i == NULL) ||
/* Jumping forward */
unlikely(sphead->push_backpatch(thd, i, spcont->last_label())) ||
@@ -7061,7 +7694,7 @@ Item *LEX::create_and_link_Item_trigger_field(THD *thd,
new_row ?
Item_trigger_field::NEW_ROW:
Item_trigger_field::OLD_ROW,
- name, SELECT_ACL, tmp_read_only);
+ *name, SELECT_ACL, tmp_read_only);
/*
Let us add this item to list of all Item_trigger_field objects
in trigger.
@@ -7210,7 +7843,7 @@ Item *LEX::create_item_for_loop_bound(THD *thd,
Pass NULL as the name resolution context.
This is OK, fix_fields() won't be called for this Item_field.
*/
- return new (thd->mem_root) Item_field(thd, NULL, a->str, b->str, c);
+ return new (thd->mem_root) Item_field(thd, NULL, *a, *b, *c);
}
@@ -7248,7 +7881,7 @@ Item *LEX::create_item_ident_nospvar(THD *thd,
if (current_select->parsing_place == FOR_LOOP_BOUND)
return create_item_for_loop_bound(thd, &null_clex_str, a, b);
- return create_item_ident_field(thd, NullS, a->str, b);
+ return create_item_ident_field(thd, Lex_ident_sys(), *a, *b);
}
@@ -7421,11 +8054,11 @@ Item *LEX::create_item_ident(THD *thd,
if ((thd->variables.sql_mode & MODE_ORACLE) && b.length == 7)
{
- if (!my_strnncoll(system_charset_info,
+ if (!system_charset_info->strnncoll(
(const uchar *) b.str, 7,
(const uchar *) "NEXTVAL", 7))
return create_item_func_nextval(thd, &null_clex_str, &a);
- else if (!my_strnncoll(system_charset_info,
+ else if (!system_charset_info->strnncoll(
(const uchar *) b.str, 7,
(const uchar *) "CURRVAL", 7))
return create_item_func_lastval(thd, &null_clex_str, &a);
@@ -7440,16 +8073,15 @@ Item *LEX::create_item_ident(THD *thd,
const Lex_ident_sys_st *b,
const Lex_ident_sys_st *c)
{
- const char *schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ?
- NullS : a->str);
-
+ Lex_ident_sys_st schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
+ Lex_ident_sys() : *a;
if ((thd->variables.sql_mode & MODE_ORACLE) && c->length == 7)
{
- if (!my_strnncoll(system_charset_info,
+ if (!system_charset_info->strnncoll(
(const uchar *) c->str, 7,
(const uchar *) "NEXTVAL", 7))
return create_item_func_nextval(thd, a, b);
- else if (!my_strnncoll(system_charset_info,
+ else if (!system_charset_info->strnncoll(
(const uchar *) c->str, 7,
(const uchar *) "CURRVAL", 7))
return create_item_func_lastval(thd, a, b);
@@ -7464,7 +8096,7 @@ Item *LEX::create_item_ident(THD *thd,
if (current_select->parsing_place == FOR_LOOP_BOUND)
return create_item_for_loop_bound(thd, &null_clex_str, b, c);
- return create_item_ident_field(thd, schema, b->str, c);
+ return create_item_ident_field(thd, schema, *b, *c);
}
@@ -7551,11 +8183,12 @@ bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val)
}
-Item *LEX::create_item_ident_field(THD *thd, const char *db,
- const char *table,
- const Lex_ident_sys_st *name)
+Item *LEX::create_item_ident_field(THD *thd,
+ const Lex_ident_sys_st &db,
+ const Lex_ident_sys_st &table,
+ const Lex_ident_sys_st &name)
{
- if (check_expr_allows_fields_or_error(thd, name->str))
+ if (check_expr_allows_fields_or_error(thd, name.str))
return NULL;
if (current_select->parsing_place != IN_HAVING ||
@@ -7635,7 +8268,7 @@ Item *LEX::create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
-bool LEX::set_variable(const LEX_CSTRING *name, Item *item)
+bool LEX::set_variable(const Lex_ident_sys_st *name, Item *item)
{
sp_pcontext *ctx;
const Sp_rcontext_handler *rh;
@@ -7649,8 +8282,8 @@ bool LEX::set_variable(const LEX_CSTRING *name, Item *item)
Generate instructions for:
SET x.y= expr;
*/
-bool LEX::set_variable(const LEX_CSTRING *name1,
- const LEX_CSTRING *name2,
+bool LEX::set_variable(const Lex_ident_sys_st *name1,
+ const Lex_ident_sys_st *name2,
Item *item)
{
const Sp_rcontext_handler *rh;
@@ -7680,10 +8313,10 @@ bool LEX::set_variable(const LEX_CSTRING *name1,
bool LEX::set_default_system_variable(enum_var_type var_type,
- const LEX_CSTRING *name,
+ const Lex_ident_sys_st *name,
Item *val)
{
- static LEX_CSTRING default_base_name= {STRING_WITH_LEN("default")};
+ static Lex_ident_sys default_base_name= {STRING_WITH_LEN("default")};
sys_var *var= find_sys_var(thd, name->str, name->length);
if (!var)
return true;
@@ -7697,18 +8330,19 @@ bool LEX::set_default_system_variable(enum_var_type var_type,
bool LEX::set_system_variable(enum_var_type var_type,
- const LEX_CSTRING *name,
+ const Lex_ident_sys_st *name,
Item *val)
{
sys_var *var= find_sys_var(thd, name->str, name->length);
DBUG_ASSERT(thd->is_error() || var != NULL);
- return likely(var) ? set_system_variable(var_type, var, &null_clex_str, val) : true;
+ static Lex_ident_sys null_str;
+ return likely(var) ? set_system_variable(var_type, var, &null_str, val) : true;
}
bool LEX::set_system_variable(THD *thd, enum_var_type var_type,
- const LEX_CSTRING *name1,
- const LEX_CSTRING *name2,
+ const Lex_ident_sys_st *name1,
+ const Lex_ident_sys_st *name2,
Item *val)
{
sys_var *tmp;
@@ -8390,15 +9024,15 @@ bool LEX::call_statement_start(THD *thd, sp_name *name)
}
-bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name)
+bool LEX::call_statement_start(THD *thd, const Lex_ident_sys_st *name)
{
sp_name *spname= make_sp_name(thd, name);
return unlikely(!spname) || call_statement_start(thd, spname);
}
-bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name1,
- const LEX_CSTRING *name2)
+bool LEX::call_statement_start(THD *thd, const Lex_ident_sys_st *name1,
+ const Lex_ident_sys_st *name2)
{
sp_name *spname= make_sp_name(thd, name1, name2);
return unlikely(!spname) || call_statement_start(thd, spname);
@@ -8426,7 +9060,7 @@ sp_package *LEX::create_package_start(THD *thd,
}
if (unlikely(set_command_with_check(command, options)))
return NULL;
- if (sph->type() == TYPE_ENUM_PACKAGE_BODY)
+ if (sph->type() == SP_TYPE_PACKAGE_BODY)
{
/*
If we start parsing a "CREATE PACKAGE BODY", we need to load
@@ -8501,16 +9135,13 @@ bool LEX::create_package_finalize(THD *thd,
}
-bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
- stored_procedure_type type_arg)
+bool LEX::add_grant_command(THD *thd, const List<LEX_COLUMN> &columns)
{
if (columns.elements)
{
thd->parse_error();
return true;
}
- sql_command= sql_command_arg,
- type= type_arg;
return false;
}
@@ -8547,9 +9178,9 @@ bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table,
{
DBUG_ASSERT(field_name.str);
Item_field *fld= new (thd->mem_root) Item_field(thd, &context,
- table->db.str,
- table->alias.str,
- &field_name);
+ table->db,
+ table->alias,
+ field_name);
if (unlikely(!fld) || unlikely(item_list.push_back(fld)))
return true;
@@ -8662,13 +9293,27 @@ Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
}
+Item *LEX::make_item_func_call_native_or_parse_error(THD *thd,
+ Lex_ident_cli_st &name,
+ List<Item> *args)
+{
+ Create_func *builder= find_native_function_builder(thd, &name);
+ DBUG_EXECUTE_IF("make_item_func_call_native_simulate_not_found",
+ builder= NULL;);
+ if (builder)
+ return builder->create_func(thd, &name, args);
+ thd->parse_error(ER_SYNTAX_ERROR, name.end());
+ return NULL;
+}
+
+
Item *LEX::create_item_qualified_asterisk(THD *thd,
const Lex_ident_sys_st *name)
{
Item *item;
if (!(item= new (thd->mem_root) Item_field(thd, current_context(),
- NullS, name->str,
- &star_clex_str)))
+ null_clex_str, *name,
+ star_clex_str)))
return NULL;
current_select->with_wild++;
return item;
@@ -8680,11 +9325,10 @@ Item *LEX::create_item_qualified_asterisk(THD *thd,
const Lex_ident_sys_st *b)
{
Item *item;
- const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
- NullS : a->str;
+ Lex_ident_sys_st schema= thd->client_capabilities & CLIENT_NO_SCHEMA ?
+ Lex_ident_sys() : *a;
if (!(item= new (thd->mem_root) Item_field(thd, current_context(),
- schema, b->str,
- &star_clex_str)))
+ schema, *b, star_clex_str)))
return NULL;
current_select->with_wild++;
return item;
@@ -8946,6 +9590,7 @@ void st_select_lex::add_statistics(SELECT_LEX_UNIT *unit)
bool LEX::main_select_push(bool service)
{
DBUG_ENTER("LEX::main_select_push");
+ DBUG_PRINT("info", ("service: %u", service));
current_select_number= 1;
builtin_select.select_number= 1;
builtin_select.is_service_select= service;
@@ -9221,6 +9866,17 @@ LEX::add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit,
}
+SELECT_LEX_UNIT *
+LEX::add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *sel,
+ enum sub_select_type unit_type,
+ bool distinct)
+{
+ return
+ add_primary_to_query_expression_body(unit, sel, unit_type, distinct,
+ thd->variables.sql_mode & MODE_ORACLE);
+}
+
/**
Add query primary to a parenthesized query primary
pruducing a new query expression body
@@ -10422,6 +11078,89 @@ bool LEX::stmt_create_stored_function_start(const DDL_options_st &options,
}
+bool LEX::stmt_drop_function(const DDL_options_st &options,
+ const Lex_ident_sys_st &db,
+ const Lex_ident_sys_st &name)
+{
+ if (unlikely(db.str && check_db_name((LEX_STRING*) &db)))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
+ return true;
+ }
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+ return true;
+ }
+ set_command(SQLCOM_DROP_FUNCTION, options);
+ spname= new (thd->mem_root) sp_name(&db, &name, true);
+ return spname == NULL;
+}
+
+
+bool LEX::stmt_drop_function(const DDL_options_st &options,
+ const Lex_ident_sys_st &name)
+{
+ LEX_CSTRING db= {0, 0};
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+ return true;
+ }
+ if (thd->db.str && unlikely(copy_db_to(&db)))
+ return true;
+ set_command(SQLCOM_DROP_FUNCTION, options);
+ spname= new (thd->mem_root) sp_name(&db, &name, false);
+ return spname == NULL;
+}
+
+
+bool LEX::stmt_drop_procedure(const DDL_options_st &options,
+ sp_name *name)
+{
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE");
+ return true;
+ }
+ set_command(SQLCOM_DROP_PROCEDURE, options);
+ spname= name;
+ return false;
+}
+
+
+bool LEX::stmt_alter_function_start(sp_name *name)
+{
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "FUNCTION");
+ return true;
+ }
+ if (main_select_push())
+ return true;
+ sp_chistics.init();
+ sql_command= SQLCOM_ALTER_FUNCTION;
+ spname= name;
+ return false;
+}
+
+
+bool LEX::stmt_alter_procedure_start(sp_name *name)
+{
+ if (unlikely(sphead))
+ {
+ my_error(ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE");
+ return true;
+ }
+ if (main_select_push())
+ return true;
+ sp_chistics.init();
+ sql_command= SQLCOM_ALTER_PROCEDURE;
+ spname= name;
+ return false;
+}
+
+
Spvar_definition *LEX::row_field_name(THD *thd, const Lex_ident_sys_st &name)
{
Spvar_definition *res;
@@ -10438,6 +11177,301 @@ Spvar_definition *LEX::row_field_name(THD *thd, const Lex_ident_sys_st &name)
}
+Item *
+Lex_cast_type_st::create_typecast_item_or_error(THD *thd, Item *item,
+ CHARSET_INFO *cs) const
+{
+ Item *tmp= create_typecast_item(thd, item, cs);
+ if (!tmp)
+ {
+ Name name= m_type_handler->name();
+ char buf[128];
+ size_t length= my_snprintf(buf, sizeof(buf), "CAST(expr AS %.*s)",
+ (int) name.length(), name.ptr());
+ my_error(ER_UNKNOWN_OPERATOR, MYF(0),
+ ErrConvString(buf, length, system_charset_info).ptr());
+ }
+ return tmp;
+}
+
+
+void Lex_field_type_st::set_handler_length_flags(const Type_handler *handler,
+ const char *length,
+ uint32 flags)
+{
+ DBUG_ASSERT(!handler->is_unsigned());
+ if (flags & UNSIGNED_FLAG)
+ handler= handler->type_handler_unsigned();
+ set(handler, length, NULL);
+}
+
+
+bool LEX::set_field_type_udt(Lex_field_type_st *type,
+ const LEX_CSTRING &name,
+ const Lex_length_and_dec_st &attr)
+{
+ const Type_handler *h;
+ if (!(h= Type_handler::handler_by_name_or_error(thd, name)))
+ return true;
+ type->set(h, attr);
+ charset= &my_charset_bin;
+ return false;
+}
+
+
+bool LEX::set_cast_type_udt(Lex_cast_type_st *type,
+ const LEX_CSTRING &name)
+{
+ const Type_handler *h;
+ if (!(h= Type_handler::handler_by_name_or_error(thd, name)))
+ return true;
+ type->set(h);
+ charset= NULL;
+ return false;
+}
+
+
+bool sp_expr_lex::sp_repeat_loop_finalize(THD *thd)
+{
+ uint ip= sphead->instructions();
+ sp_label *lab= spcont->last_label(); /* Jumping back */
+ sp_instr_jump_if_not *i= new (thd->mem_root)
+ sp_instr_jump_if_not(ip, spcont, get_item(), lab->ip, this);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)))
+ return true;
+ /* We can shortcut the cont_backpatch here */
+ i->m_cont_dest= ip+1;
+ return false;
+}
+
+
+bool sp_expr_lex::sp_if_expr(THD *thd)
+{
+ uint ip= sphead->instructions();
+ sp_instr_jump_if_not *i= new (thd->mem_root)
+ sp_instr_jump_if_not(ip, spcont, get_item(), this);
+ return
+ (unlikely(i == NULL) ||
+ unlikely(sphead->push_backpatch(thd, i,
+ spcont->push_label(thd, &empty_clex_str,
+ 0))) ||
+ unlikely(sphead->add_cont_backpatch(i)) ||
+ unlikely(sphead->add_instr(i)));
+}
+
+
+bool LEX::sp_if_after_statements(THD *thd)
+{
+ uint ip= sphead->instructions();
+ sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont);
+ if (unlikely(i == NULL) ||
+ unlikely(sphead->add_instr(i)))
+ return true;
+ sphead->backpatch(spcont->pop_label());
+ sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0));
+ return false;
+}
+
+
+sp_condition_value *LEX::stmt_signal_value(const Lex_ident_sys_st &ident)
+{
+ sp_condition_value *cond;
+ /* SIGNAL foo cannot be used outside of stored programs */
+ if (unlikely(spcont == NULL))
+ {
+ my_error(ER_SP_COND_MISMATCH, MYF(0), ident.str);
+ return NULL;
+ }
+ cond= spcont->find_declared_or_predefined_condition(thd, &ident);
+ if (unlikely(cond == NULL))
+ {
+ my_error(ER_SP_COND_MISMATCH, MYF(0), ident.str);
+ return NULL;
+ }
+ bool bad= thd->variables.sql_mode & MODE_ORACLE ?
+ !cond->has_sql_state() :
+ cond->type != sp_condition_value::SQLSTATE;
+ if (unlikely(bad))
+ {
+ my_error(ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0));
+ return NULL;
+ }
+ return cond;
+}
+
+
+bool LEX::add_table_foreign_key(const LEX_CSTRING *name,
+ const LEX_CSTRING *constraint_name,
+ Table_ident *ref_table_name,
+ DDL_options ddl_options)
+{
+ Key *key= new (thd->mem_root) Foreign_key(name,
+ &last_key->columns,
+ constraint_name,
+ &ref_table_name->db,
+ &ref_table_name->table,
+ &ref_list,
+ fk_delete_opt,
+ fk_update_opt,
+ fk_match_option,
+ ddl_options);
+ if (unlikely(key == NULL))
+ return true;
+
+ /*
+ handle_if_exists_options() expects the two keys in this order:
+ the Foreign_key, followed by its auto-generated Key.
+ */
+ alter_info.key_list.push_back(key, thd->mem_root);
+ alter_info.key_list.push_back(last_key, thd->mem_root);
+
+ option_list= NULL;
+
+ /* Only used for ALTER TABLE. Ignored otherwise. */
+ alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
+
+ return false;
+}
+
+
+bool LEX::add_column_foreign_key(const LEX_CSTRING *name,
+ const LEX_CSTRING *constraint_name,
+ Table_ident *ref_table_name,
+ DDL_options ddl_options)
+{
+ if (last_field->vcol_info || last_field->vers_sys_field())
+ {
+ thd->parse_error();
+ return true;
+ }
+ if (unlikely(!(last_key= (new (thd->mem_root)
+ Key(Key::MULTIPLE, constraint_name,
+ HA_KEY_ALG_UNDEF, true, ddl_options)))))
+ return true;
+ Key_part_spec *key= new (thd->mem_root) Key_part_spec(name, 0);
+ if (unlikely(key == NULL))
+ return true;
+ last_key->columns.push_back(key, thd->mem_root);
+ if (ref_list.is_empty())
+ {
+ ref_list.push_back(key, thd->mem_root);
+ }
+ if (unlikely(add_table_foreign_key(constraint_name, constraint_name,
+ ref_table_name, ddl_options)))
+ return true;
+ option_list= NULL;
+
+ /* Only used for ALTER TABLE. Ignored otherwise. */
+ alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
+
+ return false;
+}
+
+
+bool LEX::stmt_grant_table(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident,
+ privilege_t grant_option)
+{
+ sql_command= SQLCOM_GRANT;
+ return
+ grant->set_object_name(thd, ident, current_select, grant_option) ||
+ !(m_sql_cmd= new (thd->mem_root) Sql_cmd_grant_table(sql_command, *grant));
+}
+
+
+bool LEX::stmt_revoke_table(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident)
+{
+ sql_command= SQLCOM_REVOKE;
+ return
+ grant->set_object_name(thd, ident, current_select, NO_ACL) ||
+ !(m_sql_cmd= new (thd->mem_root) Sql_cmd_grant_table(sql_command, *grant));
+}
+
+
+bool LEX::stmt_grant_sp(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident,
+ const Sp_handler &sph,
+ privilege_t grant_option)
+{
+ sql_command= SQLCOM_GRANT;
+ return
+ grant->set_object_name(thd, ident, current_select, grant_option) ||
+ add_grant_command(thd, grant->columns()) ||
+ !(m_sql_cmd= new (thd->mem_root) Sql_cmd_grant_sp(sql_command,
+ *grant, sph));
+}
+
+
+bool LEX::stmt_revoke_sp(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident,
+ const Sp_handler &sph)
+{
+ sql_command= SQLCOM_REVOKE;
+ return
+ grant->set_object_name(thd, ident, current_select, NO_ACL) ||
+ add_grant_command(thd, grant->columns()) ||
+ !(m_sql_cmd= new (thd->mem_root) Sql_cmd_grant_sp(sql_command,
+ *grant, sph));
+}
+
+
+bool LEX::stmt_grant_proxy(THD *thd, LEX_USER *user, privilege_t grant_option)
+{
+ users_list.push_front(user);
+ sql_command= SQLCOM_GRANT;
+ return !(m_sql_cmd= new (thd->mem_root) Sql_cmd_grant_proxy(sql_command,
+ grant_option));
+}
+
+
+bool LEX::stmt_revoke_proxy(THD *thd, LEX_USER *user)
+{
+ users_list.push_front(user);
+ sql_command= SQLCOM_REVOKE;
+ return !(m_sql_cmd= new (thd->mem_root) Sql_cmd_grant_proxy(sql_command,
+ NO_ACL));
+}
+
+
+LEX_USER *LEX::current_user_for_set_password(THD *thd)
+{
+ LEX_CSTRING pw= { STRING_WITH_LEN("password") };
+ if (unlikely(spcont && spcont->find_variable(&pw, false)))
+ {
+ my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
+ return NULL;
+ }
+ LEX_USER *res;
+ if (unlikely(!(res= (LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
+ return NULL;
+ res->user= current_user;
+ return res;
+}
+
+
+bool LEX::sp_create_set_password_instr(THD *thd,
+ LEX_USER *user,
+ USER_AUTH *auth,
+ bool no_lookahead)
+{
+ user->auth= auth;
+ set_var_password *var= new (thd->mem_root) set_var_password(user);
+ if (unlikely(var == NULL) ||
+ unlikely(var_list.push_back(var, thd->mem_root)))
+ return true;
+ autocommit= true;
+ if (sphead)
+ sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ return sp_create_assignment_instr(thd, no_lookahead);
+}
+
+
bool LEX::map_data_type(const Lex_ident_sys_st &schema_name,
Lex_field_type_st *type) const
{
@@ -10451,13 +11485,7 @@ bool LEX::map_data_type(const Lex_ident_sys_st &schema_name,
my_snprintf(buf, sizeof(buf), "%.*s.%.*s",
(int) schema_name.length, schema_name.str,
(int) type_name.length(), type_name.ptr());
-#if MYSQL_VERSION_ID > 100500
-#error Please remove the old code
my_error(ER_UNKNOWN_DATA_TYPE, MYF(0), buf);
-#else
- my_printf_error(ER_UNKNOWN_ERROR, "Unknown data type: '%-.64s'",
- MYF(0), buf);
-#endif
return true;
}
const Type_handler *mapped= schema->map_data_type(thd, type->type_handler());
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index c8fca748b77..3e59ad3afc9 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2019, Oracle and/or its affiliates.
- Copyright (c) 2010, 2019, MariaDB Corporation.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
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,13 +25,15 @@
#include "sql_trigger.h"
#include "thr_lock.h" /* thr_lock_type, TL_UNLOCK */
#include "mem_root_array.h"
+#include "grant.h"
#include "sql_cmd.h"
#include "sql_alter.h" // Alter_info
#include "sql_window.h"
#include "sql_trigger.h"
-#include "sp.h" // enum stored_procedure_type
+#include "sp.h" // enum enum_sp_type
#include "sql_tvc.h"
#include "item.h"
+#include "sql_limit.h" // Select_limit_counters
#include "sql_schema.h"
/* Used for flags of nesting constructs */
@@ -84,13 +86,13 @@ public:
bool is_quoted() const { return m_quote != '\0'; }
char quote() const { return m_quote; }
// Get string repertoire by the 8-bit flag and the character set
- uint repertoire(CHARSET_INFO *cs) const
+ my_repertoire_t repertoire(CHARSET_INFO *cs) const
{
return !m_is_8bit && my_charset_is_ascii_based(cs) ?
MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
}
// Get string repertoire by the 8-bit flag, for ASCII-based character sets
- uint repertoire() const
+ my_repertoire_t repertoire() const
{
return !m_is_8bit ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
}
@@ -183,6 +185,24 @@ public:
};
+struct Lex_column_list_privilege_st
+{
+ List<Lex_ident_sys> *m_columns;
+ privilege_t m_privilege;
+};
+
+
+class Lex_column_list_privilege: public Lex_column_list_privilege_st
+{
+public:
+ Lex_column_list_privilege(List<Lex_ident_sys> *columns, privilege_t privilege)
+ {
+ m_columns= columns;
+ m_privilege= privilege;
+ }
+};
+
+
/**
ORDER BY ... LIMIT parameters;
*/
@@ -208,6 +228,14 @@ enum sub_select_type
GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE
};
+enum set_op_type
+{
+ UNSPECIFIED,
+ UNION_DISTINCT, UNION_ALL,
+ EXCEPT_DISTINCT, EXCEPT_ALL,
+ INTERSECT_DISTINCT, INTERSECT_ALL
+};
+
inline int cmp_unit_op(enum sub_select_type op1, enum sub_select_type op2)
{
DBUG_ASSERT(op1 >= UNION_TYPE && op1 <= EXCEPT_TYPE);
@@ -259,6 +287,7 @@ class sp_name;
class sp_instr;
class sp_pcontext;
class sp_variable;
+class sp_expr_lex;
class sp_assignment_lex;
class st_alter_tablespace;
class partition_info;
@@ -482,11 +511,11 @@ struct LEX_MASTER_INFO
void init()
{
bzero(this, sizeof(*this));
- my_init_dynamic_array(&repl_ignore_server_ids,
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &repl_ignore_server_ids,
sizeof(::server_id), 0, 16, MYF(0));
- my_init_dynamic_array(&repl_do_domain_ids,
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &repl_do_domain_ids,
sizeof(ulong), 0, 16, MYF(0));
- my_init_dynamic_array(&repl_ignore_domain_ids,
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &repl_ignore_domain_ids,
sizeof(ulong), 0, 16, MYF(0));
sql_delay= -1;
}
@@ -822,6 +851,7 @@ 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_for_slow_log(LEX *lex, THD *thd, String *str);
+
class st_select_lex_unit: public st_select_lex_node {
protected:
TABLE_LIST result_table_list;
@@ -830,7 +860,7 @@ protected:
bool saved_error;
bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result,
- ulong additional_options,
+ ulonglong additional_options,
bool is_union_select);
bool join_union_type_handlers(THD *thd,
class Type_holder *holders, uint count);
@@ -842,8 +872,8 @@ public:
// Ensures that at least all members used during cleanup() are initialized.
st_select_lex_unit()
: union_result(NULL), table(NULL), result(NULL),
- cleaned(false),
- fake_select_lex(NULL)
+ cleaned(false), bag_set_op_optimized(false),
+ have_except_all_or_intersect_all(false), fake_select_lex(NULL)
{
}
@@ -854,9 +884,11 @@ public:
optimized, // optimize phase already performed for UNION (unit)
optimized_2,
executed, // already executed
- cleaned;
+ cleaned,
+ bag_set_op_optimized;
bool optimize_started;
+ bool have_except_all_or_intersect_all;
// list of fields which points to temporary table for union
List<Item> item_list;
@@ -869,11 +901,6 @@ public:
*/
List<Item> types;
/**
- There is INTERSECT and it is item used in creating temporary
- table for it
- */
- Item_int *intersect_mark;
- /**
TRUE if the unit contained TVC at the top level that has been wrapped
into SELECT:
VALUES (v1) ... (vn) => SELECT * FROM (VALUES (v1) ... (vn)) as tvc
@@ -904,7 +931,7 @@ public:
//node on which we should return current_select pointer after parsing subquery
st_select_lex *return_to;
/* LIMIT clause runtime counters */
- ha_rows select_limit_cnt, offset_limit_cnt;
+ Select_limit_counters lim;
/* not NULL if unit used in subselect, point to subselect item */
Item_subselect *item;
/*
@@ -929,8 +956,9 @@ public:
fake_select_lex is used.
*/
st_select_lex *saved_fake_select_lex;
-
- st_select_lex *union_distinct; /* pointer to the last UNION DISTINCT */
+
+ /* pointer to the last node before last subsequence of UNION ALL */
+ st_select_lex *union_distinct;
bool describe; /* union exec() called for EXPLAIN */
Procedure *last_procedure; /* Pointer to procedure, if such exists */
@@ -954,8 +982,9 @@ public:
/* UNION methods */
bool prepare(TABLE_LIST *derived_arg, select_result *sel_result,
- ulong additional_options);
+ ulonglong additional_options);
bool optimize();
+ void optimize_bag_operation(bool is_outer_distinct);
bool exec();
bool exec_recursive();
bool cleanup();
@@ -987,6 +1016,21 @@ public:
int save_union_explain_part2(Explain_query *output);
unit_common_op common_op();
+ bool explainable() const
+ {
+ /*
+ EXPLAIN/ANALYZE unit, when:
+ (1) if it's a subquery - it's not part of eliminated WHERE/ON clause.
+ (2) if it's a CTE - it's not hanging (needed for execution)
+ (3) if it's a derived - it's not merged
+ if it's not 1/2/3 - it's some weird internal thing, ignore it
+ */
+ return item ? !item->eliminated : // (1)
+ with_element ? derived && derived->derived_result : // (2)
+ derived ? derived->is_materialized_derived() : // (3)
+ false;
+ }
+
void reset_distinct();
void fix_distinct();
@@ -1026,7 +1070,7 @@ Field_pair *find_matching_field_pair(Item *item, List<Field_pair> pair_list);
#define TOUCHED_SEL_COND 1/* WHERE/HAVING/ON should be reinited before use */
#define TOUCHED_SEL_DERIVED (1<<1)/* derived should be reinited before use */
-
+#define UNIT_NEST_FL 1
/*
SELECT_LEX - store information of parsed SELECT statment
*/
@@ -1049,7 +1093,7 @@ public:
select1->first_nested points to select1.
*/
st_select_lex *first_nested;
-
+ uint8 nest_flags;
Name_resolution_context context;
LEX_CSTRING db;
Item *where, *having; /* WHERE & HAVING clauses */
@@ -1290,10 +1334,8 @@ public:
table_value_constr *tvc;
bool in_tvc;
- /* The interface employed to execute the select query by a foreign engine */
- select_handler *select_h;
/* The object used to organize execution of the query by a foreign engine */
- Pushdown_select *pushdown_select;
+ select_handler *pushdown_select;
/** System Versioning */
public:
@@ -1532,6 +1574,13 @@ public:
select_handler *find_select_handler(THD *thd);
+ bool is_set_op()
+ {
+ return linkage == UNION_TYPE ||
+ linkage == EXCEPT_TYPE ||
+ linkage == INTERSECT_TYPE;
+ }
+
private:
bool m_non_agg_field_used;
bool m_agg_func_used;
@@ -1578,6 +1627,8 @@ public:
void add_statistics(SELECT_LEX_UNIT *unit);
bool make_unique_derived_name(THD *thd, LEX_CSTRING *alias);
void lex_start(LEX *plex);
+ bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); }
+ void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; }
};
typedef class st_select_lex SELECT_LEX;
@@ -2919,15 +2970,6 @@ 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;
@@ -3075,6 +3117,28 @@ public:
};
+class Lex_grant_object_name: public Grant_object_name, public Sql_alloc
+{
+public:
+ Lex_grant_object_name(Table_ident *table_ident)
+ :Grant_object_name(table_ident)
+ { }
+ Lex_grant_object_name(const LEX_CSTRING &db, Type type)
+ :Grant_object_name(db, type)
+ { }
+};
+
+
+class Lex_grant_privilege: public Grant_privilege, public Sql_alloc
+{
+public:
+ Lex_grant_privilege() {}
+ Lex_grant_privilege(privilege_t grant, bool all_privileges= false)
+ :Grant_privilege(grant, all_privileges)
+ { }
+};
+
+
struct LEX: public Query_tables_list
{
SELECT_LEX_UNIT unit; /* most upper unit */
@@ -3082,14 +3146,14 @@ struct LEX: public Query_tables_list
private:
SELECT_LEX builtin_select;
- /* current SELECT_LEX in parsing */
public:
+ /* current SELECT_LEX in parsing */
SELECT_LEX *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
/* current with clause in parsing if any, otherwise 0*/
- With_clause *curr_with_clause;
+ With_clause *curr_with_clause;
/* pointer to the first with clause in the current statement */
With_clause *with_clauses_list;
/*
@@ -3171,7 +3235,6 @@ public:
Table_type table_type; /* Used for SHOW CREATE */
List<Key_part_spec> ref_list;
List<LEX_USER> users_list;
- List<LEX_COLUMN> columns;
List<Item> *insert_list,field_list,value_list,update_list;
List<List_item> many_values;
List<set_var_base> var_list;
@@ -3187,7 +3250,6 @@ private:
bool sp_exit_block(THD *thd, sp_label *lab, Item *when);
bool sp_continue_loop(THD *thd, sp_label *lab);
- bool sp_continue_loop(THD *thd, sp_label *lab, Item *when);
bool sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop);
bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop);
@@ -3198,6 +3260,10 @@ private:
@retval true ERROR (fields are not allowed). Error is raised.
*/
bool check_expr_allows_fields_or_error(THD *thd, const char *name) const;
+
+protected:
+ bool sp_continue_loop(THD *thd, sp_label *lab, Item *when);
+
public:
void parse_error(uint err_number= ER_SYNTAX_ERROR);
inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;}
@@ -3205,10 +3271,10 @@ public:
void reset_arena_for_set_stmt(Query_arena *backup);
void free_arena_for_set_stmt();
+ void print(String *str, enum_query_type qtype);
List<Item_func_set_user_var> set_var_list; // in-query assignment list
List<Item_param> param_list;
List<LEX_CSTRING> view_list; // view list (list of field names in view)
- List<LEX_CSTRING> with_column_list; // list of column names in with_list_element
List<LEX_STRING> *column_list; // list of column names (in ANALYZE)
List<LEX_STRING> *index_list; // list of index names (in ANALYZE)
/*
@@ -3291,7 +3357,6 @@ public:
uint profile_query_id;
uint profile_options;
- uint grant, grant_tot_col, which_columns;
enum backup_stages backup_stage;
enum Foreign_key::fk_match_opt fk_match_option;
enum_fk_option fk_update_opt;
@@ -3344,7 +3409,6 @@ public:
sp_head *sphead;
sp_name *spname;
bool sp_lex_in_use; // Keep track on lex usage in SPs for error handling
- bool all_privileges;
sp_pcontext *spcont;
@@ -3637,8 +3701,9 @@ public:
if (unlikely(!select_stack_top))
{
current_select= &builtin_select;
- DBUG_PRINT("info", ("Top Select is empty -> sel builtin: %p",
- current_select));
+ DBUG_PRINT("info", ("Top Select is empty -> sel builtin: %p service: %u",
+ current_select, builtin_select.is_service_select));
+ builtin_select.is_service_select= false;
}
else
current_select= select_stack[select_stack_top - 1];
@@ -3714,25 +3779,25 @@ public:
bool sp_proc_stmt_statement_finalize(THD *, bool no_lookahead);
sp_variable *sp_param_init(LEX_CSTRING *name);
- bool sp_param_fill_definition(sp_variable *spvar);
+ bool sp_param_fill_definition(sp_variable *spvar,
+ const Lex_field_type_st &def);
+ bool sf_return_fill_definition(const Lex_field_type_st &def);
- int case_stmt_action_expr(Item* expr);
- int case_stmt_action_when(Item *when, bool simple);
int case_stmt_action_then();
bool setup_select_in_parentheses();
bool set_trigger_new_row(const LEX_CSTRING *name, Item *val);
bool set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
Item *val);
bool set_system_variable(enum_var_type var_type, sys_var *var,
- const LEX_CSTRING *base_name, Item *val);
- bool set_system_variable(enum_var_type var_type, const LEX_CSTRING *name,
- Item *val);
+ const Lex_ident_sys_st *base_name, Item *val);
+ bool set_system_variable(enum_var_type var_type,
+ const Lex_ident_sys_st *name, Item *val);
bool set_system_variable(THD *thd, enum_var_type var_type,
- const LEX_CSTRING *name1,
- const LEX_CSTRING *name2,
+ const Lex_ident_sys_st *name1,
+ const Lex_ident_sys_st *name2,
Item *val);
bool set_default_system_variable(enum_var_type var_type,
- const LEX_CSTRING *name,
+ const Lex_ident_sys_st *name,
Item *val);
bool set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val);
void set_stmt_init();
@@ -3762,9 +3827,9 @@ public:
const char *body_start,
const char *body_end);
bool call_statement_start(THD *thd, sp_name *name);
- bool call_statement_start(THD *thd, const LEX_CSTRING *name);
- bool call_statement_start(THD *thd, const LEX_CSTRING *name1,
- const LEX_CSTRING *name2);
+ bool call_statement_start(THD *thd, const Lex_ident_sys_st *name);
+ bool call_statement_start(THD *thd, const Lex_ident_sys_st *name1,
+ const Lex_ident_sys_st *name2);
sp_variable *find_variable(const LEX_CSTRING *name,
sp_pcontext **ctx,
const Sp_rcontext_handler **rh) const;
@@ -3774,9 +3839,9 @@ public:
sp_pcontext *not_used_ctx;
return find_variable(name, &not_used_ctx, rh);
}
- bool set_variable(const LEX_CSTRING *name, Item *item);
- bool set_variable(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
- Item *item);
+ bool set_variable(const Lex_ident_sys_st *name, Item *item);
+ bool set_variable(const Lex_ident_sys_st *name1,
+ const Lex_ident_sys_st *name2, Item *item);
void sp_variable_declarations_init(THD *thd, int nvars);
bool sp_variable_declarations_finalize(THD *thd, int nvars,
const Column_definition *cdef,
@@ -3808,6 +3873,21 @@ public:
const Column_definition &ref,
Row_definition_list *fields,
Item *def);
+
+ LEX_USER *current_user_for_set_password(THD *thd);
+ bool sp_create_set_password_instr(THD *thd,
+ LEX_USER *user,
+ USER_AUTH *auth,
+ bool no_lookahead);
+ bool sp_create_set_password_instr(THD *thd,
+ USER_AUTH *auth,
+ bool no_lookahead)
+ {
+ LEX_USER *user;
+ return !(user= current_user_for_set_password(thd)) ||
+ sp_create_set_password_instr(thd, user, auth, no_lookahead);
+ }
+
bool sp_handler_declaration_init(THD *thd, int type);
bool sp_handler_declaration_finalize(THD *thd, int type);
@@ -3841,11 +3921,13 @@ public:
return create_item_qualified_asterisk(thd, &a, &b);
}
- Item *create_item_ident_field(THD *thd, const char *db, const char *table,
- const Lex_ident_sys_st *name);
+ Item *create_item_ident_field(THD *thd,
+ const Lex_ident_sys_st &db,
+ const Lex_ident_sys_st &table,
+ const Lex_ident_sys_st &name);
Item *create_item_ident_nosp(THD *thd, Lex_ident_sys_st *name)
{
- return create_item_ident_field(thd, NullS, NullS, name);
+ return create_item_ident_field(thd, Lex_ident_sys(), Lex_ident_sys(), *name);
}
Item *create_item_ident_sp(THD *thd, Lex_ident_sys_st *name,
const char *start, const char *end);
@@ -3991,6 +4073,9 @@ public:
Item *make_item_func_substr(THD *thd, Item *a, Item *b);
Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db,
Lex_ident_cli_st *name, List<Item> *args);
+ Item *make_item_func_call_native_or_parse_error(THD *thd,
+ Lex_ident_cli_st &name,
+ List<Item> *args);
my_var *create_outvar(THD *thd, const LEX_CSTRING *name);
/*
@@ -4077,8 +4162,8 @@ public:
bool sp_leave_statement(THD *thd, const LEX_CSTRING *label_name);
bool sp_goto_statement(THD *thd, const LEX_CSTRING *label_name);
- bool sp_continue_statement(THD *thd, Item *when);
- bool sp_continue_statement(THD *thd, const LEX_CSTRING *label_name, Item *when);
+ bool sp_continue_statement(THD *thd);
+ bool sp_continue_statement(THD *thd, const LEX_CSTRING *label_name);
bool sp_iterate_statement(THD *thd, const LEX_CSTRING *label_name);
bool maybe_start_compound_statement(THD *thd);
@@ -4088,6 +4173,7 @@ public:
void sp_pop_loop_empty_label(THD *thd);
bool sp_while_loop_expression(THD *thd, Item *expr);
bool sp_while_loop_finalize(THD *thd);
+ bool sp_if_after_statements(THD *thd);
bool sp_push_goto_label(THD *thd, const LEX_CSTRING *label_name);
Item_param *add_placeholder(THD *thd, const LEX_CSTRING *name,
@@ -4254,13 +4340,13 @@ public:
bool if_not_exists)
{
constr->name= name;
- constr->flags= if_not_exists ?
- Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
+ constr->flags= if_not_exists ? VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS : 0;
alter_info.check_constraint_list.push_back(constr);
return false;
}
- bool add_alter_list(const char *par_name, Virtual_column_info *expr,
+ bool add_alter_list(LEX_CSTRING par_name, Virtual_column_info *expr,
bool par_exists);
+ bool add_alter_list(LEX_CSTRING name, LEX_CSTRING new_name, bool exists);
void set_command(enum_sql_command command,
DDL_options_st options)
{
@@ -4351,8 +4437,30 @@ public:
bool add_create_view(THD *thd, DDL_options_st ddl,
uint16 algorithm, enum_view_suid suid,
Table_ident *table_ident);
- bool add_grant_command(THD *thd, enum_sql_command sql_command_arg,
- stored_procedure_type type_arg);
+ bool add_grant_command(THD *thd, const List<LEX_COLUMN> &columns);
+
+ bool stmt_grant_table(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident,
+ privilege_t grant_option);
+
+ bool stmt_revoke_table(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident);
+
+ bool stmt_grant_sp(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident,
+ const Sp_handler &sph,
+ privilege_t grant_option);
+
+ bool stmt_revoke_sp(THD *thd,
+ Grant_privilege *grant,
+ const Lex_grant_object_name &ident,
+ const Sp_handler &sph);
+
+ bool stmt_grant_proxy(THD *thd, LEX_USER *user, privilege_t grant_option);
+ bool stmt_revoke_proxy(THD *thd, LEX_USER *user);
Vers_parse_info &vers_get_info()
{
@@ -4415,6 +4523,12 @@ public:
SELECT_LEX_UNIT *create_unit(SELECT_LEX*);
SELECT_LEX *wrap_unit_into_derived(SELECT_LEX_UNIT *unit);
SELECT_LEX *wrap_select_chain_into_derived(SELECT_LEX *sel);
+ void init_select()
+ {
+ current_select->init_select();
+ wild= 0;
+ exchange= 0;
+ }
bool main_select_push(bool service= false);
bool insert_select_hack(SELECT_LEX *sel);
SELECT_LEX *create_priority_nest(SELECT_LEX *first_in_nest);
@@ -4457,6 +4571,11 @@ public:
bool distinct,
bool oracle);
SELECT_LEX_UNIT *
+ add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit,
+ SELECT_LEX *sel,
+ enum sub_select_type unit_type,
+ bool distinct);
+ SELECT_LEX_UNIT *
add_primary_to_query_expression_body_ext_parens(
SELECT_LEX_UNIT *unit,
SELECT_LEX *sel,
@@ -4494,6 +4613,11 @@ public:
void stmt_purge_to(const LEX_CSTRING &to);
bool stmt_purge_before(Item *item);
+ SELECT_LEX *returning()
+ { return &builtin_select; }
+ bool has_returning()
+ { return !builtin_select.item_list.is_empty(); }
+
private:
bool stmt_create_routine_start(const DDL_options_st &options)
{
@@ -4526,8 +4650,30 @@ public:
const Lex_ident_sys_st &name,
Item_result return_type,
const LEX_CSTRING &soname);
+
+ bool stmt_drop_function(const DDL_options_st &options,
+ const Lex_ident_sys_st &db,
+ const Lex_ident_sys_st &name);
+
+ bool stmt_drop_function(const DDL_options_st &options,
+ const Lex_ident_sys_st &name);
+
+ bool stmt_drop_procedure(const DDL_options_st &options,
+ sp_name *name);
+
+ bool stmt_alter_function_start(sp_name *name);
+ bool stmt_alter_procedure_start(sp_name *name);
+
+ sp_condition_value *stmt_signal_value(const Lex_ident_sys_st &ident);
+
Spvar_definition *row_field_name(THD *thd, const Lex_ident_sys_st &name);
+ bool set_field_type_udt(Lex_field_type_st *type,
+ const LEX_CSTRING &name,
+ const Lex_length_and_dec_st &attr);
+ bool set_cast_type_udt(Lex_cast_type_st *type,
+ const LEX_CSTRING &name);
+
bool map_data_type(const Lex_ident_sys_st &schema,
Lex_field_type_st *type) const;
@@ -4541,7 +4687,14 @@ public:
select_stack[0]->is_service_select);
}
-
+ bool add_table_foreign_key(const LEX_CSTRING *name,
+ const LEX_CSTRING *constraint_name,
+ Table_ident *table_name,
+ DDL_options ddl_options);
+ bool add_column_foreign_key(const LEX_CSTRING *name,
+ const LEX_CSTRING *constraint_name,
+ Table_ident *ref_table_name,
+ DDL_options ddl_options);
};
@@ -4750,6 +4903,51 @@ public:
};
+class sp_lex_set_var: public sp_lex_local
+{
+public:
+ sp_lex_set_var(THD *thd, const LEX *oldlex)
+ :sp_lex_local(thd, oldlex)
+ {
+ // Set new LEX as if we at start of set rule
+ init_select();
+ sql_command= SQLCOM_SET_OPTION;
+ var_list.empty();
+ autocommit= 0;
+ option_type= oldlex->option_type; // Inherit from the outer lex
+ }
+};
+
+
+class sp_expr_lex: public sp_lex_local
+{
+ Item *m_item; // The expression
+public:
+ sp_expr_lex(THD *thd, LEX *oldlex)
+ :sp_lex_local(thd, oldlex),
+ m_item(NULL)
+ { }
+ void set_item(Item *item)
+ {
+ m_item= item;
+ }
+ Item *get_item() const
+ {
+ return m_item;
+ }
+ bool sp_continue_when_statement(THD *thd);
+ bool sp_continue_when_statement(THD *thd, const LEX_CSTRING *label_name);
+ int case_stmt_action_expr();
+ int case_stmt_action_when(bool simple);
+ bool sp_while_loop_expression(THD *thd)
+ {
+ return LEX::sp_while_loop_expression(thd, get_item());
+ }
+ bool sp_repeat_loop_finalize(THD *thd);
+ bool sp_if_expr(THD *thd);
+};
+
+
/**
An assignment specific LEX, which additionally has an Item (an expression)
and an associated with the Item free_list, which is usually freed
@@ -4829,8 +5027,9 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr);
Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
Item *expr);
-bool sp_create_assignment_lex(THD *thd, bool no_lookahead);
-bool sp_create_assignment_instr(THD *thd, bool no_lookahead);
+bool sp_create_assignment_lex(THD *thd, const char *pos);
+bool sp_create_assignment_instr(THD *thd, bool no_lookahead,
+ bool need_set_keyword= true);
void mark_or_conds_to_avoid_pushdown(Item *cond);
diff --git a/sql/sql_limit.h b/sql/sql_limit.h
new file mode 100644
index 00000000000..a4fcedac14a
--- /dev/null
+++ b/sql/sql_limit.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2019, MariaDB Corporation.
+
+ 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-1335 USA */
+
+
+#ifndef INCLUDES_MARIADB_SQL_LIMIT_H
+#define INCLUDES_MARIADB_SQL_LIMIT_H
+/**
+ LIMIT/OFFSET parameters for execution.
+*/
+
+class Select_limit_counters
+{
+ ha_rows select_limit_cnt, offset_limit_cnt;
+
+ public:
+ Select_limit_counters():
+ select_limit_cnt(0), offset_limit_cnt(0)
+ {};
+ Select_limit_counters(Select_limit_counters &orig):
+ select_limit_cnt(orig.select_limit_cnt),
+ offset_limit_cnt(orig.offset_limit_cnt)
+ {};
+
+ void set_limit(ha_rows limit, ha_rows offset)
+ {
+ offset_limit_cnt= offset;
+ select_limit_cnt= limit;
+ if (select_limit_cnt + offset_limit_cnt >=
+ select_limit_cnt)
+ select_limit_cnt+= offset_limit_cnt;
+ else
+ select_limit_cnt= HA_POS_ERROR;
+ }
+
+ void set_single_row()
+ {
+ offset_limit_cnt= 0;
+ select_limit_cnt= 1;
+ }
+
+ bool is_unlimited()
+ { return select_limit_cnt == HA_POS_ERROR; }
+ bool is_unrestricted()
+ { return select_limit_cnt == HA_POS_ERROR && offset_limit_cnt == 0; }
+ void set_unlimited()
+ { select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; }
+
+ bool check_offset(ha_rows sent)
+ {
+ return sent < offset_limit_cnt;
+ }
+ void remove_offset() { offset_limit_cnt= 0; }
+
+ ha_rows get_select_limit()
+ { return select_limit_cnt; }
+ ha_rows get_offset_limit()
+ { return offset_limit_cnt; }
+};
+
+#endif // INCLUDES_MARIADB_SQL_LIMIT_H
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 9d1c01a484d..91134bcbeb2 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -677,7 +677,8 @@ struct ilink
struct ilink **prev,*next;
static void *operator new(size_t size) throw ()
{
- return (void*)my_malloc((uint)size, MYF(MY_WME | MY_FAE | ME_FATAL));
+ return (void*)my_malloc(PSI_INSTRUMENT_ME,
+ (uint)size, MYF(MY_WME | MY_FAE | ME_FATAL));
}
static void operator delete(void* ptr_arg, size_t)
{
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index afc2f121167..2869011e313 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, MariaDB Corporation.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
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
@@ -190,7 +190,7 @@ class READ_INFO: public Load_data_param
bool read_mbtail(String *str)
{
int chlen;
- if ((chlen= my_charlen(charset(), str->end() - 1, str->end())) == 1)
+ if ((chlen= charset()->charlen(str->end() - 1, str->end())) == 1)
return false; // Single byte character found
for (uint32 length0= str->length() - 1 ; MY_CS_IS_TOOSMALL(chlen); )
{
@@ -201,7 +201,7 @@ class READ_INFO: public Load_data_param
return true; // EOF
}
str->append(chr);
- chlen= my_charlen(charset(), str->ptr() + length0, str->end());
+ chlen= charset()->charlen(str->ptr() + length0, str->end());
if (chlen == MY_CS_ILSEQ)
{
/**
@@ -391,6 +391,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
DBUG_RETURN(TRUE);
if (thd->lex->handle_list_of_derived(table_list, DT_PREPARE))
DBUG_RETURN(TRUE);
+
if (setup_tables_and_check_access(thd,
&thd->lex->first_select_lex()->context,
&thd->lex->first_select_lex()->
@@ -435,11 +436,14 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
}
table= table_list->table;
- transactional_table= table->file->has_transactions();
+ transactional_table= table->file->has_transactions_and_rollback();
#ifndef EMBEDDED_LIBRARY
is_concurrent= (table_list->lock_type == TL_WRITE_CONCURRENT_INSERT);
#endif
+ if (check_duplic_insert_without_overlaps(thd, table, handle_duplicates) != 0)
+ DBUG_RETURN(true);
+
if (!fields_vars.elements)
{
Field_iterator_table_ref field_iterator;
@@ -647,10 +651,14 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
- if ((table_list->table->file->ha_table_flags() & HA_DUPLICATE_POS) &&
- (error= table_list->table->file->ha_rnd_init_with_error(0)))
- goto err;
-
+ bool create_lookup_handler= handle_duplicates != DUP_ERROR;
+ if ((table_list->table->file->ha_table_flags() & HA_DUPLICATE_POS))
+ {
+ create_lookup_handler= true;
+ if ((error= table_list->table->file->ha_rnd_init_with_error(0)))
+ goto err;
+ }
+ table->file->prepare_for_insert(create_lookup_handler);
thd_progress_init(thd, 2);
if (table_list->table->validate_default_values_of_unset_fields(thd))
{
@@ -731,7 +739,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
/* since there is already an error, the possible error of
writing binary log will be ignored */
- if (thd->transaction.stmt.modified_non_trans_table)
+ if (thd->transaction->stmt.modified_non_trans_table)
(void) write_execute_load_query_log_event(thd, ex,
table_list->db.str,
table_list->table_name.str,
@@ -756,10 +764,10 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
(ulong) (info.records - info.copied),
(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;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
#ifndef EMBEDDED_LIBRARY
if (mysql_bin_log.is_open())
{
@@ -808,7 +816,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
my_ok(thd, info.copied + info.deleted, 0L, name);
err:
DBUG_ASSERT(transactional_table || !(info.copied || info.deleted) ||
- thd->transaction.stmt.modified_non_trans_table);
+ thd->transaction->stmt.modified_non_trans_table);
table->file->ha_release_auto_increment();
table->auto_increment_field_not_null= FALSE;
thd->abort_on_warning= 0;
@@ -1204,7 +1212,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
bool no_trans_update_stmt;
DBUG_ENTER("read_xml_field");
- no_trans_update_stmt= !table->file->has_transactions();
+ no_trans_update_stmt= !table->file->has_transactions_and_rollback();
for ( ; ; it.rewind())
{
@@ -1292,7 +1300,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
We don't need to reset auto-increment field since we are restoring
its default value at the beginning of each loop iteration.
*/
- thd->transaction.stmt.modified_non_trans_table= no_trans_update_stmt;
+ thd->transaction->stmt.modified_non_trans_table= no_trans_update_stmt;
thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
@@ -1587,7 +1595,7 @@ int READ_INFO::read_field()
}
}
data.append(chr);
- if (use_mb(charset()) && read_mbtail(&data))
+ if (charset()->use_mb() && read_mbtail(&data))
goto found_eof;
}
/*
@@ -1686,8 +1694,8 @@ int READ_INFO::next_line()
if (getbyte(&buf[0]))
return 1; // EOF
- if (use_mb(charset()) &&
- (chlen= my_charlen(charset(), buf, buf + 1)) != 1)
+ if (charset()->use_mb() &&
+ (chlen= charset()->charlen(buf, buf + 1)) != 1)
{
uint i;
for (i= 1; MY_CS_IS_TOOSMALL(chlen); )
@@ -1696,7 +1704,7 @@ int READ_INFO::next_line()
DBUG_ASSERT(chlen != 1);
if (getbyte(&buf[i++]))
return 1; // EOF
- chlen= my_charlen(charset(), buf, buf + i);
+ chlen= charset()->charlen(buf, buf + i);
}
/*
@@ -1867,7 +1875,7 @@ int READ_INFO::read_value(int delim, String *val)
else
{
val->append(chr);
- if (use_mb(charset()) && read_mbtail(val))
+ if (charset()->use_mb() && read_mbtail(val))
return my_b_EOF;
}
}
diff --git a/sql/sql_locale.h b/sql/sql_locale.h
index 87145a106cc..b7ce9f7ba1d 100644
--- a/sql/sql_locale.h
+++ b/sql/sql_locale.h
@@ -60,13 +60,13 @@ public:
grouping(grouping_par),
errmsgs(errmsgs_par)
{}
- uint repertoire() const
+ my_repertoire_t repertoire() const
{ return is_ascii ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_EXTENDED; }
};
/* Exported variables */
extern MY_LOCALE my_locale_en_US;
-extern MY_LOCALE *my_locales[];
+extern MYSQL_PLUGIN_IMPORT MY_LOCALE *my_locales[];
extern MY_LOCALE *my_default_lc_messages;
extern MY_LOCALE *my_default_lc_time_names;
diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc
index 2ad8d8a914a..a3d1a7242e4 100644
--- a/sql/sql_manager.cc
+++ b/sql/sql_manager.cc
@@ -51,7 +51,8 @@ bool mysql_manager_submit(void (*action)())
cb= &(*cb)->next;
if (!*cb)
{
- *cb= (struct handler_cb *)my_malloc(sizeof(struct handler_cb), MYF(MY_WME));
+ *cb= (struct handler_cb *)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(struct handler_cb), MYF(MY_WME));
if (!*cb)
result= TRUE;
else
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 6eb147d0480..3d43305431b 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -56,13 +56,6 @@
#include "sql_rename.h" // mysql_rename_tables
#include "sql_tablespace.h" // mysql_alter_tablespace
#include "hostname.h" // hostname_cache_refresh
-#include "sql_acl.h" // *_ACL, check_grant, is_acl_user,
- // has_any_table_level_privileges,
- // mysql_drop_user, mysql_rename_user,
- // check_grant_routine,
- // mysql_routine_grant,
- // mysql_show_grants,
- // sp_grant_privileges, ...
#include "sql_test.h" // mysql_print_status
#include "sql_select.h" // handle_select, mysql_select,
// mysql_explain_union
@@ -101,6 +94,7 @@
#include "sql_bootstrap.h"
#include "sql_sequence.h"
#include "opt_trace.h"
+#include "mysql/psi/mysql_sp.h"
#include "my_json_writer.h"
@@ -133,6 +127,10 @@ 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 *);
+static bool generate_incident_event(THD *thd);
+static int show_create_db(THD *thd, LEX *lex);
+static bool alter_routine(THD *thd, LEX *lex);
+static bool drop_routine(THD *thd, LEX *lex);
const char *any_db="*any*"; // Special symbol for check_access
@@ -439,16 +437,13 @@ bool stmt_causes_implicit_commit(THD *thd, uint mask)
DBUG_RETURN(FALSE);
switch (lex->sql_command) {
- case SQLCOM_DROP_TABLE:
- case SQLCOM_DROP_SEQUENCE:
- skip= (lex->tmp_table() ||
- (thd->variables.option_bits & OPTION_GTID_BEGIN));
- break;
case SQLCOM_ALTER_TABLE:
case SQLCOM_ALTER_SEQUENCE:
/* If ALTER TABLE of non-temporary table, do implicit commit */
skip= (lex->tmp_table());
break;
+ case SQLCOM_DROP_TABLE:
+ case SQLCOM_DROP_SEQUENCE:
case SQLCOM_CREATE_TABLE:
case SQLCOM_CREATE_SEQUENCE:
/*
@@ -679,7 +674,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_CREATE_USER]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_BINLOG_STAT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
@@ -990,7 +985,7 @@ int bootstrap(MYSQL_FILE *file)
thd->bootstrap=1;
my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
thd->max_client_packet_length= thd->net.max_packet;
- thd->security_ctx->master_access= ~(ulong)0;
+ thd->security_ctx->master_access= ALL_KNOWN_ACL;
#ifndef EMBEDDED_LIBRARY
mysql_thread_set_psi_id(thd->thread_id);
@@ -1002,7 +997,8 @@ int bootstrap(MYSQL_FILE *file)
thd->thread_stack= (char*) &thd;
thd->store_globals();
- thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
+ thd->security_ctx->user= (char*) my_strdup(key_memory_MPVIO_EXT_auth_info,
+ "boot", MYF(MY_WME));
thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=
thd->security_ctx->priv_role[0]= 0;
/*
@@ -1101,7 +1097,7 @@ int bootstrap(MYSQL_FILE *file)
thd->reset_kill_query(); /* Ensure that killed_errmsg is released */
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
+ thd->transaction->free();
thd->lex->restore_set_statement_var();
}
delete thd;
@@ -1289,7 +1285,7 @@ bool do_command(THD *thd)
Aborted by background rollbacker thread.
Handle error here and jump straight to out
*/
- if (wsrep_before_command(thd))
+ if (unlikely(wsrep_service_started) && wsrep_before_command(thd))
{
thd->store_globals();
WSREP_LOG_THD(thd, "enter found BF aborted");
@@ -1366,7 +1362,8 @@ out:
if (packet_length != packet_error)
{
/* there was a command to process, and before_command() has been called */
- wsrep_after_command_after_result(thd);
+ if (unlikely(wsrep_service_started))
+ wsrep_after_command_after_result(thd);
}
#endif /* WITH_WSREP */
DBUG_RETURN(return_value);
@@ -1397,8 +1394,7 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
LEX *lex= thd->lex;
/* Super user is allowed to do changes */
- if (((ulong)(thd->security_ctx->master_access & SUPER_ACL) ==
- (ulong)SUPER_ACL))
+ if ((thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) != NO_ACL)
DBUG_RETURN(FALSE);
/* Check if command doesn't update anything */
@@ -1438,10 +1434,10 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables)
static my_bool wsrep_read_only_option(THD *thd, TABLE_LIST *all_tables)
{
int opt_readonly_saved = opt_readonly;
- ulong flag_saved = (ulong)(thd->security_ctx->master_access & SUPER_ACL);
+ privilege_t flag_saved= thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY;
opt_readonly = 0;
- thd->security_ctx->master_access &= ~SUPER_ACL;
+ thd->security_ctx->master_access &= ~PRIV_IGNORE_READ_ONLY;
my_bool ret = !deny_updates_if_read_only_option(thd, all_tables);
@@ -1458,7 +1454,7 @@ static void wsrep_copy_query(THD *thd)
if (thd->wsrep_retry_query) {
my_free(thd->wsrep_retry_query);
}
- thd->wsrep_retry_query = (char *)my_malloc(
+ thd->wsrep_retry_query = (char *)my_malloc(PSI_INSTRUMENT_ME,
thd->wsrep_retry_query_len + 1, MYF(0));
strncpy(thd->wsrep_retry_query, thd->query(), thd->wsrep_retry_query_len);
thd->wsrep_retry_query[thd->wsrep_retry_query_len] = '\0';
@@ -1506,6 +1502,31 @@ uint maria_multi_check(THD *thd, char *packet, size_t packet_length)
}
+#if defined(WITH_ARIA_STORAGE_ENGINE)
+class Silence_all_errors : public Internal_error_handler
+{
+ char m_message[MYSQL_ERRMSG_SIZE];
+ int error;
+public:
+ Silence_all_errors():error(0) {}
+ virtual ~Silence_all_errors() {}
+
+ virtual bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sql_state,
+ Sql_condition::enum_warning_level *level,
+ const char* msg,
+ Sql_condition ** cond_hdl)
+ {
+ error= sql_errno;
+ *cond_hdl= NULL;
+ strmake_buf(m_message, msg);
+ return true; // Error handled
+ }
+};
+#endif
+
+
/**
Perform one connection-level (COM_XXXX) command.
@@ -1658,14 +1679,20 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
thd->status_var.com_other++;
#ifdef WITH_WSREP
- wsrep_after_command_ignore_result(thd);
- wsrep_close(thd);
+ if (unlikely(wsrep_service_started))
+ {
+ wsrep_after_command_ignore_result(thd);
+ wsrep_close(thd);
+ }
#endif /* WITH_WSREP */
thd->change_user();
thd->clear_error(); // if errors from rollback
#ifdef WITH_WSREP
- wsrep_open(thd);
- wsrep_before_command(thd);
+ if (unlikely(wsrep_service_started))
+ {
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+ }
#endif /* WITH_WSREP */
/* Restore original charset from client authentication packet.*/
if(thd->org_charset)
@@ -1679,13 +1706,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
status_var_increment(thd->status_var.com_other);
#ifdef WITH_WSREP
- wsrep_after_command_ignore_result(thd);
- wsrep_close(thd);
+ if (unlikely(wsrep_service_started))
+ {
+ wsrep_after_command_ignore_result(thd);
+ wsrep_close(thd);
+ }
#endif /* WITH_WSREP */
thd->change_user();
#ifdef WITH_WSREP
- wsrep_open(thd);
- wsrep_before_command(thd);
+ if (unlikely(wsrep_service_started))
+ {
+ wsrep_open(thd);
+ wsrep_before_command(thd);
+ }
#endif /* WITH_WSREP */
thd->clear_error(); // if errors from rollback
@@ -1719,7 +1752,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
else
auth_rc= acl_authenticate(thd, packet_length);
- mysql_audit_notify_connection_change_user(thd);
+ mysql_audit_notify_connection_change_user(thd, &save_security_ctx);
if (auth_rc)
{
/* Free user if allocated by acl_authenticate */
@@ -1846,8 +1879,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*/
char *beginning_of_next_stmt= (char*) parser_state.m_lip.found_semicolon;
- ha_maria_implicit_commit(thd, FALSE);
-
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
thd->protocol->end_statement();
@@ -1900,8 +1931,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
com_statement_info[command].m_key,
thd->db.str, thd->db.length,
- thd->charset());
- THD_STAGE_INFO(thd, stage_init);
+ thd->charset(), NULL);
+ THD_STAGE_INFO(thd, stage_starting);
MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, beginning_of_next_stmt,
length);
@@ -2043,7 +2074,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysqld_list_fields(thd,&table_list,fields);
thd->lex->unit.cleanup();
/* No need to rollback statement transaction, it's not started. */
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
@@ -2083,7 +2114,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
status_var_increment(thd->status_var.com_other);
thd->query_plan_flags|= QPLAN_ADMIN;
- if (check_global_access(thd, REPL_SLAVE_ACL))
+ if (check_global_access(thd, PRIV_COM_BINLOG_DUMP))
break;
/* TODO: The following has to be changed to an 8 byte integer */
@@ -2213,13 +2244,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
my_snprintf(buff, buff_len - 1,
"Uptime: %lu Threads: %d Questions: %lu "
- "Slow queries: %lu Opens: %lu Flush tables: %lld "
+ "Slow queries: %lu Opens: %lu "
"Open tables: %u Queries per second avg: %u.%03u",
uptime,
(int) thread_count, (ulong) thd->query_id,
current_global_status_var->long_query_count,
current_global_status_var->opened_tables,
- tdc_refresh_version(),
tc_records(),
(uint) (queries_per_second1000 / 1000),
(uint) (queries_per_second1000 % 1000));
@@ -2240,12 +2270,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_PROCESS_INFO:
status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]);
if (!thd->security_ctx->priv_user[0] &&
- check_global_access(thd, PROCESS_ACL))
+ check_global_access(thd, PRIV_COM_PROCESS_INFO))
break;
general_log_print(thd, command, NullS);
mysqld_list_processes(thd,
- thd->security_ctx->master_access & PROCESS_ACL ?
- NullS : thd->security_ctx->priv_user, 0);
+ thd->security_ctx->master_access & PRIV_COM_PROCESS_INFO ?
+ NullS : thd->security_ctx->priv_user, 0);
break;
case COM_PROCESS_KILL:
{
@@ -2277,7 +2307,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_DEBUG:
status_var_increment(thd->status_var.com_other);
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_DEBUG))
break; /* purecov: inspected */
mysql_print_status();
general_log_print(thd, command, NullS);
@@ -2374,46 +2404,52 @@ com_multi_end:
}
dispatch_end:
+ do_end_of_statement= true;
#ifdef WITH_WSREP
/*
- BF aborted before sending response back to client
+ Next test should really be WSREP(thd), but that causes a failure when doing
+ 'set WSREP_ON=0'
*/
- if (thd->killed == KILL_QUERY)
- {
- WSREP_DEBUG("THD is killed at dispatch_end");
- }
- wsrep_after_command_before_result(thd);
- if (wsrep_current_error(thd) &&
- !(command == COM_STMT_PREPARE ||
- command == COM_STMT_FETCH ||
- command == COM_STMT_SEND_LONG_DATA ||
- command == COM_STMT_CLOSE
- ))
- {
- /* todo: Pass wsrep client state current error to override */
- wsrep_override_error(thd, wsrep_current_error(thd),
- wsrep_current_error_status(thd));
- WSREP_LOG_THD(thd, "leave");
- }
- if (WSREP(thd))
+ if (unlikely(wsrep_service_started))
{
/*
- MDEV-10812
- In the case of COM_QUIT/COM_STMT_CLOSE thread status should be disabled.
+ BF aborted before sending response back to client
*/
- DBUG_ASSERT((command != COM_QUIT && command != COM_STMT_CLOSE)
+ if (thd->killed == KILL_QUERY)
+ {
+ WSREP_DEBUG("THD is killed at dispatch_end");
+ }
+ wsrep_after_command_before_result(thd);
+ if (wsrep_current_error(thd) &&
+ !(command == COM_STMT_PREPARE ||
+ command == COM_STMT_FETCH ||
+ command == COM_STMT_SEND_LONG_DATA ||
+ command == COM_STMT_CLOSE
+ ))
+ {
+ /* todo: Pass wsrep client state current error to override */
+ wsrep_override_error(thd, wsrep_current_error(thd),
+ wsrep_current_error_status(thd));
+ WSREP_LOG_THD(thd, "leave");
+ }
+ if (WSREP(thd))
+ {
+ /*
+ MDEV-10812
+ In the case of COM_QUIT/COM_STMT_CLOSE thread status should be disabled.
+ */
+ DBUG_ASSERT((command != COM_QUIT && command != COM_STMT_CLOSE)
|| thd->get_stmt_da()->is_disabled());
- DBUG_ASSERT(thd->wsrep_trx().state() != wsrep::transaction::s_replaying);
- /* wsrep BF abort in query exec phase */
- mysql_mutex_lock(&thd->LOCK_thd_kill);
- do_end_of_statement= thd_is_connection_alive(thd);
- mysql_mutex_unlock(&thd->LOCK_thd_kill);
+ DBUG_ASSERT(thd->wsrep_trx().state() != wsrep::transaction::s_replaying);
+ /* wsrep BF abort in query exec phase */
+ mysql_mutex_lock(&thd->LOCK_thd_kill);
+ do_end_of_statement= thd_is_connection_alive(thd);
+ mysql_mutex_unlock(&thd->LOCK_thd_kill);
+ }
}
- else
- do_end_of_statement= true;
-
#endif /* WITH_WSREP */
+
if (do_end_of_statement)
{
DBUG_ASSERT(thd->derived_tables == NULL &&
@@ -2671,6 +2707,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
/* 'parent_lex' is used in init_query() so it must be before it. */
schema_select_lex->parent_lex= lex;
schema_select_lex->init_query();
+ schema_select_lex->select_number= 0;
if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
MDL_SHARED_READ))
DBUG_RETURN(1);
@@ -2831,7 +2868,7 @@ bool sp_process_definer(THD *thd)
!my_strcasecmp(system_charset_info, d->host.str,
thd->security_ctx->priv_host);
if (!curuserhost && !currole &&
- check_global_access(thd, SUPER_ACL, false))
+ check_global_access(thd, PRIV_DEFINER_CLAUSE, false))
DBUG_RETURN(TRUE);
}
@@ -2863,7 +2900,8 @@ bool sp_process_definer(THD *thd)
@return FALSE in case of success, TRUE in case of error.
*/
-static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+static bool __attribute__ ((noinline))
+lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
MDL_deadlock_and_lock_abort_error_handler deadlock_handler;
@@ -3024,7 +3062,8 @@ static bool do_execute_sp(THD *thd, sp_head *sp)
}
-static int mysql_create_routine(THD *thd, LEX *lex)
+static int __attribute__ ((noinline))
+mysql_create_routine(THD *thd, LEX *lex)
{
DBUG_ASSERT(lex->sphead != 0);
DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
@@ -3053,7 +3092,7 @@ static int mysql_create_routine(THD *thd, LEX *lex)
const LEX_CSTRING *name= lex->sphead->name();
#ifdef HAVE_DLOPEN
- if (lex->sphead->m_handler->type() == TYPE_ENUM_FUNCTION)
+ if (lex->sphead->m_handler->type() == SP_TYPE_FUNCTION)
{
udf_func *udf = find_udf(name->str, name->length);
@@ -3069,6 +3108,7 @@ static int mysql_create_routine(THD *thd, LEX *lex)
return true;
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+
if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead))
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -3086,14 +3126,13 @@ static int mysql_create_routine(THD *thd, LEX *lex)
statement takes metadata locks should be detected by a deadlock
detector in MDL subsystem and reported as errors.
- No need to commit/rollback statement transaction, it's not started.
-
TODO: Long-term we should either ensure that implicit GRANT statement
is written into binary log as a separate statement or make both
creation of routine and implicit GRANT parts of one fully atomic
statement.
*/
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ if (trans_commit_stmt(thd))
+ goto wsrep_error_label;
close_thread_tables(thd);
/*
Check if the definer exists on slave,
@@ -3137,7 +3176,9 @@ static int mysql_create_routine(THD *thd, LEX *lex)
#endif
return false;
}
-#ifdef WITH_WSREP
+ (void) trans_commit_stmt(thd);
+
+#if !defined(NO_EMBEDDED_ACCESS_CHECKS) || defined(WITH_WSREP)
wsrep_error_label:
#endif
return true;
@@ -3159,7 +3200,8 @@ wsrep_error_label:
from other cases: bad database error, no access error.
This can be done by testing thd->is_error().
*/
-static bool prepare_db_action(THD *thd, ulong want_access, LEX_CSTRING *dbname)
+static bool prepare_db_action(THD *thd, privilege_t want_access,
+ LEX_CSTRING *dbname)
{
if (check_db_name((LEX_STRING*)dbname))
{
@@ -3313,7 +3355,7 @@ mysql_execute_command(THD *thd)
DBUG_RETURN(1);
}
- DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
+ DBUG_ASSERT(thd->transaction->stmt.is_empty() || thd->in_sub_stmt);
/*
Each statement or replication event which might produce deadlock
should handle transaction rollback on its own. So by the start of
@@ -3389,7 +3431,8 @@ mysql_execute_command(THD *thd)
according to slave filtering rules.
Returning success without producing any errors in this case.
*/
- if (!thd->lex->create_info.if_exists())
+ if (!thd->lex->create_info.if_exists() &&
+ !(thd->variables.option_bits & OPTION_IF_EXISTS))
DBUG_RETURN(0);
/*
DROP TRIGGER IF NOT EXISTS will return without an error later
@@ -3539,7 +3582,7 @@ mysql_execute_command(THD *thd)
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);
+ 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;
@@ -3696,7 +3739,7 @@ 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());
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
{
/* Commit the normal transaction if one is active. */
@@ -3710,7 +3753,7 @@ mysql_execute_command(THD *thd)
goto error;
}
}
- thd->transaction.stmt.mark_trans_did_ddl();
+ thd->transaction->stmt.mark_trans_did_ddl();
#ifdef WITH_WSREP
/* Clean up the previous transaction on implicit commit */
if (wsrep_thd_is_local(thd) && wsrep_after_statement(thd))
@@ -3816,7 +3859,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_EXPLAIN:
{
if (!thd->security_ctx->priv_user[0] &&
- check_global_access(thd,PROCESS_ACL))
+ check_global_access(thd, PRIV_STMT_SHOW_EXPLAIN))
break;
/*
@@ -3880,8 +3923,8 @@ mysql_execute_command(THD *thd)
lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
requires FILE_ACL access.
*/
- ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL;
+ privilege_t privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL;
if (all_tables)
res= check_table_access(thd,
@@ -3934,7 +3977,7 @@ mysql_execute_command(THD *thd)
#ifndef EMBEDDED_LIBRARY
case SQLCOM_PURGE:
{
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_PURGE_BINLOG))
goto error;
/* PURGE MASTER LOGS TO 'file' */
res = purge_master_logs(thd, lex->to_log);
@@ -3944,7 +3987,7 @@ mysql_execute_command(THD *thd)
{
Item *it;
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_PURGE_BINLOG))
goto error;
/* PURGE MASTER LOGS BEFORE 'data' */
it= (Item *)lex->value_list.head();
@@ -3991,16 +4034,23 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
case SQLCOM_SHOW_SLAVE_HOSTS:
{
- if (check_global_access(thd, REPL_SLAVE_ACL))
+ if (check_global_access(thd, PRIV_STMT_SHOW_SLAVE_HOSTS))
goto error;
res = show_slave_hosts(thd);
break;
}
- case SQLCOM_SHOW_RELAYLOG_EVENTS: /* fall through */
+ case SQLCOM_SHOW_RELAYLOG_EVENTS:
+ {
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
+ if (check_global_access(thd, PRIV_STMT_SHOW_RELAYLOG_EVENTS))
+ goto error;
+ res = mysql_show_binlog_events(thd);
+ break;
+ }
case SQLCOM_SHOW_BINLOG_EVENTS:
{
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
- if (check_global_access(thd, REPL_SLAVE_ACL))
+ if (check_global_access(thd, PRIV_STMT_SHOW_BINLOG_EVENTS))
goto error;
res = mysql_show_binlog_events(thd);
break;
@@ -4037,7 +4087,7 @@ mysql_execute_command(THD *thd)
bool new_master= 0;
bool master_info_added;
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_CHANGE_MASTER))
goto error;
/*
In this code it's ok to use LOCK_active_mi as we are adding new things
@@ -4091,35 +4141,11 @@ mysql_execute_command(THD *thd)
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
- case SQLCOM_SHOW_SLAVE_STAT:
- {
- /* Accept one of two privileges */
- if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
- goto error;
- if (lex->verbose)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- res= show_all_master_info(thd);
- mysql_mutex_unlock(&LOCK_active_mi);
- }
- else
- {
- LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
- Master_info *mi;
- if ((mi= get_master_info(&lex_mi->connection_name,
- Sql_condition::WARN_LEVEL_ERROR)))
- {
- res= show_master_info(thd, mi, 0);
- mi->release();
- }
- }
- break;
- }
- case SQLCOM_SHOW_MASTER_STAT:
+ case SQLCOM_SHOW_BINLOG_STAT:
{
/* Accept one of two privileges */
- if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
+ if (check_global_access(thd, PRIV_STMT_SHOW_BINLOG_STATUS))
goto error;
res = show_binlog_info(thd);
break;
@@ -4128,20 +4154,23 @@ mysql_execute_command(THD *thd)
#endif /* HAVE_REPLICATION */
case SQLCOM_SHOW_ENGINE_STATUS:
{
- if (check_global_access(thd, PROCESS_ACL))
+ if (check_global_access(thd, PRIV_STMT_SHOW_ENGINE_STATUS))
goto error;
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
break;
}
case SQLCOM_SHOW_ENGINE_MUTEX:
{
- if (check_global_access(thd, PROCESS_ACL))
+ if (check_global_access(thd, PRIV_STMT_SHOW_ENGINE_MUTEX))
goto error;
res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
break;
}
- case SQLCOM_CREATE_INDEX:
case SQLCOM_DROP_INDEX:
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+ /* fall through */
+ case SQLCOM_CREATE_INDEX:
/*
CREATE INDEX and DROP INDEX are implemented by calling ALTER
TABLE with proper arguments.
@@ -4161,16 +4190,18 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
- WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL);
bzero((char*) &create_info, sizeof(create_info));
create_info.db_type= 0;
create_info.row_type= ROW_TYPE_NOT_USED;
create_info.default_table_charset= thd->variables.collation_database;
+ create_info.alter_info= &alter_info;
+
+ WSREP_TO_ISOLATION_BEGIN(first_table->db.str, first_table->table_name.str, NULL);
res= mysql_alter_table(thd, &first_table->db, &first_table->table_name,
&create_info, first_table, &alter_info,
- 0, (ORDER*) 0, 0);
+ 0, (ORDER*) 0, 0, lex->if_exists());
break;
}
#ifdef HAVE_REPLICATION
@@ -4282,7 +4313,10 @@ mysql_execute_command(THD *thd)
WSREP_TO_ISOLATION_BEGIN(0, 0, first_table);
- if (mysql_rename_tables(thd, first_table, 0))
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+
+ if (mysql_rename_tables(thd, first_table, 0, lex->if_exists()))
goto error;
break;
}
@@ -4294,7 +4328,7 @@ mysql_execute_command(THD *thd)
goto error;
#else
{
- if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
+ if (check_global_access(thd, PRIV_STMT_SHOW_BINARY_LOGS))
goto error;
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
res = show_binlogs(thd);
@@ -4368,7 +4402,7 @@ mysql_execute_command(THD *thd)
select_lex->where,
select_lex->order_list.elements,
select_lex->order_list.first,
- unit->select_limit_cnt,
+ unit->lim.get_select_limit(),
lex->ignore, &found, &updated);
MYSQL_UPDATE_DONE(res, found, updated);
/* mysql_update return 2 if we need to switch to multi-update */
@@ -4431,7 +4465,7 @@ mysql_execute_command(THD *thd)
if (res)
break;
if (opt_readonly &&
- !(thd->security_ctx->master_access & SUPER_ACL) &&
+ !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) &&
some_non_temp_table_to_be_updated(thd, all_tables))
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
@@ -4468,44 +4502,13 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_REPLACE:
-#ifndef DBUG_OFF
- if (mysql_bin_log.is_open())
- {
- /*
- Generate an incident log event before writing the real event
- to the binary log. We put this event is before the statement
- since that makes it simpler to check that the statement was
- not executed on the slave (since incidents usually stop the
- slave).
-
- Observe that any row events that are generated will be
- generated before.
-
- This is only for testing purposes and will not be present in a
- release build.
- */
-
- Incident incident= INCIDENT_NONE;
- DBUG_PRINT("debug", ("Just before generate_incident()"));
- DBUG_EXECUTE_IF("incident_database_resync_on_replace",
- incident= INCIDENT_LOST_EVENTS;);
- if (incident)
- {
- Incident_log_event ev(thd, incident);
- (void) mysql_bin_log.write(&ev); /* error is ignored */
- if (mysql_bin_log.rotate_and_purge(true))
- {
- res= 1;
- break;
- }
- }
- DBUG_PRINT("debug", ("Just after generate_incident()"));
- }
-#endif
+ if ((res= generate_incident_event(thd)))
+ break;
/* fall through */
case SQLCOM_INSERT:
{
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
+ select_result *sel_result= NULL;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
@@ -4526,9 +4529,41 @@ mysql_execute_command(THD *thd)
break;
MYSQL_INSERT_START(thd->query());
+ Protocol* save_protocol=NULL;
+
+ if (lex->has_returning())
+ {
+ status_var_increment(thd->status_var.feature_insert_returning);
+
+ /* This is INSERT ... RETURNING. It will return output to the client */
+ if (thd->lex->analyze_stmt)
+ {
+ /*
+ Actually, it is ANALYZE .. INSERT .. RETURNING. We need to produce
+ output and then discard it.
+ */
+ sel_result= new (thd->mem_root) select_send_analyze(thd);
+ save_protocol= thd->protocol;
+ thd->protocol= new Protocol_discard(thd);
+ }
+ else
+ {
+ if (!(sel_result= new (thd->mem_root) select_send(thd)))
+ goto error;
+ }
+ }
+
res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
- lex->update_list, lex->value_list,
- lex->duplicates, lex->ignore);
+ lex->update_list, lex->value_list,
+ lex->duplicates, lex->ignore, sel_result);
+ if (save_protocol)
+ {
+ delete thd->protocol;
+ thd->protocol= save_protocol;
+ }
+ if (!res && thd->lex->analyze_stmt)
+ res= thd->lex->explain->send_explain(thd);
+ delete sel_result;
MYSQL_INSERT_DONE(res, (ulong) thd->get_row_count_func());
/*
If we have inserted into a VIEW, and the base table has
@@ -4543,12 +4578,8 @@ mysql_execute_command(THD *thd)
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("after_mysql_insert",
{
- const char act1[]=
- "now "
- "wait_for signal.continue";
- const char act2[]=
- "now "
- "signal signal.continued";
+ const char act1[]= "now wait_for signal.continue";
+ const char act2[]= "now signal signal.continued";
DBUG_ASSERT(debug_sync_service);
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act1)));
@@ -4564,6 +4595,7 @@ mysql_execute_command(THD *thd)
{
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
select_insert *sel_result;
+ select_result *result= NULL;
bool explain= MY_TEST(lex->describe);
DBUG_ASSERT(first_table == all_tables && first_table != 0);
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE);
@@ -4612,6 +4644,31 @@ mysql_execute_command(THD *thd)
Only the INSERT table should be merged. Other will be handled by
select.
*/
+
+ Protocol* save_protocol=NULL;
+
+ if (lex->has_returning())
+ {
+ status_var_increment(thd->status_var.feature_insert_returning);
+
+ /* This is INSERT ... RETURNING. It will return output to the client */
+ if (thd->lex->analyze_stmt)
+ {
+ /*
+ Actually, it is ANALYZE .. INSERT .. RETURNING. We need to produce
+ output and then discard it.
+ */
+ result= new (thd->mem_root) select_send_analyze(thd);
+ save_protocol= thd->protocol;
+ thd->protocol= new Protocol_discard(thd);
+ }
+ else
+ {
+ if (!(result= new (thd->mem_root) select_send(thd)))
+ goto error;
+ }
+ }
+
/* Skip first table, which is the table we are inserting in */
TABLE_LIST *second_table= first_table->next_local;
/*
@@ -4622,17 +4679,19 @@ mysql_execute_command(THD *thd)
be done properly as well)
*/
select_lex->table_list.first= second_table;
- select_lex->context.table_list=
+ select_lex->context.table_list=
select_lex->context.first_name_resolution_table= second_table;
- res= mysql_insert_select_prepare(thd);
- if (!res && (sel_result= new (thd->mem_root) select_insert(thd,
- first_table,
- first_table->table,
- &lex->field_list,
- &lex->update_list,
- &lex->value_list,
- lex->duplicates,
- lex->ignore)))
+ res= mysql_insert_select_prepare(thd, result);
+ if (!res &&
+ (sel_result= new (thd->mem_root)
+ select_insert(thd, first_table,
+ first_table->table,
+ &lex->field_list,
+ &lex->update_list,
+ &lex->value_list,
+ lex->duplicates,
+ lex->ignore,
+ result)))
{
if (lex->analyze_stmt)
((select_result_interceptor*)sel_result)->disable_my_ok_calls();
@@ -4668,7 +4727,20 @@ mysql_execute_command(THD *thd)
}
delete sel_result;
}
-
+ else if (res < 0)
+ {
+ /*
+ Insert should be ignored but we have to log the query in statement
+ format in the binary log
+ */
+ res= thd->binlog_current_query_unfiltered();
+ }
+ delete result;
+ if (save_protocol)
+ {
+ delete thd->protocol;
+ thd->protocol= save_protocol;
+ }
if (!res && (explain || lex->analyze_stmt))
res= thd->lex->explain->send_explain(thd);
@@ -4701,10 +4773,9 @@ mysql_execute_command(THD *thd)
unit->set_limit(select_lex);
MYSQL_DELETE_START(thd->query());
- Protocol * UNINIT_VAR(save_protocol);
- bool replaced_protocol= false;
+ Protocol *save_protocol= NULL;
- if (!select_lex->item_list.is_empty())
+ if (lex->has_returning())
{
/* This is DELETE ... RETURNING. It will return output to the client */
if (thd->lex->analyze_stmt)
@@ -4714,7 +4785,6 @@ mysql_execute_command(THD *thd)
output and then discard it.
*/
sel_result= new (thd->mem_root) select_send_analyze(thd);
- replaced_protocol= true;
save_protocol= thd->protocol;
thd->protocol= new Protocol_discard(thd);
}
@@ -4727,10 +4797,10 @@ mysql_execute_command(THD *thd)
res = mysql_delete(thd, all_tables,
select_lex->where, &select_lex->order_list,
- unit->select_limit_cnt, select_lex->options,
+ unit->lim.get_select_limit(), select_lex->options,
lex->result ? lex->result : sel_result);
- if (replaced_protocol)
+ if (save_protocol)
{
delete thd->protocol;
thd->protocol= save_protocol;
@@ -4784,7 +4854,6 @@ mysql_execute_command(THD *thd)
goto multi_delete_error;
res= mysql_select(thd,
select_lex->get_table_list(),
- select_lex->with_wild,
select_lex->item_list,
select_lex->where,
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
@@ -4832,6 +4901,8 @@ mysql_execute_command(THD *thd)
}
else
{
+ if (thd->transaction->xid_state.check_has_uncommitted_xa())
+ goto error;
status_var_decrement(thd->status_var.com_stat[lex->sql_command]);
status_var_increment(thd->status_var.com_drop_tmp_table);
@@ -4844,10 +4915,12 @@ mysql_execute_command(THD *thd)
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)
+ if ((thd->slave_thread && !thd->slave_expected_error &&
+ slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT) ||
+ thd->variables.option_bits & OPTION_IF_EXISTS)
lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+#ifdef WITH_WSREP
if (WSREP(thd))
{
for (TABLE_LIST *table= all_tables; table; table= table->next_global)
@@ -4861,10 +4934,11 @@ mysql_execute_command(THD *thd)
}
}
}
-
+#endif /* WITH_WSREP */
+
/* DDL and binlog write order are protected by metadata locks. */
res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table(),
- lex->table_type == TABLE_TYPE_SEQUENCE);
+ lex->table_type == TABLE_TYPE_SEQUENCE, 0);
/*
When dropping temporary tables if @@session_track_state_change is ON
@@ -4872,19 +4946,19 @@ mysql_execute_command(THD *thd)
*/
if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
{
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
}
break;
}
case SQLCOM_SHOW_PROCESSLIST:
if (!thd->security_ctx->priv_user[0] &&
- check_global_access(thd,PROCESS_ACL))
+ check_global_access(thd, PRIV_STMT_SHOW_PROCESSLIST))
break;
mysqld_list_processes(thd,
- (thd->security_ctx->master_access & PROCESS_ACL ?
- NullS :
- thd->security_ctx->priv_user),
- lex->verbose);
+ (thd->security_ctx->master_access & PRIV_STMT_SHOW_PROCESSLIST ?
+ NullS :
+ thd->security_ctx->priv_user),
+ lex->verbose);
break;
case SQLCOM_SHOW_AUTHORS:
res= mysqld_show_authors(thd);
@@ -4919,17 +4993,18 @@ mysql_execute_command(THD *thd)
case SQLCOM_LOAD:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
- uint privilege= (lex->duplicates == DUP_REPLACE ?
- INSERT_ACL | DELETE_ACL : INSERT_ACL) |
- (lex->local_file ? 0 : FILE_ACL);
+ privilege_t privilege= (lex->duplicates == DUP_REPLACE ?
+ INSERT_ACL | DELETE_ACL : INSERT_ACL) |
+ (lex->local_file ? NO_ACL : FILE_ACL);
if (lex->local_file)
{
if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
!opt_local_infile)
{
- my_message(ER_NOT_ALLOWED_COMMAND, ER_THD(thd, ER_NOT_ALLOWED_COMMAND), MYF(0));
- goto error;
+ my_message(ER_LOAD_INFILE_CAPABILITY_DISABLED,
+ ER_THD(thd, ER_LOAD_INFILE_CAPABILITY_DISABLED), MYF(0));
+ goto error;
}
}
@@ -4979,9 +5054,11 @@ mysql_execute_command(THD *thd)
if (thd->variables.option_bits & OPTION_TABLE_LOCK)
{
res= trans_commit_implicit(thd);
- thd->locked_tables_list.unlock_locked_tables(thd);
+ if (thd->locked_tables_list.unlock_locked_tables(thd))
+ res= 1;
thd->mdl_context.release_transactional_locks();
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
+ thd->reset_binlog_for_next_statement();
}
if (thd->global_read_lock.is_acquired() &&
thd->current_backup_stage == BACKUP_FINISHED)
@@ -4993,7 +5070,8 @@ mysql_execute_command(THD *thd)
case SQLCOM_LOCK_TABLES:
/* We must end the transaction first, regardless of anything */
res= trans_commit_implicit(thd);
- thd->locked_tables_list.unlock_locked_tables(thd);
+ if (thd->locked_tables_list.unlock_locked_tables(thd))
+ res= 1;
/* Release transactional metadata locks. */
thd->mdl_context.release_transactional_locks();
if (res)
@@ -5074,16 +5152,23 @@ mysql_execute_command(THD *thd)
(CREATE_ACL | DROP_ACL) : CREATE_ACL,
&lex->name))
break;
+
WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL);
+
res= mysql_create_db(thd, &lex->name,
lex->create_info, &lex->create_info);
break;
}
case SQLCOM_DROP_DB:
{
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+
if (prepare_db_action(thd, DROP_ACL, &lex->name))
break;
+
WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL);
+
res= mysql_rm_db(thd, &lex->name, lex->if_exists());
break;
}
@@ -5115,7 +5200,9 @@ mysql_execute_command(THD *thd)
res= 1;
break;
}
+
WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL);
+
res= mysql_upgrade_db(thd, db);
if (!res)
my_ok(thd);
@@ -5126,31 +5213,16 @@ mysql_execute_command(THD *thd)
LEX_CSTRING *db= &lex->name;
if (prepare_db_action(thd, ALTER_ACL, db))
break;
+
WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL);
+
res= mysql_alter_db(thd, db, &lex->create_info);
break;
}
case SQLCOM_SHOW_CREATE_DB:
- {
- char db_name_buff[NAME_LEN+1];
- LEX_CSTRING db_name;
- DBUG_EXECUTE_IF("4x_server_emul",
- my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
-
- db_name.str= db_name_buff;
- db_name.length= lex->name.length;
- strmov(db_name_buff, lex->name.str);
-
WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
-
- if (check_db_name((LEX_STRING*) &db_name))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
- break;
- }
- res= mysqld_show_create_db(thd, &db_name, &lex->name, lex->create_info);
+ res= show_create_db(thd, lex);
break;
- }
case SQLCOM_CREATE_EVENT:
case SQLCOM_ALTER_EVENT:
#ifdef HAVE_EVENT_SCHEDULER
@@ -5217,6 +5289,7 @@ mysql_execute_command(THD *thd)
break;
#ifdef HAVE_DLOPEN
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+
if (!(res = mysql_create_function(thd, &lex->udf)))
my_ok(thd);
#else
@@ -5234,7 +5307,9 @@ mysql_execute_command(THD *thd)
"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,
lex->sql_command == SQLCOM_CREATE_ROLE)))
@@ -5247,8 +5322,10 @@ mysql_execute_command(THD *thd)
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);
+
+ /* Conditionally writes to binlog */
if (!(res= mysql_drop_user(thd, lex->users_list,
lex->sql_command == SQLCOM_DROP_ROLE)))
my_ok(thd);
@@ -5260,8 +5337,10 @@ mysql_execute_command(THD *thd)
if (check_access(thd, UPDATE_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);
+
+ /* Conditionally writes to binlog */
if (lex->sql_command == SQLCOM_ALTER_USER)
res= mysql_alter_user(thd, lex->users_list);
else
@@ -5276,123 +5355,19 @@ mysql_execute_command(THD *thd)
check_global_access(thd,CREATE_USER_ACL))
break;
- /* Conditionally writes to binlog */
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+
+ /* Conditionally writes to binlog */
if (!(res = mysql_revoke_all(thd, lex->users_list)))
my_ok(thd);
break;
}
- case SQLCOM_REVOKE:
- case SQLCOM_GRANT:
- {
- if (lex->type != TYPE_ENUM_PROXY &&
- check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- first_table ? first_table->db.str : select_lex->db.str,
- first_table ? &first_table->grant.privilege : NULL,
- first_table ? &first_table->grant.m_internal : NULL,
- first_table ? 0 : 1, 0))
- goto error;
-
- /* Replicate current user as grantor */
- thd->binlog_invoker(false);
-
- if (thd->security_ctx->user) // If not replication
- {
- LEX_USER *user;
- bool first_user= TRUE;
- List_iterator <LEX_USER> user_list(lex->users_list);
- while ((user= user_list++))
- {
- if (specialflag & SPECIAL_NO_RESOLVE &&
- hostname_requires_resolving(user->host.str))
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WARN_HOSTNAME_WONT_WORK,
- ER_THD(thd, ER_WARN_HOSTNAME_WONT_WORK));
-
- /*
- 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;
- }
- }
- }
- if (first_table)
- {
- const Sp_handler *sph= Sp_handler::handler((stored_procedure_type)
- lex->type);
- if (sph)
- {
- uint grants= lex->all_privileges
- ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
- : lex->grant;
- if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, sph, 0))
- goto error;
- /* Conditionally writes to binlog */
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
- res= mysql_routine_grant(thd, all_tables, sph,
- lex->users_list, grants,
- lex->sql_command == SQLCOM_REVOKE, TRUE);
- if (!res)
- my_ok(thd);
- }
- else
- {
- if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
- all_tables, FALSE, UINT_MAX, FALSE))
- goto error;
- /* Conditionally writes to binlog */
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
- res= mysql_table_grant(thd, all_tables, lex->users_list,
- lex->columns, lex->grant,
- lex->sql_command == SQLCOM_REVOKE);
- }
- }
- else
- {
- if (lex->columns.elements || (lex->type && lex->type != TYPE_ENUM_PROXY))
- {
- my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
- MYF(0));
- goto error;
- }
- else
- {
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
- /* Conditionally writes to binlog */
- res= mysql_grant(thd, select_lex->db.str, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE,
- lex->type == TYPE_ENUM_PROXY);
- }
- if (!res)
- {
- if (lex->sql_command == SQLCOM_GRANT)
- {
- List_iterator <LEX_USER> str_list(lex->users_list);
- LEX_USER *user, *tmp_user;
- while ((tmp_user=str_list++))
- {
- if (!(user= get_current_user(thd, tmp_user)))
- goto error;
- reset_mqh(user, 0);
- }
- }
- }
- }
- 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);
@@ -5606,7 +5581,8 @@ mysql_execute_command(THD *thd)
res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- unit->select_limit_cnt, unit->offset_limit_cnt);
+ unit->lim.get_select_limit(),
+ unit->lim.get_offset_limit());
break;
case SQLCOM_BEGIN:
@@ -5721,159 +5697,35 @@ mysql_execute_command(THD *thd)
break; /* break super switch */
} /* end case group bracket */
case SQLCOM_COMPOUND:
+ {
+ sp_head *sp= lex->sphead;
DBUG_ASSERT(all_tables == 0);
DBUG_ASSERT(thd->in_sub_stmt == 0);
- lex->sphead->m_sql_mode= thd->variables.sql_mode;
+ sp->m_sql_mode= thd->variables.sql_mode;
+ sp->m_sp_share= MYSQL_GET_SP_SHARE(sp->m_handler->type(),
+ sp->m_db.str, static_cast<uint>(sp->m_db.length),
+ sp->m_name.str, static_cast<uint>(sp->m_name.length));
if (do_execute_sp(thd, lex->sphead))
goto error;
break;
+ }
case SQLCOM_ALTER_PROCEDURE:
case SQLCOM_ALTER_FUNCTION:
- {
- int sp_result;
- const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
- if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db,
- &lex->spname->m_name, sph, 0))
- goto error;
-
- /*
- Note that if you implement the capability of ALTER FUNCTION to
- alter the body of the function, this command should be made to
- follow the restrictions that log-bin-trust-function-creators=0
- already puts on CREATE FUNCTION.
- */
- /* Conditionally writes to binlog */
- sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics);
- switch (sp_result)
- {
- case SP_OK:
- my_ok(thd);
- break;
- case SP_KEY_NOT_FOUND:
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- sph->type_str(), ErrConvDQName(lex->spname).ptr());
- goto error;
- default:
- my_error(ER_SP_CANT_ALTER, MYF(0),
- sph->type_str(), ErrConvDQName(lex->spname).ptr());
- goto error;
- }
- break;
- }
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+ if (alter_routine(thd, lex))
+ goto error;
+ break;
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
case SQLCOM_DROP_PACKAGE:
case SQLCOM_DROP_PACKAGE_BODY:
- {
-#ifdef HAVE_DLOPEN
- if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
- ! lex->spname->m_explicit_name)
- {
- /* DROP FUNCTION <non qualified name> */
- enum drop_udf_result rc= mysql_drop_function(thd,
- &lex->spname->m_name);
- if (rc == UDF_DEL_RESULT_DELETED)
- {
- my_ok(thd);
- break;
- }
-
- if (rc == UDF_DEL_RESULT_ERROR)
- goto error;
-
- DBUG_ASSERT(rc == UDF_DEL_RESULT_ABSENT);
-
- // If there was no current database, so it can not be SP
- if (lex->spname->m_db.str == NULL)
- {
- if (lex->if_exists())
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
- "FUNCTION (UDF)", lex->spname->m_name.str);
- res= FALSE;
- my_ok(thd);
- break;
- }
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- "FUNCTION (UDF)", lex->spname->m_name.str);
- goto error;
- }
- /* Fall thought to test for a stored function */
- }
-#endif
-
- int sp_result;
- const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
-
- if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db, &lex->spname->m_name,
- Sp_handler::handler(lex->sql_command), 0))
- goto error;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
-
- /* Conditionally writes to binlog */
- sp_result= sph->sp_drop_routine(thd, lex->spname);
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /*
- We're going to issue an implicit REVOKE statement so we close all
- open tables. We have to keep metadata locks as this ensures that
- this statement is atomic against concurent FLUSH TABLES WITH READ
- LOCK. Deadlocks which can arise due to fact that this implicit
- statement takes metadata locks should be detected by a deadlock
- detector in MDL subsystem and reported as errors.
-
- No need to commit/rollback statement transaction, it's not started.
-
- TODO: Long-term we should either ensure that implicit REVOKE statement
- is written into binary log as a separate statement or make both
- dropping of routine and implicit REVOKE parts of one fully atomic
- statement.
- */
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
- close_thread_tables(thd);
-
- if (sp_result != SP_KEY_NOT_FOUND &&
- sp_automatic_privileges && !opt_noacl &&
- sp_revoke_privileges(thd, lex->spname->m_db.str, lex->spname->m_name.str,
- Sp_handler::handler(lex->sql_command)))
- {
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_PROC_AUTO_REVOKE_FAIL,
- ER_THD(thd, ER_PROC_AUTO_REVOKE_FAIL));
- /* If this happens, an error should have been reported. */
- goto error;
- }
-#endif
-
- res= sp_result;
- switch (sp_result) {
- case SP_OK:
- my_ok(thd);
- break;
- case SP_KEY_NOT_FOUND:
- if (lex->if_exists())
- {
- res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST),
- sph->type_str(),
- ErrConvDQName(lex->spname).ptr());
- if (!res)
- my_ok(thd);
- break;
- }
- my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
- sph->type_str(), ErrConvDQName(lex->spname).ptr());
- goto error;
- default:
- my_error(ER_SP_DROP_FAILED, MYF(0),
- sph->type_str(), ErrConvDQName(lex->spname).ptr());
- goto error;
- }
- break;
- }
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+ if (drop_routine(thd, lex))
+ goto error;
+ break;
case SQLCOM_SHOW_CREATE_PROC:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_PACKAGE:
@@ -5937,8 +5789,13 @@ mysql_execute_command(THD *thd)
{
if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error;
- /* Conditionally writes to binlog. */
+
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+
+ /* Conditionally writes to binlog. */
res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
break;
}
@@ -5951,6 +5808,9 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_DROP_TRIGGER:
{
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
+
/* Conditionally writes to binlog. */
res= mysql_create_or_drop_trigger(thd, all_tables, 0);
break;
@@ -6038,7 +5898,7 @@ mysql_execute_command(THD *thd)
{
DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_CREATE_SERVER))
break;
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
@@ -6051,7 +5911,7 @@ mysql_execute_command(THD *thd)
int error;
DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_ALTER_SERVER))
break;
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
@@ -6071,7 +5931,7 @@ mysql_execute_command(THD *thd)
int err_code;
DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
- if (check_global_access(thd, SUPER_ACL))
+ if (check_global_access(thd, PRIV_STMT_DROP_SERVER))
break;
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
@@ -6104,12 +5964,19 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(first_table == all_tables && first_table != 0);
/* fall through */
case SQLCOM_ALTER_SEQUENCE:
+ case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL:
case SQLCOM_GET_DIAGNOSTICS:
case SQLCOM_CALL:
+ case SQLCOM_REVOKE:
+ case SQLCOM_GRANT:
+ if (thd->variables.option_bits & OPTION_IF_EXISTS)
+ lex->create_info.set(DDL_options_st::OPT_IF_EXISTS);
DBUG_ASSERT(lex->m_sql_cmd != NULL);
res= lex->m_sql_cmd->execute(thd);
+ DBUG_PRINT("result", ("res: %d killed: %d is_error(): %d",
+ res, thd->killed, thd->is_error()));
break;
default:
@@ -6170,7 +6037,6 @@ finish:
trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
}
- ha_maria_implicit_commit(thd, FALSE);
}
/* Free tables. Set stage 'closing tables' */
@@ -6238,7 +6104,7 @@ finish:
#ifdef WITH_WSREP
thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
-
+
WSREP_TO_ISOLATION_END;
/*
Force release of transactional locks if not in active MST and wsrep is on.
@@ -6313,8 +6179,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
/*
Do like the original select_describe did: remove OFFSET from the
top-level LIMIT
- */
- result->reset_offset_limit();
+ */
+ result->remove_offset_limit();
if (lex->explain_json)
{
lex->explain->print_explain_json(result, lex->analyze_stmt);
@@ -6392,7 +6258,15 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
}
-static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
+/**
+ SHOW STATUS
+
+ Notes: This is noinline as we don't want to have system_status_var (> 3K)
+ to be on the stack of mysql_execute_command()
+*/
+
+static bool __attribute__ ((noinline))
+execute_show_status(THD *thd, TABLE_LIST *all_tables)
{
bool res;
system_status_var old_status_var= thd->status_var;
@@ -6402,6 +6276,7 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
UINT_MAX, FALSE)))
res= execute_sqlcom_select(thd, all_tables);
+ thd->initial_status_var= NULL;
/* Don't log SHOW STATUS commands to slow query log */
thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
SERVER_QUERY_NO_GOOD_INDEX_USED);
@@ -6477,8 +6352,9 @@ static TABLE *find_temporary_table_for_rename(THD *thd,
}
-static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
- TABLE_LIST *all_tables)
+static bool __attribute__ ((noinline))
+check_rename_table(THD *thd, TABLE_LIST *first_table,
+ TABLE_LIST *all_tables)
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *table;
@@ -6517,6 +6393,223 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
return 0;
}
+/*
+ Generate an incident log event before writing the real event
+ to the binary log. We put this event is before the statement
+ since that makes it simpler to check that the statement was
+ not executed on the slave (since incidents usually stop the
+ slave).
+
+ Observe that any row events that are generated will be generated before.
+
+ This is only for testing purposes and will not be present in a release build.
+*/
+
+#ifndef DBUG_OFF
+static bool __attribute__ ((noinline)) generate_incident_event(THD *thd)
+{
+ if (mysql_bin_log.is_open())
+ {
+
+ Incident incident= INCIDENT_NONE;
+ DBUG_PRINT("debug", ("Just before generate_incident()"));
+ DBUG_EXECUTE_IF("incident_database_resync_on_replace",
+ incident= INCIDENT_LOST_EVENTS;);
+ if (incident)
+ {
+ Incident_log_event ev(thd, incident);
+ (void) mysql_bin_log.write(&ev); /* error is ignored */
+ if (mysql_bin_log.rotate_and_purge(true))
+ return 1;
+ }
+ DBUG_PRINT("debug", ("Just after generate_incident()"));
+ }
+ return 0;
+}
+#else
+static bool generate_incident_event(THD *thd)
+{
+ return 0;
+}
+#endif
+
+
+static int __attribute__ ((noinline))
+show_create_db(THD *thd, LEX *lex)
+{
+ char db_name_buff[NAME_LEN+1];
+ LEX_CSTRING db_name;
+ DBUG_EXECUTE_IF("4x_server_emul",
+ my_error(ER_UNKNOWN_ERROR, MYF(0)); return 1;);
+
+ db_name.str= db_name_buff;
+ db_name.length= lex->name.length;
+ strmov(db_name_buff, lex->name.str);
+
+ if (check_db_name((LEX_STRING*) &db_name))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
+ return 1;
+ }
+ return mysqld_show_create_db(thd, &db_name, &lex->name, lex->create_info);
+}
+
+
+/**
+ Called on SQLCOM_ALTER_PROCEDURE and SQLCOM_ALTER_FUNCTION
+*/
+
+static bool __attribute__ ((noinline))
+alter_routine(THD *thd, LEX *lex)
+{
+ int sp_result;
+ const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
+ if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db,
+ &lex->spname->m_name, sph, 0))
+ return 1;
+ /*
+ Note that if you implement the capability of ALTER FUNCTION to
+ alter the body of the function, this command should be made to
+ follow the restrictions that log-bin-trust-function-creators=0
+ already puts on CREATE FUNCTION.
+ */
+ /* Conditionally writes to binlog */
+ sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics);
+ switch (sp_result) {
+ case SP_OK:
+ my_ok(thd);
+ return 0;
+ case SP_KEY_NOT_FOUND:
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
+ return 1;
+ default:
+ my_error(ER_SP_CANT_ALTER, MYF(0),
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
+ return 1;
+ }
+ return 0; /* purecov: deadcode */
+}
+
+
+static bool __attribute__ ((noinline))
+drop_routine(THD *thd, LEX *lex)
+{
+ int sp_result;
+#ifdef HAVE_DLOPEN
+ if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
+ ! lex->spname->m_explicit_name)
+ {
+ /* DROP FUNCTION <non qualified name> */
+ enum drop_udf_result rc= mysql_drop_function(thd, &lex->spname->m_name);
+ switch (rc) {
+ case UDF_DEL_RESULT_DELETED:
+ my_ok(thd);
+ return 0;
+ case UDF_DEL_RESULT_ERROR:
+ return 1;
+ case UDF_DEL_RESULT_ABSENT:
+ goto absent;
+ }
+
+ DBUG_ASSERT("wrong return code" == 0);
+absent:
+ // If there was no current database, so it cannot be SP
+ if (!lex->spname->m_db.str)
+ {
+ if (lex->if_exists())
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST,
+ ER_THD(thd, ER_SP_DOES_NOT_EXIST),
+ "FUNCTION (UDF)", lex->spname->m_name.str);
+ my_ok(thd);
+ return 0;
+ }
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ "FUNCTION (UDF)", lex->spname->m_name.str);
+ return 1;
+ }
+ /* Fall trough to test for a stored function */
+ }
+#endif /* HAVE_DLOPEN */
+
+ const Sp_handler *sph= Sp_handler::handler(lex->sql_command);
+
+ if (check_routine_access(thd, ALTER_PROC_ACL, &lex->spname->m_db,
+ &lex->spname->m_name,
+ Sp_handler::handler(lex->sql_command), 0))
+ return 1;
+
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+
+ /* Conditionally writes to binlog */
+ sp_result= sph->sp_drop_routine(thd, lex->spname);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ We're going to issue an implicit REVOKE statement so we close all
+ open tables. We have to keep metadata locks as this ensures that
+ this statement is atomic against concurent FLUSH TABLES WITH READ
+ LOCK. Deadlocks which can arise due to fact that this implicit
+ statement takes metadata locks should be detected by a deadlock
+ detector in MDL subsystem and reported as errors.
+
+ TODO: Long-term we should either ensure that implicit REVOKE statement
+ is written into binary log as a separate statement or make both
+ dropping of routine and implicit REVOKE parts of one fully atomic
+ statement.
+ */
+ if (trans_commit_stmt(thd))
+ sp_result= SP_INTERNAL_ERROR;
+ close_thread_tables(thd);
+
+ if (sp_result != SP_KEY_NOT_FOUND &&
+ sp_automatic_privileges && !opt_noacl &&
+ sp_revoke_privileges(thd, lex->spname->m_db.str, lex->spname->m_name.str,
+ Sp_handler::handler(lex->sql_command)))
+ {
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_REVOKE_FAIL,
+ ER_THD(thd, ER_PROC_AUTO_REVOKE_FAIL));
+ /* If this happens, an error should have been reported. */
+ return 1;
+ }
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+ switch (sp_result) {
+ case SP_OK:
+ my_ok(thd);
+ return 0;
+ case SP_KEY_NOT_FOUND:
+ int res;
+ if (lex->if_exists())
+ {
+ res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST,
+ ER_THD(thd, ER_SP_DOES_NOT_EXIST),
+ sph->type_str(),
+ ErrConvDQName(lex->spname).ptr());
+ if (res)
+ return 1;
+ my_ok(thd);
+ return 0;
+ }
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
+ return 1;
+ default:
+ my_error(ER_SP_DROP_FAILED, MYF(0),
+ sph->type_str(), ErrConvDQName(lex->spname).ptr());
+ return 1;
+ }
+
+#ifdef WITH_WSREP
+wsrep_error_label:
+ return 1;
+#endif
+}
/**
@brief Compare requested privileges with the privileges acquired from the
@@ -6546,7 +6639,8 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
*/
bool
-check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
+check_access(THD *thd, privilege_t want_access,
+ const char *db, privilege_t *save_priv,
GRANT_INTERNAL_INFO *grant_internal_info,
bool dont_check_global_grants, bool no_errors)
{
@@ -6556,7 +6650,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
return false;
#else
Security_context *sctx= thd->security_ctx;
- ulong db_access;
+ privilege_t db_access(NO_ACL);
/*
GRANT command:
@@ -6568,17 +6662,19 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
set db_is_pattern according to 'dont_check_global_grants' value.
*/
bool db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants);
- ulong dummy;
+ privilege_t dummy(NO_ACL);
DBUG_ENTER("check_access");
- DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
- db ? db : "", want_access, sctx->master_access));
+ DBUG_PRINT("enter",("db: %s want_access: %llx master_access: %llx",
+ db ? db : "",
+ (longlong) want_access,
+ (longlong) sctx->master_access));
if (save_priv)
- *save_priv=0;
+ *save_priv= NO_ACL;
else
{
save_priv= &dummy;
- dummy= 0;
+ dummy= NO_ACL;
}
/* check access may be called twice in a row. Don't change to same stage */
@@ -6696,8 +6792,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
else
db_access= sctx->db_access;
- DBUG_PRINT("info",("db_access: %lu want_access: %lu",
- db_access, want_access));
+ DBUG_PRINT("info",("db_access: %llx want_access: %llx",
+ (longlong) db_access, (longlong) want_access));
/*
Save the union of User-table and the intersection between Db-table and
@@ -6765,7 +6861,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
1 access denied, error is sent to client
*/
-bool check_single_table_access(THD *thd, ulong privilege,
+bool check_single_table_access(THD *thd, privilege_t privilege,
TABLE_LIST *all_tables, bool no_errors)
{
Switch_to_definer_security_ctx backup_sctx(thd, all_tables);
@@ -6806,7 +6902,8 @@ bool check_single_table_access(THD *thd, ulong privilege,
1 access denied, error is sent to client
*/
-bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
+bool check_one_table_access(THD *thd, privilege_t privilege,
+ TABLE_LIST *all_tables)
{
if (check_single_table_access (thd,privilege,all_tables, FALSE))
return 1;
@@ -6958,7 +7055,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
*/
bool
-check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+check_table_access(THD *thd, privilege_t requirements, TABLE_LIST *tables,
bool any_combination_of_privileges_will_do,
uint number, bool no_errors)
{
@@ -6977,7 +7074,7 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
tables->correspondent_table : tables;
Switch_to_definer_security_ctx backup_ctx(thd, table_ref);
- ulong want_access= requirements;
+ privilege_t want_access(requirements);
/*
Register access for view underlying table.
@@ -7020,7 +7117,7 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
bool
-check_routine_access(THD *thd, ulong want_access, const LEX_CSTRING *db,
+check_routine_access(THD *thd, privilege_t want_access, const LEX_CSTRING *db,
const LEX_CSTRING *name,
const Sp_handler *sph, bool no_errors)
{
@@ -7042,7 +7139,7 @@ check_routine_access(THD *thd, ulong want_access, const LEX_CSTRING *db,
as long as this code path is not abused to create routines.
The assert enforce that.
*/
- DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0);
+ DBUG_ASSERT((want_access & CREATE_PROC_ACL) == NO_ACL);
if ((thd->security_ctx->master_access & want_access) == want_access)
tables->grant.privilege= want_access;
else if (check_access(thd, want_access, db->str,
@@ -7071,7 +7168,7 @@ check_routine_access(THD *thd, ulong want_access, const LEX_CSTRING *db,
bool check_some_routine_access(THD *thd, const char *db, const char *name,
const Sp_handler *sph)
{
- ulong save_priv;
+ privilege_t save_priv(NO_ACL);
/*
The following test is just a shortcut for check_access() (to avoid
calculating db_access)
@@ -7102,16 +7199,15 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name,
1 error
*/
-bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
+bool check_some_access(THD *thd, privilege_t want_access, TABLE_LIST *table)
{
- ulong access;
DBUG_ENTER("check_some_access");
- /* This loop will work as long as we have less than 32 privileges */
- for (access= 1; access < want_access ; access<<= 1)
+ for (ulonglong bit= 1; bit < (ulonglong) want_access ; bit<<= 1)
{
- if (access & want_access)
+ if (bit & want_access)
{
+ privilege_t access= ALL_KNOWN_ACL & bit;
if (!check_access(thd, access, table->db.str,
&table->grant.privilege,
&table->grant.m_internal,
@@ -7134,10 +7230,8 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
@param want_access Use should have any of these global rights
@warning
- One gets access right if one has ANY of the rights in want_access.
- This is useful as one in most cases only need one global right,
- but in some case we want to check if the user has SUPER or
- REPL_CLIENT_ACL rights.
+ Starting from 10.5.2 only one bit is allowed in want_access.
+ Access denied error is returned if want_access has multiple bits set.
@retval
0 ok
@@ -7145,11 +7239,11 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
1 Access denied. In this case an error is sent to the client
*/
-bool check_global_access(THD *thd, ulong want_access, bool no_errors)
+bool check_global_access(THD *thd, privilege_t want_access, bool no_errors)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
char command[128];
- if ((thd->security_ctx->master_access & want_access))
+ if (thd->security_ctx->master_access & want_access)
return 0;
if (unlikely(!no_errors))
{
@@ -7197,8 +7291,7 @@ bool check_fk_parent_table_access(THD *thd,
LEX_CSTRING db_name;
LEX_CSTRING 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);
+ const privilege_t privileges(COL_DML_ACLS | REFERENCES_ACL);
// Check if tablename is valid or not.
DBUG_ASSERT(table_name.str != NULL);
@@ -7312,8 +7405,17 @@ long max_stack_used;
corresponding exec. (Thus we only have to check in fix_fields.)
- Passing to check_stack_overrun() prevents the compiler from removing it.
*/
-bool check_stack_overrun(THD *thd, long margin,
- uchar *buf __attribute__((unused)))
+
+bool
+#if defined __GNUC__ && !defined __clang__
+/*
+ Do not optimize the function in order to preserve a stack variable creation.
+ Otherwise, the variable pointed as "buf" can be removed due to a missing
+ usage.
+ */
+__attribute__((optimize("-O0")))
+#endif
+check_stack_overrun(THD *thd, long margin, uchar *buf __attribute__((unused)))
{
long stack_used;
DBUG_ASSERT(thd == current_thd);
@@ -7355,11 +7457,11 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, size_t *yystacksize)
old_info= *yystacksize;
*yystacksize= set_zone((int)(*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
if (!(state->yacc_yyvs= (uchar*)
- my_realloc(state->yacc_yyvs,
+ my_realloc(key_memory_bison_stack, state->yacc_yyvs,
*yystacksize*sizeof(**yyvs),
MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
!(state->yacc_yyss= (uchar*)
- my_realloc(state->yacc_yyss,
+ my_realloc(key_memory_bison_stack, state->yacc_yyss,
*yystacksize*sizeof(**yyss),
MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
return 1;
@@ -7397,6 +7499,12 @@ void THD::reset_for_next_command(bool do_clear_error)
DBUG_ENTER("THD::reset_for_next_command");
DBUG_ASSERT(!spcont); /* not for substatements of routines */
DBUG_ASSERT(!in_sub_stmt);
+ /*
+ Table maps should have been reset after previous statement except in the
+ case where we have locked tables
+ */
+ DBUG_ASSERT(binlog_table_maps == 0 ||
+ locked_tables_mode == LTM_LOCK_TABLES);
if (likely(do_clear_error))
{
@@ -7456,7 +7564,7 @@ void THD::reset_for_next_command(bool do_clear_error)
if (!in_multi_stmt_transaction_mode())
{
variables.option_bits&= ~OPTION_KEEP_LOG;
- transaction.all.reset();
+ transaction->all.reset();
}
DBUG_ASSERT(security_ctx== &main_security_ctx);
thread_specific_used= FALSE;
@@ -7496,10 +7604,7 @@ void THD::reset_for_next_command(bool do_clear_error)
void
mysql_init_select(LEX *lex)
{
- SELECT_LEX *select_lex= lex->current_select;
- select_lex->init_select();
- lex->wild= 0;
- lex->exchange= 0;
+ lex->init_select();
}
@@ -7640,7 +7745,7 @@ void mysql_init_multi_delete(LEX *lex)
lex->sql_command= SQLCOM_DELETE_MULTI;
mysql_init_select(lex);
lex->first_select_lex()->select_limit= 0;
- lex->unit.select_limit_cnt= HA_POS_ERROR;
+ lex->unit.lim.set_unlimited();
lex->first_select_lex()->table_list.
save_and_clear(&lex->auxiliary_table_list);
lex->query_tables= 0;
@@ -7828,7 +7933,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
bool is_next_command)
{
DBUG_ENTER("mysql_parse");
- DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
+ DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on_MYSQLparse(););
+ DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on_ORAparse(););
/*
Warning.
@@ -8212,9 +8318,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
// Pure table aliases do not need to be locked:
if (ptr->db.str && !(table_options & TL_OPTION_ALIAS))
{
- ptr->mdl_request.init(MDL_key::TABLE, ptr->db.str, ptr->table_name.str,
- mdl_type,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&ptr->mdl_request, MDL_key::TABLE, ptr->db.str,
+ ptr->table_name.str, mdl_type, MDL_TRANSACTION);
}
DBUG_RETURN(ptr);
}
@@ -9115,11 +9220,11 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
*/
#ifdef WITH_WSREP
- if (((thd->security_ctx->master_access & SUPER_ACL) ||
+ if (((thd->security_ctx->master_access & PRIV_KILL_OTHER_USER_PROCESS) ||
thd->security_ctx->user_matches(tmp->security_ctx)) &&
!wsrep_thd_is_BF(tmp, false) && !tmp->wsrep_applier)
#else
- if ((thd->security_ctx->master_access & SUPER_ACL) ||
+ if ((thd->security_ctx->master_access & PRIV_KILL_OTHER_USER_PROCESS) ||
thd->security_ctx->user_matches(tmp->security_ctx))
#endif /* WITH_WSREP */
{
@@ -9190,7 +9295,8 @@ static my_bool kill_threads_callback(THD *thd, kill_threads_callback_arg *arg)
!strcmp(thd->security_ctx->host_or_ip, arg->user->host.str)) &&
!strcmp(thd->security_ctx->user, arg->user->user.str))
{
- if (!(arg->thd->security_ctx->master_access & SUPER_ACL) &&
+ if (!(arg->thd->security_ctx->master_access &
+ PRIV_KILL_OTHER_USER_PROCESS) &&
!arg->thd->security_ctx->user_matches(thd->security_ctx))
return 1;
if (!arg->threads_to_kill.push_back(thd, arg->thd->mem_root))
@@ -9272,8 +9378,8 @@ 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 void __attribute__ ((noinline))
+sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
{
uint error;
ha_rows rows;
@@ -9434,7 +9540,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->grant.orig_want_privilege= NO_ACL;
table->table_in_first_from_clause= 1;
}
/*
@@ -9695,9 +9801,9 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables)
Check that we have modify privileges for the first table and
select privileges for the rest
*/
- ulong privilege= (INSERT_ACL |
- (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
- (lex->value_list.elements ? UPDATE_ACL : 0));
+ privilege_t privilege= (INSERT_ACL |
+ (lex->duplicates == DUP_REPLACE ? DELETE_ACL : NO_ACL) |
+ (lex->value_list.elements ? UPDATE_ACL : NO_ACL));
if (check_one_table_access(thd, privilege, tables))
DBUG_RETURN(TRUE);
@@ -9757,7 +9863,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
{
LEX *lex= thd->lex;
SELECT_LEX *select_lex= lex->first_select_lex();
- ulong want_priv;
+ privilege_t want_priv(NO_ACL);
bool error= TRUE; // Error message is given
DBUG_ENTER("create_table_precheck");
@@ -9766,8 +9872,8 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
CREATE TABLE ... SELECT, also require INSERT.
*/
- want_priv= lex->tmp_table() ? CREATE_TMP_ACL :
- (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0));
+ want_priv= lex->tmp_table() ? CREATE_TMP_ACL :
+ (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : NO_ACL));
/* CREATE OR REPLACE on not temporary tables require DROP_ACL */
if (lex->create_info.or_replace() && !lex->tmp_table())
@@ -10106,10 +10212,10 @@ int path_starts_from_data_home_dir(const char *path)
if (lower_case_file_system)
{
- if (!my_strnncoll(default_charset_info, (const uchar*) path,
- mysql_unpacked_real_data_home_len,
- (const uchar*) mysql_unpacked_real_data_home,
- mysql_unpacked_real_data_home_len))
+ if (!default_charset_info->strnncoll(path,
+ mysql_unpacked_real_data_home_len,
+ mysql_unpacked_real_data_home,
+ mysql_unpacked_real_data_home_len))
{
DBUG_PRINT("error", ("Path is part of mysql_real_data_home"));
DBUG_RETURN(1);
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 1d25b898ca4..0e5cc7f4bad 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -41,7 +41,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
int mysql_multi_update_prepare(THD *thd);
int mysql_multi_delete_prepare(THD *thd);
-bool mysql_insert_select_prepare(THD *thd);
+int mysql_insert_select_prepare(THD *thd,select_result *sel_res);
bool update_precheck(THD *thd, TABLE_LIST *tables);
bool delete_precheck(THD *thd, TABLE_LIST *tables);
bool insert_precheck(THD *thd, TABLE_LIST *tables);
@@ -143,32 +143,32 @@ inline bool check_identifier_name(LEX_CSTRING *str)
}
#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,
- TABLE_LIST *tables, bool no_errors);
-bool check_routine_access(THD *thd,ulong want_access,
+bool check_one_table_access(THD *thd, privilege_t privilege, TABLE_LIST *tables);
+bool check_single_table_access(THD *thd, privilege_t privilege,
+ TABLE_LIST *tables, bool no_errors);
+bool check_routine_access(THD *thd, privilege_t want_access,
const LEX_CSTRING *db,
const LEX_CSTRING *name,
const Sp_handler *sph, bool no_errors);
-bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
+bool check_some_access(THD *thd, privilege_t want_access, TABLE_LIST *table);
bool check_some_routine_access(THD *thd, const char *db, const char *name,
const Sp_handler *sph);
-bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+bool check_table_access(THD *thd, privilege_t requirements,TABLE_LIST *tables,
bool any_combination_of_privileges_will_do,
uint number,
bool no_errors);
#else
-inline bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
+inline bool check_one_table_access(THD *thd, privilege_t privilege, TABLE_LIST *tables)
{ return false; }
-inline bool check_single_table_access(THD *thd, ulong privilege,
- TABLE_LIST *tables, bool no_errors)
+inline bool check_single_table_access(THD *thd, privilege_t privilege,
+ TABLE_LIST *tables, bool no_errors)
{ return false; }
-inline bool check_routine_access(THD *thd,ulong want_access,
+inline bool check_routine_access(THD *thd, privilege_t want_access,
const LEX_CSTRING *db,
const LEX_CSTRING *name,
const Sp_handler *sph, bool no_errors)
{ return false; }
-inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
+inline bool check_some_access(THD *thd, privilege_t want_access, TABLE_LIST *table)
{
table->grant.privilege= want_access;
return false;
@@ -178,7 +178,7 @@ inline bool check_some_routine_access(THD *thd, const char *db,
const Sp_handler *sph)
{ return false; }
inline bool
-check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
+check_table_access(THD *thd, privilege_t requirements,TABLE_LIST *tables,
bool any_combination_of_privileges_will_do,
uint number,
bool no_errors)
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index 7ed1eb7aa52..9051484b6b8 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2005, 2017, Oracle and/or its affiliates.
- Copyright (c) 2009, 2018, MariaDB
+ Copyright (c) 2009, 2020, 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
@@ -1864,7 +1864,7 @@ bool field_is_partition_charset(Field *field)
DESCRIPTION
We will check in this routine that the fields of the partition functions
do not contain unallowed parts. It can also be used to check if there
- are fields that require special care by calling my_strnxfrm before
+ are fields that require special care by calling strnxfrm before
calling the functions to calculate partition id.
*/
@@ -2013,7 +2013,7 @@ bool fix_partition_func(THD *thd, TABLE *table, bool is_create_table_ind)
else
{
if (part_info->part_type == VERSIONING_PARTITION &&
- part_info->vers_setup_expression(thd))
+ part_info->vers_fix_field_list(thd))
goto end;
if (unlikely(fix_fields_part_func(thd, part_info->part_expr,
table, FALSE, is_create_table_ind)))
@@ -2241,80 +2241,6 @@ static int add_partition_options(String *str, partition_element *p_elem)
/*
- Check partition fields for result type and if they need
- to check the character set.
-
- SYNOPSIS
- check_part_field()
- sql_type Type provided by user
- field_name Name of field, used for error handling
- result_type Out value: Result type of field
- need_cs_check Out value: Do we need character set check
-
- RETURN VALUES
- TRUE Error
- FALSE Ok
-*/
-
-static int check_part_field(enum_field_types sql_type,
- const char *field_name,
- Item_result *result_type,
- bool *need_cs_check)
-{
- if (sql_type >= MYSQL_TYPE_TINY_BLOB &&
- sql_type <= MYSQL_TYPE_BLOB)
- {
- my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
- return TRUE;
- }
- switch (sql_type)
- {
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_INT24:
- *result_type= INT_RESULT;
- *need_cs_check= FALSE;
- return FALSE;
- case MYSQL_TYPE_NEWDATE:
- 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;
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VAR_STRING:
- *result_type= STRING_RESULT;
- *need_cs_check= TRUE;
- return FALSE;
- 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:
- case MYSQL_TYPE_BIT:
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- case MYSQL_TYPE_GEOMETRY:
- goto error;
- default:
- goto error;
- }
-error:
- my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0),
- field_name);
- return TRUE;
-}
-
-
-/*
Find the given field's Create_field object using name of field
SYNOPSIS
@@ -2377,8 +2303,7 @@ static int add_column_list_values(String *str, partition_info *part_info,
else
{
CHARSET_INFO *field_cs;
- bool need_cs_check= FALSE;
- Item_result result_type= STRING_RESULT;
+ const Type_handler *th= NULL;
/*
This function is called at a very early stage, even before
@@ -2396,57 +2321,24 @@ static int add_column_list_values(String *str, partition_info *part_info,
my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
return 1;
}
- if (check_part_field(sql_field->real_field_type(),
- sql_field->field_name.str,
- &result_type,
- &need_cs_check))
+ th= sql_field->type_handler();
+ if (th->partition_field_check(sql_field->field_name, item_expr))
return 1;
- if (need_cs_check)
- field_cs= get_sql_field_charset(sql_field, create_info);
- else
- field_cs= NULL;
+ field_cs= get_sql_field_charset(sql_field, create_info);
}
else
{
Field *field= part_info->part_field_array[i];
- result_type= field->result_type();
- if (check_part_field(field->real_type(),
- field->field_name.str,
- &result_type,
- &need_cs_check))
+ th= field->type_handler();
+ if (th->partition_field_check(field->field_name, item_expr))
return 1;
- DBUG_ASSERT(result_type == field->result_type());
- if (need_cs_check)
- field_cs= field->charset();
- else
- field_cs= NULL;
+ field_cs= field->charset();
}
- if (result_type != item_expr->result_type())
- {
- my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
+ if (th->partition_field_append_value(str, item_expr, field_cs,
+ alter_info == NULL ?
+ PARTITION_VALUE_PRINT_MODE_SHOW:
+ PARTITION_VALUE_PRINT_MODE_FRM))
return 1;
- }
- if (field_cs && field_cs != item_expr->collation.collation)
- {
- if (!(item_expr= convert_charset_partition_constant(item_expr,
- field_cs)))
- {
- my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
- return 1;
- }
- }
- {
- StringBuffer<MAX_KEY_LENGTH> buf;
- String val_conv, *res;
- val_conv.set_charset(system_charset_info);
- res= item_expr->val_str(&buf);
- if (get_cs_converted_part_value_from_string(current_thd,
- item_expr, res,
- &val_conv, field_cs,
- (bool)(alter_info != NULL)))
- return 1;
- err+= str->append(val_conv);
- }
}
}
if (i != (num_elements - 1))
@@ -2587,11 +2479,13 @@ char *generate_partition_syntax_for_frm(THD *thd, partition_info *part_info,
HA_CREATE_INFO *create_info,
Alter_info *alter_info)
{
- sql_mode_t old_mode= thd->variables.sql_mode;
- thd->variables.sql_mode &= ~MODE_ANSI_QUOTES;
+ Sql_mode_instant_remove sms(thd, MODE_ANSI_QUOTES);
char *res= generate_partition_syntax(thd, part_info, buf_length,
true, create_info, alter_info);
- thd->variables.sql_mode= old_mode;
+ DBUG_EXECUTE_IF("generate_partition_syntax_for_frm",
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES,
+ ErrConvString(res, (uint32) *buf_length,
+ system_charset_info).ptr()););
return res;
}
@@ -2675,11 +2569,21 @@ char *generate_partition_syntax(THD *thd, partition_info *part_info,
err+= str.append(STRING_WITH_LEN("INTERVAL "));
err+= append_interval(&str, vers_info->interval.type,
vers_info->interval.step);
+ err+= str.append(STRING_WITH_LEN(" STARTS "));
if (create_info) // not SHOW CREATE
{
- err+= str.append(STRING_WITH_LEN(" STARTS "));
err+= str.append_ulonglong(vers_info->interval.start);
}
+ else
+ {
+ MYSQL_TIME ltime;
+ char ctime[MAX_DATETIME_WIDTH + 1];
+ thd->variables.time_zone->gmt_sec_to_TIME(&ltime, vers_info->interval.start);
+ uint ctime_len= my_datetime_to_str(&ltime, ctime, 0);
+ err+= str.append(STRING_WITH_LEN("TIMESTAMP'"));
+ err+= str.append(ctime, ctime_len);
+ err+= str.append('\'');
+ }
}
if (vers_info->limit)
{
@@ -3067,8 +2971,8 @@ static void copy_to_part_field_buffers(Field **ptr,
if (field->type() == MYSQL_TYPE_VARCHAR)
{
uint len_bytes= ((Field_varstring*)field)->length_bytes;
- my_strnxfrm(cs, field_buf + len_bytes, max_len,
- field->ptr + len_bytes, data_len);
+ cs->strnxfrm(field_buf + len_bytes, max_len,
+ field->ptr + len_bytes, data_len);
if (len_bytes == 1)
*field_buf= (uchar) data_len;
else
@@ -3076,8 +2980,8 @@ static void copy_to_part_field_buffers(Field **ptr,
}
else
{
- my_strnxfrm(cs, field_buf, max_len,
- field->ptr, max_len);
+ cs->strnxfrm(field_buf, max_len,
+ field->ptr, max_len);
}
field->ptr= field_buf;
}
@@ -4901,7 +4805,6 @@ static void check_datadir_altered_for_innodb(THD *thd,
uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
HA_CREATE_INFO *create_info,
- Alter_table_ctx *alter_ctx,
bool *partition_changed,
bool *fast_alter_table)
{
@@ -4911,7 +4814,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
if (table->part_info && (alter_info->flags & (ALTER_ADD_FOREIGN_KEY |
ALTER_DROP_FOREIGN_KEY)))
{
- my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
+ my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0), "FOREIGN KEY");
DBUG_RETURN(TRUE);
}
/* Remove partitioning on a not partitioned table is not possible */
@@ -4996,8 +4899,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
object to allow fast_alter_partition_table to perform the changes.
*/
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
- alter_ctx->db.str,
- alter_ctx->table_name.str,
+ table->s->db.str,
+ table->s->table_name.str,
MDL_INTENTION_EXCLUSIVE));
tab_part_info= table->part_info;
@@ -5233,7 +5136,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(thd, table->file, 0,
- tab_part_info->num_parts))
+ tab_part_info->next_part_no(num_new_partitions)))
{
goto err;
}
@@ -5477,6 +5380,7 @@ that are reorganised.
my_error(ER_VERS_WRONG_PARTS, MYF(0), table->s->table_name.str);
goto err;
}
+ tab_part_info->use_default_partitions= false;
}
else
{
@@ -6908,20 +6812,21 @@ static bool alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
@param lpt Struct carrying parameters
- @return Always 0.
+ @return error code if external_unlock fails
*/
static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt)
{
+ int error= 0;
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();
+ error= mysql_lock_remove(lpt->thd, lpt->thd->lock, lpt->table);
+ error= lpt->table->file->ha_close();
lpt->table->db_stat= 0; // Mark file closed
}
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
@@ -6935,44 +6840,33 @@ static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt)
@param close_table Table is still open, close it before reverting
*/
-void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
- bool action_completed,
- bool drop_partition,
- bool frm_install,
- bool close_table)
+static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
+ bool action_completed,
+ bool drop_partition,
+ bool frm_install)
{
- partition_info *part_info= lpt->part_info;
THD *thd= lpt->thd;
+ partition_info *part_info= lpt->part_info->get_clone(thd);
TABLE *table= lpt->table;
DBUG_ENTER("handle_alter_part_error");
DBUG_ASSERT(table->needs_reopen());
- if (close_table)
- {
- /*
- 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.str,
- lpt->table_name.str,
- 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. */
- if (part_info)
- part_info= part_info->get_clone(thd);
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
- }
- else
+ /*
+ All instances of this table needs to be closed.
+ Better to do that here, than leave the cleaning up to others.
+ Acquire EXCLUSIVE mdl lock if not already acquired.
+ */
+ if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db.str,
+ lpt->table_name.str,
+ MDL_EXCLUSIVE) &&
+ wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
{
-err_exclusive_lock:
/*
+ Did not succeed in getting exclusive access to the table.
+
+ Since we have altered a cached table object (and its part_info) we need
+ at least to remove this instance so it will not be reused.
+
Temporarily remove it from the locked table list, so that it will get
reopened.
*/
@@ -6984,11 +6878,14 @@ err_exclusive_lock:
the table cache.
*/
mysql_lock_remove(thd, thd->lock, table);
- if (part_info)
- part_info= part_info->get_clone(thd);
close_thread_table(thd, &thd->open_tables);
lpt->table_list->table= NULL;
}
+ else
+ {
+ /* Ensure the share is destroyed and reopened. */
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
+ }
if (part_info->first_log_entry &&
execute_ddl_log_entry(thd, part_info->first_log_entry->entry_pos))
@@ -7004,19 +6901,20 @@ err_exclusive_lock:
if (drop_partition)
{
/* Table is still ok, but we left a shadow frm file behind. */
- 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");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1,
+ "Operation was unsuccessful, table is still "
+ "intact, but it is possible that a shadow frm "
+ "file was left behind");
}
else
{
- 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.",
- "It is also possible that temporary partitions are left behind,",
- "these could be empty or more or less filled with records");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1,
+ "Operation was unsuccessful, table is still "
+ "intact, but it is possible that a shadow frm "
+ "file was left behind. "
+ "It is also possible that temporary partitions "
+ "are left behind, these could be empty or more "
+ "or less filled with records");
}
}
else
@@ -7024,14 +6922,14 @@ err_exclusive_lock:
if (frm_install)
{
/*
- Failed during install of shadow frm file, table isn't intact
- and dropped partitions are still there
+ Failed during install of shadow frm file, table isn't intact
+ and dropped partitions are still there
*/
- 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",
- "is required.");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1,
+ "Failed during alter of partitions, table is no "
+ "longer intact. "
+ "The frm file is in an unknown state, and a "
+ "backup is required.");
}
else if (drop_partition)
{
@@ -7041,10 +6939,10 @@ err_exclusive_lock:
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(thd, Sql_condition::WARN_LEVEL_WARN, 1,
- "%s %s",
- "Failed during drop of partitions, table is intact.",
- "Manual drop of remaining partitions is required");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1,
+ "Failed during drop of partitions, table is "
+ "intact. "
+ "Manual drop of remaining partitions is required");
}
else
{
@@ -7053,11 +6951,11 @@ err_exclusive_lock:
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(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",
- "Table is disabled by writing ancient frm file version into it");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1,
+ "Failed during renaming of partitions. We are now "
+ "in a position where table is not reusable "
+ "Table is disabled by writing ancient frm file "
+ "version into it");
}
}
}
@@ -7082,9 +6980,9 @@ err_exclusive_lock:
even though we reported an error the operation was successfully
completed.
*/
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,"%s %s",
- "Operation was successfully completed by failure handling,",
- "after failure of normal operation");
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1,
+ "Operation was successfully completed by failure "
+ "handling, after failure of normal operation");
}
}
@@ -7162,9 +7060,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ALTER_PARTITION_PARAM_TYPE lpt_obj;
ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj;
bool action_completed= FALSE;
- bool close_table_on_failure= FALSE;
bool frm_install= FALSE;
MDL_ticket *mdl_ticket= table->mdl_ticket;
+ /* option_bits is used to mark if we should log the query with IF EXISTS */
+ ulonglong save_option_bits= thd->variables.option_bits;
DBUG_ENTER("fast_alter_partition_table");
DBUG_ASSERT(table->needs_reopen());
@@ -7185,6 +7084,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
lpt->pack_frm_data= NULL;
lpt->pack_frm_len= 0;
+ /* Add IF EXISTS to binlog if shared table */
+ if (table->file->partition_ht()->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
+ thd->variables.option_bits|= OPTION_IF_EXISTS;
+
if (table->file->alter_table_flags(alter_info->flags) &
HA_PARTITION_ONE_PHASE)
{
@@ -7301,13 +7204,11 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_drop_partition_3") ||
ERROR_INJECT_ERROR("fail_drop_partition_3") ||
- (close_table_on_failure= TRUE, FALSE) ||
write_log_drop_partition(lpt) ||
(action_completed= TRUE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
ERROR_INJECT_ERROR("fail_drop_partition_4") ||
alter_close_table(lpt) ||
- (close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
ERROR_INJECT_ERROR("fail_drop_partition_5") ||
((!thd->lex->no_write_to_binlog) &&
@@ -7327,8 +7228,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
ERROR_INJECT_ERROR("fail_drop_partition_9"))
{
- handle_alter_part_error(lpt, action_completed, TRUE, frm_install,
- close_table_on_failure);
+ handle_alter_part_error(lpt, action_completed, TRUE, frm_install);
goto err;
}
if (alter_partition_lock_handling(lpt))
@@ -7376,14 +7276,12 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
ERROR_INJECT_ERROR("fail_add_partition_3") ||
- (close_table_on_failure= TRUE, FALSE) ||
write_log_add_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
ERROR_INJECT_ERROR("fail_add_partition_4") ||
mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
ERROR_INJECT_ERROR("fail_add_partition_5") ||
- (close_table_on_failure= FALSE, FALSE) ||
alter_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_6") ||
ERROR_INJECT_ERROR("fail_add_partition_6") ||
@@ -7405,8 +7303,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_10") ||
ERROR_INJECT_ERROR("fail_add_partition_10"))
{
- handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
- close_table_on_failure);
+ handle_alter_part_error(lpt, action_completed, FALSE, frm_install);
goto err;
}
if (alter_partition_lock_handling(lpt))
@@ -7477,14 +7374,12 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_change_partition_3") ||
ERROR_INJECT_ERROR("fail_change_partition_3") ||
mysql_change_partitions(lpt) ||
- (close_table_on_failure= TRUE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_4") ||
ERROR_INJECT_ERROR("fail_change_partition_4") ||
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") ||
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) ||
@@ -7511,13 +7406,13 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_change_partition_12") ||
ERROR_INJECT_ERROR("fail_change_partition_12"))
{
- handle_alter_part_error(lpt, action_completed, FALSE, frm_install,
- close_table_on_failure);
+ handle_alter_part_error(lpt, action_completed, FALSE, frm_install);
goto err;
}
if (alter_partition_lock_handling(lpt))
goto err;
}
+ thd->variables.option_bits= save_option_bits;
downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE);
/*
A final step is to write the query to the binlog and send ok to the
@@ -7525,6 +7420,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
*/
DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, table_list));
err:
+ thd->variables.option_bits= save_option_bits;
downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE);
DBUG_RETURN(TRUE);
}
@@ -7621,7 +7517,7 @@ void append_row_to_str(String &str, const uchar *row, TABLE *table)
rec= row;
/* Create a new array of all read fields. */
- fields= (Field**) my_malloc(sizeof(void*) * (num_fields + 1),
+ fields= (Field**) my_malloc(PSI_INSTRUMENT_ME, sizeof(void*) * (num_fields + 1),
MYF(0));
if (!fields)
return;
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index ea197a6fc2c..58ba82dcd9f 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -98,12 +98,6 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
const key_range *key_spec,
part_id_range *part_spec);
uint get_partition_field_store_length(Field *field);
-int get_cs_converted_part_value_from_string(THD *thd,
- Item *item,
- String *input_str,
- String *output_str,
- CHARSET_INFO *cs,
- bool use_hex);
void get_full_part_id_from_key(const TABLE *table, uchar *buf,
KEY *key_info,
const key_range *key_spec,
@@ -268,7 +262,6 @@ 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,
- Alter_table_ctx *alter_ctx,
bool *partition_changed,
bool *fast_alter_table);
char *generate_partition_syntax(THD *thd, partition_info *part_info,
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index 54484846609..55abee72a52 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -46,6 +46,16 @@ bool Sql_cmd_partition_unsupported::execute(THD *)
#else
+static bool return_with_logging(THD *thd)
+{
+ if (thd->slave_thread &&
+ write_bin_log_with_if_exists(thd, true, false, true))
+ return(true);
+ my_ok(thd);
+ return(false);
+}
+
+
bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
{
/* Moved from mysql_execute_command */
@@ -63,7 +73,7 @@ bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
*/
IF_DBUG(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;
+ privilege_t priv_needed(ALTER_ACL | DROP_ACL | INSERT_ACL | CREATE_ACL);
DBUG_ENTER("Sql_cmd_alter_table_exchange_partition::execute");
@@ -501,7 +511,8 @@ bool Sql_cmd_alter_table_exchange_partition::
MDL_ticket *swap_table_mdl_ticket= NULL;
MDL_ticket *part_table_mdl_ticket= NULL;
uint table_counter;
- bool error= TRUE;
+ bool error= TRUE, force_if_exists= 0;
+ ulonglong save_option_bits= thd->variables.option_bits;
DBUG_ENTER("mysql_exchange_partition");
DBUG_ASSERT(alter_info->partition_flags & ALTER_PARTITION_EXCHANGE);
@@ -529,7 +540,25 @@ bool Sql_cmd_alter_table_exchange_partition::
table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
if (unlikely(open_tables(thd, &table_list, &table_counter, 0,
&alter_prelocking_strategy)))
+ {
+ if (thd->lex->if_exists() &&
+ thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
+ {
+ /*
+ ALTER TABLE IF EXISTS was used on not existing table
+ We have to log the query on a slave as the table may be a shared one
+ from the master and we need to ensure that the next slave can see
+ the statement as this slave may not have the table shared
+ */
+ thd->clear_error();
+ if (thd->slave_thread &&
+ write_bin_log(thd, true, thd->query(), thd->query_length()))
+ DBUG_RETURN(true);
+ my_ok(thd);
+ DBUG_RETURN(false);
+ }
DBUG_RETURN(true);
+ }
part_table= table_list->table;
swap_table= swap_table_list->table;
@@ -537,6 +566,14 @@ bool Sql_cmd_alter_table_exchange_partition::
if (unlikely(check_exchange_partition(swap_table, part_table)))
DBUG_RETURN(TRUE);
+ if (part_table->file->check_if_updates_are_ignored("ALTER"))
+ DBUG_RETURN(return_with_logging(thd));
+
+ /* Add IF EXISTS to binlog if shared table */
+ if (part_table->file->partition_ht()->flags &
+ HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
+ force_if_exists= 1;
+
/* set lock pruning on first table */
partition_name= alter_info->partition_names.head();
if (unlikely(table_list->table->part_info->
@@ -563,8 +600,8 @@ bool Sql_cmd_alter_table_exchange_partition::
swap_table_list->db.str,
swap_table_list->table_name.str,
"", 0);
- /* create a unique temp name #sqlx-nnnn_nnnn, x for eXchange */
- my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%llx",
+ /* create a unique temp name */
+ my_snprintf(temp_name, sizeof(temp_name), "%s-exchange-%lx-%llx",
tmp_file_prefix, current_pid, thd->thread_id);
if (lower_case_table_names)
my_casedn_str(files_charset_info, temp_name);
@@ -638,6 +675,9 @@ bool Sql_cmd_alter_table_exchange_partition::
*/
(void) thd->locked_tables_list.reopen_tables(thd, false);
+ if (force_if_exists)
+ thd->variables.option_bits|= OPTION_IF_EXISTS;
+
if (unlikely((error= write_bin_log(thd, TRUE, thd->query(),
thd->query_length()))))
{
@@ -648,6 +688,7 @@ bool Sql_cmd_alter_table_exchange_partition::
(void) exchange_name_with_ddl_log(thd, part_file_name, swap_file_name,
temp_file_name, table_hton);
}
+ thd->variables.option_bits= save_option_bits;
err:
if (thd->locked_tables_mode)
@@ -746,7 +787,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
Alter_info *alter_info= &thd->lex->alter_info;
uint table_counter, i;
List<String> partition_names_list;
- bool binlog_stmt;
+ bool binlog_stmt, force_if_exists= 0;
DBUG_ENTER("Sql_cmd_alter_table_truncate_partition::execute");
/*
@@ -784,16 +825,41 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
#endif /* WITH_WSREP */
if (open_tables(thd, &first_table, &table_counter, 0))
- DBUG_RETURN(true);
+ {
+ if (thd->lex->if_exists() &&
+ thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
+ {
+ /*
+ ALTER TABLE IF EXISTS was used on not existing table
+ We have to log the query on a slave as the table may be a shared one
+ from the master and we need to ensure that the next slave can see
+ the statement as this slave may not have the table shared
+ */
+ thd->clear_error();
+ DBUG_RETURN(return_with_logging(thd));
+ }
+ DBUG_RETURN(TRUE);
+ }
- if (!first_table->table || first_table->view ||
- first_table->table->s->db_type() != partition_hton)
+ if (!first_table->table || first_table->view)
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
-
+ if (first_table->table->file->check_if_updates_are_ignored("ALTER"))
+ DBUG_RETURN(return_with_logging(thd));
+
+ if (first_table->table->s->db_type() != partition_hton)
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (first_table->table->file->partition_ht()->flags &
+ HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
+ force_if_exists= 1;
+
/*
Prune all, but named partitions,
to avoid excessive calls to external_lock().
@@ -825,8 +891,7 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
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.str,
- first_table->table_name.str, FALSE);
+ first_table->table->s->tdc->flush(thd, true);
partition= (ha_partition*) first_table->table->file;
/* Invoke the handler method responsible for truncating the partition. */
@@ -846,9 +911,14 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
*/
if (likely(error != HA_ERR_WRONG_COMMAND))
{
+ ulonglong save_option_bits= thd->variables.option_bits;
+ if (force_if_exists)
+ thd->variables.option_bits|= OPTION_IF_EXISTS;
+
query_cache_invalidate3(thd, first_table, FALSE);
if (binlog_stmt)
error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+ thd->variables.option_bits= save_option_bits;
}
/*
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 981420f03ae..633d969b3de 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -28,7 +28,6 @@
#include "sql_table.h"
#include "sql_show.h" // remove_status_vars, add_status_vars
#include "strfunc.h" // find_set
-#include "sql_acl.h" // *_ACL
#include "records.h" // init_read_record, end_read_record
#include <my_pthread.h>
#include <my_getopt.h>
@@ -38,8 +37,16 @@
#include <mysql/plugin_auth.h>
#include <mysql/plugin_password_validation.h>
#include <mysql/plugin_encryption.h>
+#include <mysql/plugin_data_type.h>
+#include <mysql/plugin_function.h>
#include "sql_plugin_compat.h"
+static PSI_memory_key key_memory_plugin_mem_root;
+static PSI_memory_key key_memory_plugin_int_mem_root;
+static PSI_memory_key key_memory_mysql_plugin;
+static PSI_memory_key key_memory_mysql_plugin_dl;
+static PSI_memory_key key_memory_plugin_bookmark;
+
#ifdef HAVE_LINK_H
#include <link.h>
#endif
@@ -90,7 +97,9 @@ const LEX_CSTRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{ STRING_WITH_LEN("REPLICATION") },
{ STRING_WITH_LEN("AUTHENTICATION") },
{ STRING_WITH_LEN("PASSWORD VALIDATION") },
- { STRING_WITH_LEN("ENCRYPTION") }
+ { STRING_WITH_LEN("ENCRYPTION") },
+ { STRING_WITH_LEN("DATA TYPE") },
+ { STRING_WITH_LEN("FUNCTION") }
};
extern int initialize_schema_table(st_plugin_int *plugin);
@@ -102,6 +111,8 @@ extern int finalize_audit_plugin(st_plugin_int *plugin);
extern int initialize_encryption_plugin(st_plugin_int *plugin);
extern int finalize_encryption_plugin(st_plugin_int *plugin);
+extern int initialize_data_type_plugin(st_plugin_int *plugin);
+
/*
The number of elements in both plugin_type_initialize and
plugin_type_deinitialize should equal to the number of plugins
@@ -110,13 +121,15 @@ extern int finalize_encryption_plugin(st_plugin_int *plugin);
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
0, ha_initialize_handlerton, 0, 0,initialize_schema_table,
- initialize_audit_plugin, 0, 0, 0, initialize_encryption_plugin
+ initialize_audit_plugin, 0, 0, 0, initialize_encryption_plugin,
+ initialize_data_type_plugin, 0
};
plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
0, ha_finalize_handlerton, 0, 0, finalize_schema_table,
- finalize_audit_plugin, 0, 0, 0, finalize_encryption_plugin
+ finalize_audit_plugin, 0, 0, 0, finalize_encryption_plugin, 0,
+ 0 // FUNCTION
};
/*
@@ -128,6 +141,8 @@ static int plugin_type_initialization_order[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
MYSQL_DAEMON_PLUGIN,
MariaDB_ENCRYPTION_PLUGIN,
+ MariaDB_DATA_TYPE_PLUGIN,
+ MariaDB_FUNCTION_PLUGIN,
MYSQL_STORAGE_ENGINE_PLUGIN,
MYSQL_INFORMATION_SCHEMA_PLUGIN,
MYSQL_FTPARSER_PLUGIN,
@@ -169,7 +184,9 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_REPLICATION_INTERFACE_VERSION,
MIN_AUTHENTICATION_INTERFACE_VERSION,
MariaDB_PASSWORD_VALIDATION_INTERFACE_VERSION,
- MariaDB_ENCRYPTION_INTERFACE_VERSION
+ MariaDB_ENCRYPTION_INTERFACE_VERSION,
+ MariaDB_DATA_TYPE_INTERFACE_VERSION,
+ MariaDB_FUNCTION_INTERFACE_VERSION
};
static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
@@ -182,7 +199,9 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_REPLICATION_INTERFACE_VERSION,
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
MariaDB_PASSWORD_VALIDATION_INTERFACE_VERSION,
- MariaDB_ENCRYPTION_INTERFACE_VERSION
+ MariaDB_ENCRYPTION_INTERFACE_VERSION,
+ MariaDB_DATA_TYPE_INTERFACE_VERSION,
+ MariaDB_FUNCTION_INTERFACE_VERSION
};
static struct
@@ -440,9 +459,8 @@ static struct st_plugin_dl *plugin_dl_find(const LEX_CSTRING *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))
+ ! files_charset_info->strnncoll(dl->str, dl->length,
+ tmp->dl.str, tmp->dl.length))
DBUG_RETURN(tmp);
}
DBUG_RETURN(0);
@@ -560,7 +578,7 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
/* no op */;
cur= (struct st_maria_plugin*)
- my_malloc((i + 1) * sizeof(struct st_maria_plugin),
+ my_malloc(key_memory_mysql_plugin, (i + 1) * sizeof(struct st_maria_plugin),
MYF(MY_ZEROFILL|MY_WME));
if (!cur)
{
@@ -680,7 +698,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
/* no op */;
cur= (struct st_maria_plugin*)
- my_malloc((i + 1) * sizeof(struct st_maria_plugin),
+ my_malloc(key_memory_mysql_plugin, (i + 1) * sizeof(struct st_maria_plugin),
MYF(MY_ZEROFILL|MY_WME));
if (!cur)
{
@@ -805,7 +823,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_CSTRING *dl, myf MyFlags)
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));
+ plugin_dl.ptr_backup= (st_ptr_backup *)my_malloc(key_memory_mysql_plugin_dl,
+ bytes, MYF(0));
if (!plugin_dl.ptr_backup)
{
restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
@@ -817,7 +836,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_CSTRING *dl, myf MyFlags)
/* 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))))
+ if (! (plugin_dl.dl.str= (char*) my_malloc(key_memory_mysql_plugin_dl,
+ plugin_dl.dl.length, MYF(0))))
{
my_error(ER_OUTOFMEMORY, MyFlags,
static_cast<int>(plugin_dl.dl.length));
@@ -964,7 +984,8 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
memory manager and/or valgrind to track locked references and
double unlocks to aid resolving reference counting problems.
*/
- if (!(plugin= (plugin_ref) my_malloc(sizeof(pi), MYF(MY_WME))))
+ if (!(plugin= (plugin_ref) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(pi),
+ MYF(MY_WME))))
DBUG_RETURN(NULL);
*plugin= pi;
@@ -1114,9 +1135,8 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists,
tmp.plugin_dl->mariaversion == 0))
continue; // unsupported plugin type
- if (name->str && my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)tmp.name.str, tmp.name.length))
+ if (name->str && system_charset_info->strnncoll(name->str, name->length,
+ tmp.name.str, tmp.name.length))
continue; // plugin name doesn't match
if (!name->str &&
@@ -1174,7 +1194,8 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists,
goto err;
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, "plugin", 4096, 4096, MYF(0));
+ init_alloc_root(key_memory_plugin_int_mem_root, &tmp_plugin_ptr->mem_root,
+ 4096, 4096, MYF(0));
if (name->str)
DBUG_RETURN(INSTALL_GOOD); // all done
@@ -1529,6 +1550,15 @@ static PSI_mutex_info all_plugin_mutexes[]=
{ &key_LOCK_plugin, "LOCK_plugin", PSI_FLAG_GLOBAL}
};
+static PSI_memory_info all_plugin_memory[]=
+{
+ { &key_memory_plugin_mem_root, "plugin_mem_root", PSI_FLAG_GLOBAL},
+ { &key_memory_plugin_int_mem_root, "plugin_int_mem_root", 0},
+ { &key_memory_mysql_plugin_dl, "mysql_plugin_dl", 0},
+ { &key_memory_mysql_plugin, "mysql_plugin", 0},
+ { &key_memory_plugin_bookmark, "plugin_bookmark", PSI_FLAG_GLOBAL}
+};
+
static void init_plugin_psi_keys(void)
{
const char* category= "sql";
@@ -1539,7 +1569,12 @@ static void init_plugin_psi_keys(void)
count= array_elements(all_plugin_mutexes);
PSI_server->register_mutex(category, all_plugin_mutexes, count);
+
+ count= array_elements(all_plugin_memory);
+ mysql_memory_register(category, all_plugin_memory, count);
}
+#else
+static void init_plugin_psi_keys(void) {}
#endif /* HAVE_PSI_INTERFACE */
/*
@@ -1558,6 +1593,9 @@ int plugin_init(int *argc, char **argv, int flags)
MEM_ROOT tmp_root;
bool reaped_mandatory_plugin= false;
bool mandatory= true;
+ I_List_iterator<i_string> opt_plugin_load_list_iter(opt_plugin_load_list);
+ char plugin_table_engine_name_buf[NAME_CHAR_LEN + 1];
+ LEX_CSTRING plugin_table_engine_name= { plugin_table_engine_name_buf, 0 };
LEX_CSTRING MyISAM= { STRING_WITH_LEN("MyISAM") };
DBUG_ENTER("plugin_init");
@@ -1566,11 +1604,13 @@ int plugin_init(int *argc, char **argv, int flags)
dlopen_count =0;
- init_alloc_root(&plugin_mem_root, "plugin", 4096, 4096, MYF(0));
- init_alloc_root(&plugin_vars_mem_root, "plugin_vars", 4096, 4096, MYF(0));
- init_alloc_root(&tmp_root, "plugin_tmp", 4096, 4096, MYF(0));
+ init_plugin_psi_keys();
+
+ init_alloc_root(key_memory_plugin_mem_root, &plugin_mem_root, 4096, 4096, MYF(0));
+ init_alloc_root(key_memory_plugin_mem_root, &plugin_vars_mem_root, 4096, 4096, MYF(0));
+ init_alloc_root(PSI_NOT_INSTRUMENTED, &tmp_root, 4096, 4096, MYF(0));
- if (my_hash_init(&bookmark_hash, &my_charset_bin, 32, 0, 0,
+ if (my_hash_init(key_memory_plugin_bookmark, &bookmark_hash, &my_charset_bin, 32, 0, 0,
get_bookmark_hash_key, NULL, HASH_UNIQUE))
goto err;
@@ -1578,15 +1618,15 @@ int plugin_init(int *argc, char **argv, int flags)
The 80 is from 2016-04-27 when we had 71 default plugins
Big enough to avoid many mallocs even in future
*/
- if (my_init_dynamic_array(&plugin_dl_array,
+ if (my_init_dynamic_array(key_memory_mysql_plugin_dl, &plugin_dl_array,
sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) ||
- my_init_dynamic_array(&plugin_array,
+ my_init_dynamic_array(key_memory_mysql_plugin, &plugin_array,
sizeof(struct st_plugin_int *), 80, 32, MYF(0)))
goto err;
for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
{
- if (my_hash_init(&plugin_hash[i], system_charset_info, 32, 0, 0,
+ if (my_hash_init(key_memory_plugin_mem_root, &plugin_hash[i], system_charset_info, 32, 0, 0,
get_plugin_hash_key, NULL, HASH_UNIQUE))
goto err;
}
@@ -1620,8 +1660,7 @@ int plugin_init(int *argc, char **argv, int flags)
for (plugin= *builtins; plugin->info; plugin++)
{
if (opt_ignore_builtin_innodb &&
- !my_strnncoll(&my_charset_latin1, (const uchar*) plugin->name,
- 6, (const uchar*) "InnoDB", 6))
+ !my_charset_latin1.strnncoll(plugin->name, 6, "InnoDB", 6))
continue;
bzero(&tmp, sizeof(tmp));
@@ -1668,35 +1707,22 @@ int plugin_init(int *argc, char **argv, int flags)
global_system_variables.table_plugin =
intern_plugin_lock(NULL, plugin_int_to_ref(plugin_ptr));
DBUG_SLOW_ASSERT(plugin_ptr->ref_count == 1);
-
}
mysql_mutex_unlock(&LOCK_plugin);
/* Register (not initialize!) all dynamic plugins */
- if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING))
+ if (global_system_variables.log_warnings >= 9)
+ sql_print_information("Initializing plugins specified on the command line");
+ while (i_string *item= opt_plugin_load_list_iter++)
+ plugin_load_list(&tmp_root, item->ptr);
+
+ if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
{
- 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))
- {
- char path[FN_REFLEN + 1];
- build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0);
- char engine_name_buf[NAME_CHAR_LEN + 1];
- LEX_CSTRING maybe_myisam= { engine_name_buf, 0 };
- bool is_sequence;
- Table_type frm_type= dd_frm_type(NULL, path, &maybe_myisam, &is_sequence);
- /* if mysql.plugin table is MyISAM - load it right away */
- if (frm_type == TABLE_TYPE_NORMAL && !strcasecmp(maybe_myisam.str, "MyISAM"))
- {
- plugin_load(&tmp_root);
- flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE;
- }
- }
+ char path[FN_REFLEN + 1];
+ build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0);
+ Table_type ttype= dd_frm_type(0, path, &plugin_table_engine_name);
+ if (ttype != TABLE_TYPE_NORMAL)
+ plugin_table_engine_name=empty_clex_str;
}
/*
@@ -1717,8 +1743,12 @@ int plugin_init(int *argc, char **argv, int flags)
plugin_ptr= (struct st_plugin_int *) my_hash_element(hash, idx);
if (plugin_ptr->state == PLUGIN_IS_UNINITIALIZED)
{
- if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv,
- (flags & PLUGIN_INIT_SKIP_INITIALIZATION)))
+ bool plugin_table_engine= lex_string_eq(&plugin_table_engine_name,
+ &plugin_ptr->name);
+ bool opts_only= flags & PLUGIN_INIT_SKIP_INITIALIZATION &&
+ (flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE ||
+ !plugin_table_engine);
+ if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv, opts_only))
{
plugin_ptr->state= PLUGIN_IS_DYING;
*(reap++)= plugin_ptr;
@@ -1752,7 +1782,7 @@ int plugin_init(int *argc, char **argv, int flags)
mysql_mutex_unlock(&LOCK_plugin);
my_afree(reap);
- if (reaped_mandatory_plugin)
+ if (reaped_mandatory_plugin && !opt_help)
goto err;
free_root(&tmp_root, MYF(0));
@@ -1822,11 +1852,13 @@ static void plugin_load(MEM_ROOT *tmp_root)
{
DBUG_PRINT("error",("Can't open plugin table"));
if (!opt_help)
- sql_print_error("Could not open mysql.plugin table. "
- "Some plugins may be not loaded");
+ sql_print_error("Could not open mysql.plugin table: \"%s\". "
+ "Some plugins may be not loaded",
+ new_thd->get_stmt_da()->message());
else
- sql_print_warning("Could not open mysql.plugin table. "
- "Some options may be missing from the help text");
+ sql_print_warning("Could not open mysql.plugin table: \"%s\". "
+ "Some options may be missing from the help text",
+ new_thd->get_stmt_da()->message());
goto end;
}
@@ -2159,14 +2191,13 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name,
of the insert into the plugin table, so that it is not replicated in
row based mode.
*/
- tmp_disable_binlog(thd);
+ DBUG_ASSERT(!table->file->row_logging);
table->use_all_columns();
restore_record(table, s->default_values);
table->field[0]->store(name->str, name->length, system_charset_info);
table->field[1]->store(tmp->plugin_dl->dl.str, tmp->plugin_dl->dl.length,
files_charset_info);
error= table->file->ha_write_row(table->record[0]);
- reenable_binlog(thd);
if (unlikely(error))
{
table->file->print_error(error, MYF(0));
@@ -2230,6 +2261,7 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name,
mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
+ DEBUG_SYNC(thd, "acquired_LOCK_plugin");
error= plugin_add(thd->mem_root, thd->lex->create_info.if_not_exists(),
name, &dl, MYF(0));
if (unlikely(error != INSTALL_GOOD))
@@ -2315,9 +2347,8 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name)
of the delete from the plugin table, so that it is not replicated in
row based mode.
*/
- tmp_disable_binlog(thd);
+ table->file->row_logging= 0; // No logging
error= table->file->ha_delete_row(table->record[0]);
- reenable_binlog(thd);
if (unlikely(error))
{
table->file->print_error(error, MYF(0));
@@ -2857,7 +2888,8 @@ static void update_func_str(THD *thd, struct st_mysql_sys_var *var,
{
char *old= *(char**) tgt;
if (value)
- *(char**) tgt= my_strdup(value, MYF(0));
+ *(char**) tgt= my_strdup(key_memory_global_system_variables,
+ value, MYF(0));
else
*(char**) tgt= 0;
my_free(old);
@@ -3001,10 +3033,12 @@ static st_bookmark *register_var(const char *plugin, const char *name,
if (new_size > global_variables_dynamic_size)
{
global_system_variables.dynamic_variables_ptr= (char*)
- my_realloc(global_system_variables.dynamic_variables_ptr, new_size,
+ my_realloc(key_memory_global_system_variables,
+ global_system_variables.dynamic_variables_ptr, new_size,
MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
max_system_variables.dynamic_variables_ptr= (char*)
- my_realloc(max_system_variables.dynamic_variables_ptr, new_size,
+ my_realloc(key_memory_global_system_variables,
+ max_system_variables.dynamic_variables_ptr, new_size,
MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
/*
Clear the new variable value space. This is required for string
@@ -3046,7 +3080,8 @@ void sync_dynamic_session_variables(THD* thd, bool global_lock)
uint idx;
thd->variables.dynamic_variables_ptr= (char*)
- my_realloc(thd->variables.dynamic_variables_ptr,
+ my_realloc(key_memory_THD_variables,
+ thd->variables.dynamic_variables_ptr,
global_variables_dynamic_size,
MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR));
@@ -3080,7 +3115,7 @@ void sync_dynamic_session_variables(THD* thd, bool global_lock)
{
char **pp= (char**) (thd->variables.dynamic_variables_ptr + v->offset);
if (*pp)
- *pp= my_strdup(*pp, MYF(MY_WME|MY_FAE));
+ *pp= my_strdup(key_memory_THD_variables, *pp, MYF(MY_WME|MY_FAE));
}
}
@@ -3937,7 +3972,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
(opt->flags & PLUGIN_VAR_MEMALLOC))
{
char *def_val= *(char**)var_def_ptr(opt);
- *(char**)val= def_val ? my_strdup(def_val, MYF(0)) : NULL;
+ *(char**)val= def_val ? my_strdup(PSI_INSTRUMENT_ME, def_val, MYF(0)) : NULL;
}
else
memcpy(val, var_def_ptr(opt), var_storage_size(opt->flags));
@@ -4008,13 +4043,19 @@ static my_option *construct_help_options(MEM_ROOT *mem_root,
DBUG_RETURN(opts);
}
-extern "C" my_bool mark_changed(int, const struct my_option *, char *);
-my_bool mark_changed(int, const struct my_option *opt, char *)
+extern "C" my_bool mark_changed(const struct my_option *, char *, const char *);
+my_bool mark_changed(const struct my_option *opt, char *, const char *filename)
{
if (opt->app_type)
{
sys_var *var= (sys_var*) opt->app_type;
- var->value_origin= sys_var::CONFIG;
+ if (*filename)
+ {
+ var->origin_filename= filename;
+ var->value_origin= sys_var::CONFIG;
+ }
+ else
+ var->value_origin= sys_var::COMMAND_LINE;
}
return 0;
}
@@ -4179,7 +4220,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
*/
if (disable_plugin)
{
- if (global_system_variables.log_warnings)
+ if (global_system_variables.log_warnings && !opt_help)
sql_print_information("Plugin '%s' is disabled.",
tmp->name.str);
goto err;
@@ -4187,7 +4228,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
if (tmp->plugin->system_vars)
{
- for (opt= tmp->plugin->system_vars; *opt; opt++)
+ if (mysqld_server_started)
{
/*
PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
@@ -4200,13 +4241,22 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
Thus, for all plugins loaded after the server was started,
we copy string values to a plugin's memroot.
*/
- if (mysqld_server_started &&
- (((*opt)->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 *)*opt;
- if (*str->value)
- *str->value= strdup_root(mem_root, *str->value);
+ if ((((*opt)->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT |
+ PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
+ {
+ sysvar_str_t* str= (sysvar_str_t *)*opt;
+ if (*str->value)
+ *str->value= strdup_root(mem_root, *str->value);
+ }
+ }
+ /* same issue with config file names */
+ for (my_option *mo=opts; mo->name; mo++)
+ {
+ sys_var *var= (sys_var*) mo->app_type;
+ if (var && var->value_origin == sys_var::CONFIG)
+ var->origin_filename= strdup_root(mem_root, var->origin_filename);
}
}
@@ -4359,9 +4409,7 @@ int thd_setspecific(MYSQL_THD thd, MYSQL_THD_KEY_T key, void *value)
void plugin_mutex_init()
{
-#ifdef HAVE_PSI_INTERFACE
init_plugin_psi_keys();
-#endif
mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST);
}
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index c9c75d07a6e..e1360794a3b 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -55,9 +55,8 @@ extern ulong dlopen_count;
/*
the following flags are valid for plugin_init()
*/
-#define PLUGIN_INIT_SKIP_DYNAMIC_LOADING 1U
-#define PLUGIN_INIT_SKIP_PLUGIN_TABLE 2U
-#define PLUGIN_INIT_SKIP_INITIALIZATION 4U
+#define PLUGIN_INIT_SKIP_PLUGIN_TABLE 1U
+#define PLUGIN_INIT_SKIP_INITIALIZATION 2U
#define INITIAL_LEX_PLUGIN_LIST_SIZE 16
diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic
index 69e57de5c8b..0911170fb74 100644
--- a/sql/sql_plugin_services.ic
+++ b/sql/sql_plugin_services.ic
@@ -1,5 +1,5 @@
/* Copyright (c) 2009, 2010, Oracle and/or its affiliates.
- Copyright (c) 2012, 2014, Monty Program Ab
+ Copyright (c) 2012, 2020, MariaDB Corporation.
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,6 +17,7 @@
/* support for Services */
#include <service_versions.h>
#include <mysql/service_wsrep.h>
+#include <mysql/service_thd_mdl.h>
struct st_service_ref {
const char *name;
@@ -174,6 +175,9 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_get_debug,
wsrep_commit_ordered,
wsrep_thd_is_applying,
+ wsrep_OSU_method_get,
+ wsrep_thd_has_ignored_error,
+ wsrep_thd_set_ignored_error,
wsrep_thd_set_wsrep_aborter,
wsrep_report_bf_lock_wait
};
@@ -220,6 +224,11 @@ struct json_service_st json_handler=
json_unescape_json
};
+static struct thd_mdl_service_st thd_mdl_handler=
+{
+ thd_mdl_context
+};
+
static struct st_service_ref list_of_services[]=
{
{ "base64_service", VERSION_base64, &base64_handler },
@@ -243,6 +252,7 @@ static struct st_service_ref list_of_services[]=
{ "thd_timezone_service", VERSION_thd_timezone, &thd_timezone_handler },
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "wsrep_service", VERSION_wsrep, &wsrep_handler },
- { "json_service", VERSION_json, &json_handler }
+ { "json_service", VERSION_json, &json_handler },
+ { "thd_mdl_service", VERSION_thd_mdl, &thd_mdl_handler }
};
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 518df6eb6a2..bb0a399d1ed 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -99,7 +99,6 @@ When one supplies long data for a placeholder:
#include "sql_insert.h" // upgrade_lock_type_for_insert, mysql_prepare_insert
#include "sql_update.h" // mysql_prepare_update
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
-#include "sql_acl.h" // *_ACL
#include "sql_derived.h" // mysql_derived_prepare,
// mysql_handle_derived
#include "sql_cte.h"
@@ -122,6 +121,7 @@ When one supplies long data for a placeholder:
#include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL
#include "sql_handler.h"
#include "transaction.h" // trans_rollback_implicit
+#include "mysql/psi/mysql_ps.h" // MYSQL_EXECUTE_PS
#include "wsrep_mysqld.h"
/**
@@ -160,6 +160,7 @@ public:
};
THD *thd;
+ PSI_prepared_stmt* m_prepared_stmt;
Select_fetch_protocol_binary result;
Item_param **param_array;
Server_side_cursor *cursor;
@@ -242,62 +243,6 @@ private:
class Ed_connection;
-/**
- Protocol_local: a helper class to intercept the result
- of the data written to the network.
-*/
-
-class Protocol_local :public Protocol
-{
-public:
- Protocol_local(THD *thd, Ed_connection *ed_connection);
- ~Protocol_local() { free_root(&m_rset_root, MYF(0)); }
-protected:
- virtual void prepare_for_resend();
- virtual bool write();
- virtual bool store_null();
- virtual bool store_tiny(longlong from);
- virtual bool store_short(longlong from);
- virtual bool store_long(longlong from);
- virtual bool store_longlong(longlong from, bool unsigned_flag);
- virtual bool store_decimal(const my_decimal *);
- virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
- virtual bool store(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
- virtual bool store(MYSQL_TIME *time, int decimals);
- virtual bool store_date(MYSQL_TIME *time);
- virtual bool store_time(MYSQL_TIME *time, int decimals);
- virtual bool store(float value, uint32 decimals, String *buffer);
- virtual bool store(double value, uint32 decimals, String *buffer);
- virtual bool store(Field *field);
-
- virtual bool send_result_set_metadata(List<Item> *list, uint flags);
- virtual bool send_out_parameters(List<Item_param> *sp_params);
-#ifdef EMBEDDED_LIBRARY
- void remove_last_row();
-#endif
- virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
-
- virtual bool send_ok(uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush);
-
- virtual bool send_eof(uint server_status, uint statement_warn_count);
- virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate);
-private:
- bool store_string(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
-
- bool store_column(const void *data, size_t length);
- void opt_add_row_to_rset();
-private:
- Ed_connection *m_connection;
- MEM_ROOT m_rset_root;
- List<Ed_row> *m_rset;
- size_t m_column_count;
- Ed_column *m_current_row;
- Ed_column *m_current_column;
-};
/******************************************************************************
Implementation
@@ -739,6 +684,8 @@ void Item_param::setup_conversion(THD *thd, uchar param_type)
*/
if (!h)
h= &type_handler_string;
+ else if (unsigned_flag)
+ h= h->type_handler_unsigned();
set_handler(h);
h->Item_param_setup_conversion(thd, this);
}
@@ -1296,9 +1243,8 @@ static bool mysql_test_insert(Prepared_statement *stmt,
table_list->table->insert_values=(uchar *)1;
}
- if (mysql_prepare_insert(thd, table_list, table_list->table,
- fields, values, update_fields, update_values,
- duplic, &unused_conds, FALSE))
+ if (mysql_prepare_insert(thd, table_list, fields, values, update_fields,
+ update_values, duplic, &unused_conds, FALSE))
goto error;
value_count= values->elements;
@@ -1359,7 +1305,7 @@ static int mysql_test_update(Prepared_statement *stmt,
TABLE_LIST *update_source_table;
SELECT_LEX *select= stmt->lex->first_select_lex();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- uint want_privilege;
+ privilege_t want_privilege(NO_ACL);
#endif
DBUG_ENTER("mysql_test_update");
@@ -1482,8 +1428,6 @@ static bool mysql_test_delete(Prepared_statement *stmt,
}
DBUG_RETURN(mysql_prepare_delete(thd, table_list,
- lex->first_select_lex()->with_wild,
- lex->first_select_lex()->item_list,
&lex->first_select_lex()->where,
&delete_while_scanning));
error:
@@ -1518,7 +1462,7 @@ static int mysql_test_select(Prepared_statement *stmt,
lex->first_select_lex()->context.resolve_in_select_list= TRUE;
- ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
+ privilege_t privilege(lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL);
if (tables)
{
if (check_table_access(thd, privilege, tables, FALSE, UINT_MAX, FALSE))
@@ -1935,20 +1879,21 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_slave_status(Prepared_statement *stmt)
+static int mysql_test_show_slave_status(Prepared_statement *stmt,
+ bool show_all_slaves_stat)
{
DBUG_ENTER("mysql_test_show_slave_status");
THD *thd= stmt->thd;
List<Item> fields;
- show_master_info_get_fields(thd, &fields, thd->lex->verbose, 0);
-
+ show_master_info_get_fields(thd, &fields, show_all_slaves_stat, 0);
+
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
/**
- Validate and prepare for execution SHOW MASTER STATUS statement.
+ Validate and prepare for execution SHOW BINLOG STATUS statement.
@param stmt prepared statement
@@ -1958,9 +1903,9 @@ static int mysql_test_show_slave_status(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_master_status(Prepared_statement *stmt)
+static int mysql_test_show_binlog_status(Prepared_statement *stmt)
{
- DBUG_ENTER("mysql_test_show_master_status");
+ DBUG_ENTER("mysql_test_show_binlog_status");
THD *thd= stmt->thd;
List<Item> fields;
@@ -2156,7 +2101,7 @@ static int mysql_insert_select_prepare_tester(THD *thd)
thd->lex->first_select_lex()->context.first_name_resolution_table=
second_table;
- return mysql_insert_select_prepare(thd);
+ return mysql_insert_select_prepare(thd, NULL);
}
@@ -2395,14 +2340,22 @@ static bool check_prepared_statement(Prepared_statement *stmt)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
#ifndef EMBEDDED_LIBRARY
case SQLCOM_SHOW_SLAVE_STAT:
- if ((res= mysql_test_show_slave_status(stmt)) == 2)
{
- /* Statement and field info has already been sent */
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(thd->lex->m_sql_cmd);
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*>(thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+ if ((res= mysql_test_show_slave_status(stmt,
+ cmd->is_show_all_slaves_stat()))
+ == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
}
- break;
- case SQLCOM_SHOW_MASTER_STAT:
- if ((res= mysql_test_show_master_status(stmt)) == 2)
+ case SQLCOM_SHOW_BINLOG_STAT:
+ if ((res= mysql_test_show_binlog_status(stmt)) == 2)
{
/* Statement and field info has already been sent */
DBUG_RETURN(FALSE);
@@ -2657,8 +2610,22 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= &thd->protocol_binary;
+ /* Create PS table entry, set query text after rewrite. */
+ stmt->m_prepared_stmt= MYSQL_CREATE_PS(stmt, stmt->id,
+ thd->m_statement_psi,
+ stmt->name.str, stmt->name.length);
+
if (stmt->prepare(packet, packet_length))
{
+ /*
+ Prepare failed and stmt will be freed.
+ Now we have to save the query_string in the so the
+ audit plugin later gets the meaningful notification.
+ */
+ if (alloc_query(thd, stmt->query_string.str(), stmt->query_string.length()))
+ {
+ thd->set_query(0, 0);
+ }
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
thd->clear_last_stmt();
@@ -2793,6 +2760,7 @@ bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd,
void mysql_sql_stmt_prepare(THD *thd)
{
LEX *lex= thd->lex;
+ CSET_STRING orig_query= thd->query_string;
const LEX_CSTRING *name= &lex->prepared_stmt.name();
Prepared_statement *stmt;
LEX_CSTRING query;
@@ -2858,14 +2826,28 @@ void mysql_sql_stmt_prepare(THD *thd)
*/
Item_change_list_savepoint change_list_savepoint(thd);
- if (stmt->prepare(query.str, (uint) query.length))
+ /* Create PS table entry, set query text after rewrite. */
+ stmt->m_prepared_stmt= MYSQL_CREATE_PS(stmt, stmt->id,
+ thd->m_statement_psi,
+ stmt->name.str, stmt->name.length);
+
+ bool res= stmt->prepare(query.str, (uint) query.length);
+ /*
+ stmt->prepare() sets thd->query_string with the prepared
+ query, so the audit plugin gets adequate notification with the
+ mysqld_stmt_* set of functions.
+ But here we should restore the original query so it's mentioned in
+ logs properly.
+ */
+ thd->set_query(orig_query);
+ if (res)
{
/* Statement map deletes the statement on erase */
thd->stmt_map.erase(stmt);
}
else
{
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
my_ok(thd, 0L, 0L, "Statement prepared");
}
change_list_savepoint.rollback(thd);
@@ -2877,6 +2859,7 @@ void mysql_sql_stmt_prepare(THD *thd)
void mysql_sql_stmt_execute_immediate(THD *thd)
{
LEX *lex= thd->lex;
+ CSET_STRING orig_query= thd->query_string;
Prepared_statement *stmt;
LEX_CSTRING query;
DBUG_ENTER("mysql_sql_stmt_execute_immediate");
@@ -2925,6 +2908,14 @@ void mysql_sql_stmt_execute_immediate(THD *thd)
thd->free_items();
thd->free_list= free_list_backup;
+ /*
+ stmt->execute_immediately() sets thd->query_string with the executed
+ query, so the audit plugin gets adequate notification with the
+ mysqld_stmt_* set of functions.
+ But here we should restore the original query so it's mentioned in
+ logs properly.
+ */
+ thd->set_query_inner(orig_query);
stmt->lex->restore_set_statement_var();
delete stmt;
DBUG_VOID_RETURN;
@@ -3241,6 +3232,13 @@ static void mysql_stmt_execute_common(THD *thd,
if (!(stmt= find_prepared_statement(thd, stmt_id)))
{
char llbuf[22];
+ /*
+ Did not find the statement with the provided stmt_id.
+ Set thd->query_string with the stmt_id so the
+ audit plugin gets the meaningful notification.
+ */
+ if (alloc_query(thd, llbuf, strlen(llbuf)))
+ thd->set_query(0, 0);
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), static_cast<int>(sizeof(llbuf)),
llstr(stmt_id, llbuf), "mysqld_stmt_execute");
DBUG_VOID_RETURN;
@@ -3256,6 +3254,8 @@ static void mysql_stmt_execute_common(THD *thd,
open_cursor= MY_TEST(cursor_flags & (ulong) CURSOR_TYPE_READ_ONLY);
thd->protocol= &thd->protocol_binary;
+ MYSQL_EXECUTE_PS(thd->m_statement_psi, stmt->m_prepared_stmt);
+
if (!bulk_op)
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
else
@@ -3365,6 +3365,8 @@ void mysql_sql_stmt_execute(THD *thd)
CALL p1('x');
*/
Item_change_list_savepoint change_list_savepoint(thd);
+ MYSQL_EXECUTE_PS(thd->m_statement_psi, stmt->m_prepared_stmt);
+
(void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL);
change_list_savepoint.rollback(thd);
thd->free_items(); // Free items created by execute_loop()
@@ -3548,7 +3550,7 @@ void mysql_sql_stmt_close(THD *thd)
else
{
stmt->deallocate();
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
my_ok(thd);
}
}
@@ -3769,6 +3771,7 @@ Execute_sql_statement::execute_server_code(THD *thd)
end:
thd->lex->restore_set_statement_var();
+ delete_explain_query(thd->lex);
lex_end(thd->lex);
return error;
@@ -3783,6 +3786,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
STMT_INITIALIZED,
((++thd_arg->statement_id_counter) & STMT_ID_MASK)),
thd(thd_arg),
+ m_prepared_stmt(NULL),
result(thd_arg),
param_array(0),
cursor(0),
@@ -3796,10 +3800,9 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
read_types(0),
m_sql_mode(thd->variables.sql_mode)
{
- init_sql_alloc(&main_mem_root, "Prepared_statement",
- thd_arg->variables.query_alloc_block_size,
- thd_arg->variables.query_prealloc_size,
- MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_prepared_statement_main_mem_root,
+ &main_mem_root, thd_arg->variables.query_alloc_block_size,
+ thd_arg->variables.query_prealloc_size, MYF(MY_THREAD_SPECIFIC));
*last_error= '\0';
}
@@ -3865,6 +3868,9 @@ Prepared_statement::~Prepared_statement()
DBUG_ENTER("Prepared_statement::~Prepared_statement");
DBUG_PRINT("enter",("stmt: %p cursor: %p",
this, cursor));
+
+ MYSQL_DESTROY_PS(m_prepared_stmt);
+
delete cursor;
/*
We have to call free on the items even if cleanup is called as some items,
@@ -3997,6 +4003,19 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
DBUG_RETURN(TRUE);
}
+ /*
+ We'd like to have thd->query to be set to the actual query
+ after the function ends.
+ This value will be sent to audit plugins later.
+ As the statement is created, the query will be stored
+ in statement's arena. Normally the statement lives longer than
+ the end of this query, so we can just set thd->query_string to
+ be the stmt->query_string.
+ Though errors can result in statement to be freed. These cases
+ should be handled appropriately.
+ */
+ stmt_backup.query_string= thd->query_string;
+
old_stmt_arena= thd->stmt_arena;
thd->stmt_arena= this;
@@ -4065,7 +4084,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
lex->unit.cleanup();
/* No need to commit statement transaction, it's not started. */
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
@@ -4098,6 +4117,8 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
state= Query_arena::STMT_PREPARED;
flags&= ~ (uint) IS_IN_USE;
+ MYSQL_SET_PS_TEXT(m_prepared_stmt, query(), query_length());
+
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
prepared statements this causes two records to be output:
@@ -4504,18 +4525,18 @@ Prepared_statement::reprepare()
TRUE, &cur_db_changed)))
return TRUE;
- sql_mode_t save_sql_mode= thd->variables.sql_mode;
- thd->variables.sql_mode= m_sql_mode;
+ Sql_mode_instant_set sms(thd, m_sql_mode);
+
error= ((name.str && copy.set_name(&name)) ||
copy.prepare(query(), query_length()) ||
validate_metadata(&copy));
- thd->variables.sql_mode= save_sql_mode;
if (cur_db_changed)
mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE);
if (likely(!error))
{
+ MYSQL_REPREPARE_PS(m_prepared_stmt);
swap_prepared_statement(&copy);
swap_parameter_array(param_array, copy.param_array, param_count);
#ifdef DBUG_ASSERT_EXISTS
@@ -4530,6 +4551,15 @@ Prepared_statement::reprepare()
*/
thd->get_stmt_da()->clear_warning_info(thd->query_id);
}
+ else
+ {
+ /*
+ Prepare failed and the 'copy' will be freed.
+ Now we have to restore the query_string in the so the
+ audit plugin later gets the meaningful notification.
+ */
+ thd->set_query(query(), query_length());
+ }
return error;
}
@@ -4753,17 +4783,13 @@ 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,
thd->get_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
@@ -4876,7 +4902,11 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len)
set_sql_prepare();
name= execute_immediate_stmt_name; // for DBUG_PRINT etc
- if (unlikely(prepare(query, query_len)))
+
+ m_prepared_stmt= MYSQL_CREATE_PS(this, id, thd->m_statement_psi,
+ name.str, name.length);
+
+ if (prepare(query, query_len))
DBUG_RETURN(true);
if (param_count != thd->lex->prepared_stmt.param_count())
@@ -4886,6 +4916,7 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len)
DBUG_RETURN(true);
}
+ MYSQL_EXECUTE_PS(thd->m_statement_psi, m_prepared_stmt);
(void) execute_loop(&expanded_query, FALSE, NULL, NULL);
deallocate_immediate();
DBUG_RETURN(false);
@@ -5006,12 +5037,12 @@ Ed_connection::free_old_result()
*/
bool
-Ed_connection::execute_direct(LEX_STRING sql_text)
+Ed_connection::execute_direct(Protocol *p, LEX_STRING sql_text)
{
Execute_sql_statement execute_sql_statement(sql_text);
DBUG_PRINT("ed_query", ("%s", sql_text.str));
- return execute_direct(&execute_sql_statement);
+ return execute_direct(p, &execute_sql_statement);
}
@@ -5028,10 +5059,9 @@ Ed_connection::execute_direct(LEX_STRING sql_text)
@param server_runnable A code fragment to execute.
*/
-bool Ed_connection::execute_direct(Server_runnable *server_runnable)
+bool Ed_connection::execute_direct(Protocol *p, Server_runnable *server_runnable)
{
bool rc= FALSE;
- 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->get_stmt_da();
@@ -5040,7 +5070,7 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable)
free_old_result(); /* Delete all data from previous execution, if any */
- m_thd->protocol= &protocol_local;
+ m_thd->protocol= p;
m_thd->set_stmt_da(&m_diagnostics_area);
rc= stmt.execute_server_runnable(server_runnable);
@@ -5127,354 +5157,741 @@ Ed_connection::store_result_set()
return ed_result_set;
}
-/*************************************************************************
-* Protocol_local
-**************************************************************************/
-
-Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection)
- :Protocol(thd),
- m_connection(ed_connection),
- m_rset(NULL),
- m_column_count(0),
- m_current_row(NULL),
- m_current_column(NULL)
-{
- clear_alloc_root(&m_rset_root);
-}
+/*
+ MENT-56
+ Protocol_local and service_sql for plugins to enable 'local' SQL query execution.
+*/
-/**
- Called between two result set rows.
+#ifndef EMBEDDED_LIBRARY
+// This part is mostly copied from libmysqld/lib_sql.cc
+// TODO: get rid of code duplications
- Prepare structures to fill result set rows.
- Unfortunately, we can't return an error here. If memory allocation
- fails, we'll have to return an error later. And so is done
- in methods such as @sa store_column().
-*/
+#include <mysql.h>
+#include "../libmysqld/embedded_priv.h"
-void Protocol_local::prepare_for_resend()
+class Protocol_local : public Protocol_text
{
- DBUG_ASSERT(alloc_root_inited(&m_rset_root));
+public:
+ struct st_mysql_data *cur_data;
+ struct st_mysql_data *first_data;
+ struct st_mysql_data **data_tail;
+ void clear_data_list();
+ struct st_mysql_data *alloc_new_dataset();
+ char **next_field;
+ MYSQL_FIELD *next_mysql_field;
+ MEM_ROOT *alloc;
+
+ Protocol_local(THD *thd_arg, ulong prealloc= 0) :
+ Protocol_text(thd_arg, prealloc),
+ cur_data(0), first_data(0), data_tail(&first_data), alloc(0)
+ {}
+
+protected:
+ bool net_store_data(const uchar *from, size_t length);
+ bool net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
+ bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
+ bool, bool);
+ bool net_send_error_packet(THD *, uint, const char *, const char *);
+ bool begin_dataset();
+ bool begin_dataset(THD *thd, uint numfields);
+
+ bool write();
+ bool flush();
+
+ bool store_field_metadata(const THD *thd, const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos);
+ bool send_result_set_metadata(List<Item> *list, uint flags);
+ void remove_last_row();
+ bool store_null();
+ void prepare_for_resend();
+ bool send_list_fields(List<Field> *list, const TABLE_LIST *table_list);
+
+ enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
+};
- opt_add_row_to_rset();
- /* Start a new row. */
- m_current_row= (Ed_column *) alloc_root(&m_rset_root,
- sizeof(Ed_column) * m_column_count);
- m_current_column= m_current_row;
+static
+bool
+write_eof_packet_local(THD *thd,
+ Protocol_local *p, uint server_status, uint statement_warn_count)
+{
+// if (!thd->mysql) // bootstrap file handling
+// return FALSE;
+ /*
+ The following test should never be true, but it's better to do it
+ because if 'is_fatal_error' is set the server is not going to execute
+ other queries (see the if test in dispatch_command / COM_QUERY)
+ */
+ if (thd->is_fatal_error)
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+ p->cur_data->embedded_info->server_status= server_status;
+ /*
+ Don't send warn count during SP execution, as the warn_list
+ is cleared between substatements, and mysqltest gets confused
+ */
+ p->cur_data->embedded_info->warning_count=
+ (thd->spcont ? 0 : MY_MIN(statement_warn_count, 65535));
+ return FALSE;
}
-/**
- In "real" protocols this is called to finish a result set row.
- Unused in the local implementation.
-*/
-
-bool Protocol_local::write()
+MYSQL_DATA *Protocol_local::alloc_new_dataset()
{
- return FALSE;
+ MYSQL_DATA *data;
+ struct embedded_query_result *emb_data;
+ if (!my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME | MY_ZEROFILL),
+ &data, sizeof(*data),
+ &emb_data, sizeof(*emb_data),
+ NULL))
+ return NULL;
+
+ emb_data->prev_ptr= &data->data;
+ cur_data= data;
+ *data_tail= data;
+ data_tail= &emb_data->next;
+ data->embedded_info= emb_data;
+ return data;
}
-/**
- A helper function to add the current row to the current result
- set. Called in @sa prepare_for_resend(), when a new row is started,
- and in send_eof(), when the result set is finished.
-*/
-void Protocol_local::opt_add_row_to_rset()
+static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
- if (m_current_row)
+ uint32 dummy32;
+ uint dummy_err;
+ char *result;
+
+ /* 'tocs' is set 0 when client issues SET character_set_results=NULL */
+ if (tocs && String::needs_conversion(0, fromcs, tocs, &dummy32))
+ {
+ uint new_len= (tocs->mbmaxlen * length) / fromcs->mbminlen + 1;
+ result= (char *)alloc_root(root, new_len);
+ length= copy_and_convert(result, new_len,
+ tocs, from, length, fromcs, &dummy_err);
+ }
+ else
{
- /* Add the old row to the result set */
- Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count);
- if (ed_row)
- m_rset->push_back(ed_row, &m_rset_root);
+ result= (char *)alloc_root(root, length + 1);
+ memcpy(result, from, length);
}
+
+ result[length]= 0;
+ return result;
}
-/**
- Add a NULL column to the current row.
-*/
+static char *dup_str_aux(MEM_ROOT *root, const LEX_CSTRING &from,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+{
+ return dup_str_aux(root, from.str, (uint) from.length, fromcs, tocs);
+}
-bool Protocol_local::store_null()
+
+bool Protocol_local::net_store_data(const uchar *from, size_t length)
{
- if (m_current_column == NULL)
- return TRUE; /* prepare_for_resend() failed to allocate memory. */
+ char *field_buf;
+// if (!thd->mysql) // bootstrap file handling
+// return FALSE;
- bzero(m_current_column, sizeof(*m_current_column));
- ++m_current_column;
+ if (!(field_buf= (char*) alloc_root(alloc, length + sizeof(uint) + 1)))
+ return TRUE;
+ *(uint *)field_buf= (uint) length;
+ *next_field= field_buf + sizeof(uint);
+ memcpy((uchar*) *next_field, from, length);
+ (*next_field)[length]= 0;
+ if (next_mysql_field->max_length < length)
+ next_mysql_field->max_length= (unsigned long) length;
+ ++next_field;
+ ++next_mysql_field;
return FALSE;
}
+bool Protocol_local::net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
+{
+ uint conv_length= (uint) (to_cs->mbmaxlen * length / from_cs->mbminlen);
+ uint dummy_error;
+ char *field_buf;
+// if (!thd->mysql) // bootstrap file handling
+// return false;
+
+ if (!(field_buf= (char*) alloc_root(alloc, conv_length + sizeof(uint) + 1)))
+ return true;
+ *next_field= field_buf + sizeof(uint);
+ length= copy_and_convert(*next_field, conv_length, to_cs,
+ (const char*) from, length, from_cs, &dummy_error);
+ *(uint *) field_buf= (uint) length;
+ (*next_field)[length]= 0;
+ if (next_mysql_field->max_length < length)
+ next_mysql_field->max_length= (unsigned long) length;
+ ++next_field;
+ ++next_mysql_field;
+ return false;
+}
+
+
/**
- A helper method to add any column to the current row
- in its binary form.
+ Embedded library implementation of OK response.
- Allocates memory for the data in the result set memory root.
+ This function is used by the server to write 'OK' packet to
+ the "network" when the server is compiled as an embedded library.
+ Since there is no network in the embedded configuration,
+ a different implementation is necessary.
+ Instead of marshalling response parameters to a network representation
+ and then writing it to the socket, here we simply copy the data to the
+ corresponding client-side connection structures.
+
+ @sa Server implementation of net_send_ok in protocol.cc for
+ description of the arguments.
+
+ @return
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
-bool Protocol_local::store_column(const void *data, size_t length)
+bool
+Protocol_local::net_send_ok(THD *thd,
+ uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong id, const char *message, bool, bool)
{
- if (m_current_column == NULL)
- return TRUE; /* prepare_for_resend() failed to allocate memory. */
- /*
- alloc_root() automatically aligns memory, so we don't need to
- do any extra alignment if we're pointing to, say, an integer.
- */
- m_current_column->str= (char*) memdup_root(&m_rset_root,
- data,
- length + 1 /* Safety */);
- if (! m_current_column->str)
- return TRUE;
- m_current_column->str[length]= '\0'; /* Safety */
- m_current_column->length= length;
- ++m_current_column;
- return FALSE;
+ DBUG_ENTER("emb_net_send_ok");
+ MYSQL_DATA *data;
+// MYSQL *mysql= thd->mysql;
+
+// if (!mysql) // bootstrap file handling
+// DBUG_RETURN(FALSE);
+ if (!(data= alloc_new_dataset()))
+ DBUG_RETURN(TRUE);
+ data->embedded_info->affected_rows= affected_rows;
+ data->embedded_info->insert_id= id;
+ if (message)
+ strmake_buf(data->embedded_info->info, message);
+
+ bool error= write_eof_packet_local(thd, this,
+ server_status, statement_warn_count);
+ cur_data= 0;
+ DBUG_RETURN(error);
}
/**
- Store a string value in a result set column, optionally
- having converted it to character_set_results.
+ Embedded library implementation of EOF response.
+
+ @sa net_send_ok
+
+ @return
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
bool
-Protocol_local::store_string(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+Protocol_local::net_send_eof(THD *thd, uint server_status,
+ uint statement_warn_count)
{
- /* Store with conversion */
- uint error_unused;
-
- if (dst_cs && !my_charset_same(src_cs, dst_cs) &&
- src_cs != &my_charset_bin &&
- dst_cs != &my_charset_bin)
- {
- if (unlikely(convert->copy(str, length, src_cs, dst_cs, &error_unused)))
- return TRUE;
- str= convert->ptr();
- length= convert->length();
- }
- return store_column(str, length);
+ bool error= write_eof_packet_local(thd, this, server_status,
+ statement_warn_count);
+ cur_data= 0;
+ return error;
}
-/** Store a tiny int as is (1 byte) in a result set column. */
+bool Protocol_local::net_send_error_packet(THD *thd, uint sql_errno,
+ const char *err, const char *sqlstate)
+{
+ uint error;
+ char converted_err[MYSQL_ERRMSG_SIZE];
+ MYSQL_DATA *data= cur_data;
+ struct embedded_query_result *ei;
+
+// if (!thd->mysql) // bootstrap file handling
+// {
+// fprintf(stderr, "ERROR: %d %s\n", sql_errno, err);
+// return TRUE;
+// }
+ if (!data)
+ data= alloc_new_dataset();
+
+ ei= data->embedded_info;
+ ei->last_errno= sql_errno;
+ convert_error_message(converted_err, sizeof(converted_err),
+ thd->variables.character_set_results,
+ err, strlen(err),
+ system_charset_info, &error);
+ /* Converted error message is always null-terminated. */
+ strmake_buf(ei->info, converted_err);
+ strmov(ei->sqlstate, sqlstate);
+ ei->server_status= thd->server_status;
+ cur_data= 0;
+ return FALSE;
+}
+
-bool Protocol_local::store_tiny(longlong value)
+bool Protocol_local::begin_dataset()
{
- char v= (char) value;
- return store_column(&v, 1);
+ MYSQL_DATA *data= alloc_new_dataset();
+ if (!data)
+ return 1;
+ alloc= &data->alloc;
+ /* Assume rowlength < 8192 */
+ init_alloc_root(PSI_INSTRUMENT_ME, alloc, 8192, 0, MYF(0));
+ alloc->min_malloc= sizeof(MYSQL_ROWS);
+ return 0;
}
-/** Store a short as is (2 bytes, host order) in a result set column. */
-
-bool Protocol_local::store_short(longlong value)
+bool Protocol_local::begin_dataset(THD *thd, uint numfields)
{
- int16 v= (int16) value;
- return store_column(&v, 2);
+ if (begin_dataset())
+ return true;
+ MYSQL_DATA *data= cur_data;
+ data->fields= field_count= numfields;
+ if (!(data->embedded_info->fields_list=
+ (MYSQL_FIELD*)alloc_root(&data->alloc, sizeof(MYSQL_FIELD)*field_count)))
+ return true;
+ return false;
}
-/** Store a "long" as is (4 bytes, host order) in a result set column. */
-
-bool Protocol_local::store_long(longlong value)
+bool Protocol_local::write()
{
- int32 v= (int32) value;
- return store_column(&v, 4);
-}
+// if (!thd->mysql) // bootstrap file handling
+// return false;
+ *next_field= 0;
+ return false;
+}
-/** Store a "longlong" as is (8 bytes, host order) in a result set column. */
-bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
+bool Protocol_local::flush()
{
- int64 v= (int64) value;
- return store_column(&v, 8);
+ return 0;
}
-/** Store a decimal in string format in a result set column */
+bool Protocol_local::store_field_metadata(const THD * thd,
+ const Send_field &server_field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos)
+{
+ CHARSET_INFO *cs= system_charset_info;
+ CHARSET_INFO *thd_cs= thd->variables.character_set_results;
+ MYSQL_DATA *data= cur_data;
+ MEM_ROOT *field_alloc= &data->alloc;
+ MYSQL_FIELD *client_field= &cur_data->embedded_info->fields_list[pos];
+ DBUG_ASSERT(server_field.is_sane());
+
+ client_field->db= dup_str_aux(field_alloc, server_field.db_name,
+ cs, thd_cs);
+ client_field->table= dup_str_aux(field_alloc, server_field.table_name,
+ cs, thd_cs);
+ client_field->name= dup_str_aux(field_alloc, server_field.col_name,
+ cs, thd_cs);
+ client_field->org_table= dup_str_aux(field_alloc, server_field.org_table_name,
+ cs, thd_cs);
+ client_field->org_name= dup_str_aux(field_alloc, server_field.org_col_name,
+ cs, thd_cs);
+ if (charset_for_protocol == &my_charset_bin || thd_cs == NULL)
+ {
+ /* No conversion */
+ client_field->charsetnr= charset_for_protocol->number;
+ client_field->length= server_field.length;
+ }
+ else
+ {
+ /* With conversion */
+ client_field->charsetnr= thd_cs->number;
+ client_field->length= server_field.max_octet_length(charset_for_protocol,
+ thd_cs);
+ }
+ client_field->type= server_field.type_handler()->type_code_for_protocol();
+ client_field->flags= (uint16) server_field.flags;
+ client_field->decimals= server_field.decimals;
-bool Protocol_local::store_decimal(const my_decimal *value)
-{
- DBUG_ASSERT(0); // This method is not used yet
- StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
- return value->to_string(&str) ? store_column(str.ptr(), str.length()) : true;
-}
+ client_field->db_length= (unsigned int) strlen(client_field->db);
+ client_field->table_length= (unsigned int) strlen(client_field->table);
+ client_field->name_length= (unsigned int) strlen(client_field->name);
+ client_field->org_name_length= (unsigned int) strlen(client_field->org_name);
+ client_field->org_table_length= (unsigned int) strlen(client_field->org_table);
+ client_field->catalog= dup_str_aux(field_alloc, "def", 3, cs, thd_cs);
+ client_field->catalog_length= 3;
-/** Convert to cs_results and store a string. */
+ if (IS_NUM(client_field->type))
+ client_field->flags|= NUM_FLAG;
-bool Protocol_local::store(const char *str, size_t length,
- CHARSET_INFO *src_cs)
+ client_field->max_length= 0;
+ client_field->def= 0;
+ return false;
+}
+
+
+void Protocol_local::remove_last_row()
{
- CHARSET_INFO *dst_cs;
+ MYSQL_DATA *data= cur_data;
+ MYSQL_ROWS **last_row_hook= &data->data;
+ my_ulonglong count= data->rows;
+ DBUG_ENTER("Protocol_text::remove_last_row");
+ while (--count)
+ last_row_hook= &(*last_row_hook)->next;
- dst_cs= m_connection->m_thd->variables.character_set_results;
- return store_string(str, length, src_cs, dst_cs);
-}
+ *last_row_hook= 0;
+ data->embedded_info->prev_ptr= last_row_hook;
+ data->rows--;
+ DBUG_VOID_RETURN;
+}
-/** Store a string. */
-bool Protocol_local::store(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
{
- return store_string(str, length, src_cs, dst_cs);
-}
+ List_iterator_fast<Item> it(*list);
+ Item *item;
+// Protocol_local prot(thd);
+ DBUG_ENTER("send_result_set_metadata");
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_RETURN(0);
-/* Store MYSQL_TIME (in binary format) */
+ if (begin_dataset(thd, list->elements))
+ goto err;
-bool Protocol_local::store(MYSQL_TIME *time, int decimals)
-{
- if (decimals != AUTO_SEC_PART_DIGITS)
- my_datetime_trunc(time, decimals);
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ for (uint pos= 0 ; (item= it++); pos++)
+ {
+ if (/*prot.*/store_item_metadata(thd, item, pos))
+ goto err;
+ }
+ if (flags & SEND_EOF)
+ write_eof_packet_local(thd, this, thd->server_status,
+ thd->get_stmt_da()->current_statement_warn_count());
-/** Store MYSQL_TIME (in binary format) */
+ DBUG_RETURN(prepare_for_send(list->elements));
+ err:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+}
-bool Protocol_local::store_date(MYSQL_TIME *time)
+static void
+list_fields_send_default(THD *thd, Protocol_local *p, Field *fld, uint pos)
{
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ char buff[80];
+ String tmp(buff, sizeof(buff), default_charset_info), *res;
+ MYSQL_FIELD *client_field= &p->cur_data->embedded_info->fields_list[pos];
+ if (fld->is_null() || !(res= fld->val_str(&tmp)))
+ {
+ client_field->def_length= 0;
+ client_field->def= strmake_root(&p->cur_data->alloc, "", 0);
+ }
+ else
+ {
+ client_field->def_length= res->length();
+ client_field->def= strmake_root(&p->cur_data->alloc, res->ptr(),
+ client_field->def_length);
+ }
+}
-/** Store MYSQL_TIME (in binary format) */
-bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
+bool Protocol_local::send_list_fields(List<Field> *list, const TABLE_LIST *table_list)
{
- if (decimals != AUTO_SEC_PART_DIGITS)
- my_time_trunc(time, decimals);
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ DBUG_ENTER("send_result_set_metadata");
+ Protocol_text prot(thd);
+ List_iterator_fast<Field> it(*list);
+ Field *fld;
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_RETURN(0);
-/* Store a floating point number, as is. */
+ if (begin_dataset(thd, list->elements))
+ goto err;
-bool Protocol_local::store(float value, uint32 decimals, String *buffer)
-{
- return store_column(&value, sizeof(float));
+ for (uint pos= 0 ; (fld= it++); pos++)
+ {
+ if (prot.store_field_metadata_for_list_fields(thd, fld, table_list, pos))
+ goto err;
+ list_fields_send_default(thd, this, fld, pos);
+ }
+
+ DBUG_RETURN(prepare_for_send(list->elements));
+err:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(1);
}
-/* Store a double precision number, as is. */
+void Protocol_local::prepare_for_resend()
+{
+ MYSQL_ROWS *cur;
+ MYSQL_DATA *data= cur_data;
+ DBUG_ENTER("send_data");
+
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_VOID_RETURN;
+
+ data->rows++;
+ if (!(cur= (MYSQL_ROWS *)alloc_root(alloc, sizeof(MYSQL_ROWS)+(field_count + 1) * sizeof(char *))))
+ {
+ my_error(ER_OUT_OF_RESOURCES,MYF(0));
+ DBUG_VOID_RETURN;
+ }
+ cur->data= (MYSQL_ROW)(((char *)cur) + sizeof(MYSQL_ROWS));
+
+ *data->embedded_info->prev_ptr= cur;
+ data->embedded_info->prev_ptr= &cur->next;
+ next_field=cur->data;
+ next_mysql_field= data->embedded_info->fields_list;
+#ifndef DBUG_OFF
+ field_pos= 0;
+#endif
-bool Protocol_local::store(double value, uint32 decimals, String *buffer)
+ DBUG_VOID_RETURN;
+}
+
+bool Protocol_local::store_null()
{
- return store_column(&value, sizeof (double));
+ *(next_field++)= NULL;
+ ++next_mysql_field;
+ return false;
}
-/* Store a Field. */
+#include <sql_common.h>
+#include <errmsg.h>
-bool Protocol_local::store(Field *field)
+struct local_results
{
- if (field->is_null())
- return store_null();
- return field->send_binary(this);
-}
+ struct st_mysql_data *cur_data;
+ struct st_mysql_data *first_data;
+ struct st_mysql_data **data_tail;
+ void clear_data_list();
+ struct st_mysql_data *alloc_new_dataset();
+ char **next_field;
+ MYSQL_FIELD *next_mysql_field;
+ MEM_ROOT *alloc;
+};
-/** Called to start a new result set. */
+static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data)
+{
+ NET *net= &mysql->net;
+ struct embedded_query_result *ei= data->embedded_info;
+ net->last_errno= ei->last_errno;
+ strmake_buf(net->last_error, ei->info);
+ memcpy(net->sqlstate, ei->sqlstate, sizeof(net->sqlstate));
+ mysql->server_status= ei->server_status;
+ my_free(data);
+}
+
-bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
+static my_bool loc_read_query_result(MYSQL *mysql)
{
- DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
+ local_results *thd= (local_results *) mysql->thd;
- init_sql_alloc(&m_rset_root, "send_result_set_metadata",
- MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
+ MYSQL_DATA *res= thd->first_data;
+ DBUG_ASSERT(!thd->cur_data);
+ thd->first_data= res->embedded_info->next;
+ if (res->embedded_info->last_errno &&
+ !res->embedded_info->fields_list)
+ {
+ embedded_get_error(mysql, res);
+ return 1;
+ }
- if (! (m_rset= new (&m_rset_root) List<Ed_row>))
- return TRUE;
+ mysql->warning_count= res->embedded_info->warning_count;
+ mysql->server_status= res->embedded_info->server_status;
+ mysql->field_count= res->fields;
+ if (!(mysql->fields= res->embedded_info->fields_list))
+ {
+ mysql->affected_rows= res->embedded_info->affected_rows;
+ mysql->insert_id= res->embedded_info->insert_id;
+ }
+ net_clear_error(&mysql->net);
+ mysql->info= 0;
- m_column_count= columns->elements;
+ if (res->embedded_info->info[0])
+ {
+ strmake(mysql->info_buffer, res->embedded_info->info, MYSQL_ERRMSG_SIZE-1);
+ mysql->info= mysql->info_buffer;
+ }
- return FALSE;
+ if (res->embedded_info->fields_list)
+ {
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ thd->cur_data= res;
+ }
+ else
+ my_free(res);
+
+ return 0;
}
-/**
- Normally this is a separate result set with OUT parameters
- of stored procedures. Currently unsupported for the local
- version.
-*/
+static MYSQL_METHODS local_methods=
+{
+ loc_read_query_result, /* read_query_result */
+ NULL/*loc_advanced_command*/, /* advanced_command */
+ NULL/*loc_read_rows*/, /* read_rows */
+ NULL/*loc_use_result*/, /* use_result */
+ NULL/*loc_fetch_lengths*/, /* fetch_lengths */
+ NULL/*loc_flush_use_result*/, /* flush_use_result */
+ NULL/*loc_read_change_user_result*/ /* read_change_user_result */
+};
-bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
+
+extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql,
+ const char *host, const char *user, const char *passwd, const char *db)
{
- return FALSE;
-}
+ //char name_buff[USERNAME_LENGTH];
+ DBUG_ENTER("mysql_real_connect_local");
-/** Called for statements that don't have a result set, at statement end. */
+ /* Test whether we're already connected */
+ if (mysql->server_version)
+ {
+ set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate);
+ DBUG_RETURN(0);
+ }
-bool
-Protocol_local::send_ok(uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush)
-{
- /*
- Just make sure nothing is sent to the client, we have grabbed
- the status information in the connection diagnostics area.
- */
- return FALSE;
-}
+ if (!host || !host[0])
+ host= mysql->options.host;
+ mysql->methods= &local_methods;
-/**
- Called at the end of a result set. Append a complete
- result set to the list in Ed_connection.
+ if (!db || !db[0])
+ db=mysql->options.db;
- Don't send anything to the client, but instead finish
- building of the result set at hand.
-*/
+ if (!user || !user[0])
+ user=mysql->options.user;
-bool Protocol_local::send_eof(uint server_status, uint statement_warn_count)
-{
- Ed_result_set *ed_result_set;
+ mysql->user= my_strdup(PSI_INSTRUMENT_ME, user, MYF(0));
- DBUG_ASSERT(m_rset);
- opt_add_row_to_rset();
- m_current_row= 0;
+ mysql->info_buffer= (char *) my_malloc(PSI_INSTRUMENT_ME,
+ MYSQL_ERRMSG_SIZE, MYF(0));
+ //mysql->thd= create_embedded_thd(client_flag);
- ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count,
- &m_rset_root);
+ //init_embedded_mysql(mysql, client_flag);
- m_rset= NULL;
+ //if (mysql_init_character_set(mysql))
+ // goto error;
- if (! ed_result_set)
- return TRUE;
+ //if (check_embedded_connection(mysql, db))
+ // goto error;
- /* In case of successful allocation memory ownership was transferred. */
- DBUG_ASSERT(!alloc_root_inited(&m_rset_root));
+ mysql->server_status= SERVER_STATUS_AUTOCOMMIT;
- /*
- Link the created Ed_result_set instance into the list of connection
- result sets. Never fails.
- */
- m_connection->add_result_set(ed_result_set);
- return FALSE;
-}
+ //if (mysql->options.init_commands)
+ //{
+ // DYNAMIC_ARRAY *init_commands= mysql->options.init_commands;
+ // char **ptr= (char**)init_commands->buffer;
+ // char **end= ptr + init_commands->elements;
+//
+ // for (; ptr<end; ptr++)
+ // {
+ // MYSQL_RES *res;
+ // if (mysql_query(mysql,*ptr))
+ // goto error;
+ // if (mysql->fields)
+ // {
+ // if (!(res= (*mysql->methods->use_result)(mysql)))
+ // goto error;
+ // mysql_free_result(res);
+ // }
+ // }
+ //}
+ DBUG_PRINT("exit",("Mysql handler: %p", mysql));
+ DBUG_RETURN(mysql);
+
+//error:
+ DBUG_PRINT("error",("message: %u (%s)",
+ mysql->net.last_errno,
+ mysql->net.last_error));
+ {
+ /* Free alloced memory */
+ my_bool free_me=mysql->free_me;
+ free_old_query(mysql);
+ mysql->free_me=0;
+ mysql_close(mysql);
+ mysql->free_me=free_me;
+ }
+ DBUG_RETURN(0);
+}
-/** Called to send an error to the client at the end of a statement. */
-bool
-Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*)
+extern "C" int execute_sql_command(const char *command,
+ char *hosts, char *names, char *filters)
{
- /*
- Just make sure that nothing is sent to the client (default
- implementation).
- */
- return FALSE;
+ MYSQL_LEX_STRING sql_text;
+ THD *thd= current_thd;
+ THD *new_thd= 0;
+ int result;
+ my_bool qc_save= 0;
+
+ if (!thd)
+ {
+ new_thd= new THD(0);
+ new_thd->thread_stack= (char*) &sql_text;
+ new_thd->store_globals();
+ new_thd->security_ctx->skip_grants();
+ new_thd->query_cache_is_applicable= 0;
+ bzero((char*) &new_thd->net, sizeof(new_thd->net));
+ thd= new_thd;
+ }
+ else
+ {
+ if (thd->lock)
+ /* Doesn't work if the thread opened/locked tables already. */
+ return 2;
+
+ qc_save= thd->query_cache_is_applicable;
+ thd->query_cache_is_applicable= 0;
+ }
+ sql_text.str= (char *) command;
+ sql_text.length= strlen(command);
+ {
+ Protocol_local p(thd);
+ Ed_connection con(thd);
+ result= con.execute_direct(&p, sql_text);
+ if (!result && p.first_data)
+ {
+ int nr= (int) p.first_data->rows;
+ MYSQL_ROWS *rows= p.first_data->data;
+
+ while (nr--)
+ {
+ strcpy(hosts, rows->data[0]);
+ hosts+= strlen(hosts) + 1;
+ strcpy(names, rows->data[1]);
+ names+= strlen(names) + 1;
+ if (filters)
+ {
+ strcpy(filters, rows->data[2]);
+ filters+= strlen(filters) + 1;
+ }
+ rows= rows->next;
+ }
+ }
+ if (p.first_data)
+ {
+ if (p.alloc)
+ free_root(p.alloc, MYF(0));
+ my_free(p.first_data);
+ }
+ }
+
+ if (new_thd)
+ delete new_thd;
+ else
+ thd->query_cache_is_applicable= qc_save;
+
+ *hosts= 0;
+ return result;
}
+#endif /*!EMBEDDED_LIBRARY*/
+
-#ifdef EMBEDDED_LIBRARY
-void Protocol_local::remove_last_row()
-{ }
-#endif
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
index f1c4e5e4be9..166be95eb89 100644
--- a/sql/sql_prepare.h
+++ b/sql/sql_prepare.h
@@ -200,7 +200,7 @@ public:
@retval TRUE error, use get_last_error()
to see the error number.
*/
- bool execute_direct(LEX_STRING sql_text);
+ bool execute_direct(Protocol *p, LEX_STRING sql_text);
/**
Same as the previous, but takes an instance of Server_runnable
@@ -213,7 +213,7 @@ public:
return a result set
@retval TRUE failure
*/
- bool execute_direct(Server_runnable *server_runnable);
+ bool execute_direct(Protocol *p, Server_runnable *server_runnable);
/**
Get the number of affected (deleted, updated)
@@ -309,7 +309,6 @@ private:
THD *m_thd;
Ed_result_set *m_rsets;
Ed_result_set *m_current_rset;
- friend class Protocol_local;
private:
void free_old_result();
void add_result_set(Ed_result_set *ed_result_set);
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 0d1c9881c17..07f07a7150f 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -146,7 +146,7 @@
/** The following speeds up inserts to InnoDB tables by suppressing unique
key checks in some cases */
#define OPTION_RELAXED_UNIQUE_CHECKS (1ULL << 27) // THD, user, binlog
-#define SELECT_NO_UNLOCK (1ULL << 28) // SELECT, intern
+#define OPTION_IF_EXISTS (1ULL << 28) // binlog
#define OPTION_SCHEMA_TABLE (1ULL << 29) // SELECT, intern
/** Flag set if setup_tables already done */
#define OPTION_SETUP_TABLES_DONE (1ULL << 30) // intern
@@ -179,7 +179,11 @@
#define OPTION_RPL_SKIP_PARALLEL (1ULL << 38)
#define OPTION_NO_QUERY_CACHE (1ULL << 39) // SELECT, user
#define OPTION_PROCEDURE_CLAUSE (1ULL << 40) // Internal usage
-
+#define SELECT_NO_UNLOCK (1ULL << 41) // SELECT, intern
+#define SELECT_NO_UNLOCK (1ULL << 41) // SELECT, intern
+#define OPTION_BIN_TMP_LOG_OFF (1ULL << 42) // disable binlog, intern
+/* Disable commit of binlog. Used to combine many DDL's and DML's as one */
+#define OPTION_BIN_COMMIT_OFF (1ULL << 43)
#define OPTION_LEX_FOUND_COMMENT (1ULL << 0) // intern, parser
@@ -229,6 +233,7 @@
#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_SUBQUERY (1ULL << 32)
#define OPTIMIZER_SWITCH_USE_ROWID_FILTER (1ULL << 33)
#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FROM_HAVING (1ULL << 34)
+#define OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN (1ULL << 35)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
@@ -389,7 +394,8 @@ enum enum_yes_no_unknown
/* sql_yacc.cc */
#ifndef DBUG_OFF
-extern void turn_parser_debug_on();
+extern void turn_parser_debug_on_MYSQLparse();
+extern void turn_parser_debug_on_ORAparse();
#endif
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index f36805012b2..d8ecd2abee7 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -32,7 +32,7 @@
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_profile.h"
-#include "sql_show.h" // schema_table_store_record
+#include "sql_i_s.h" // schema_table_store_record
#include "sql_class.h" // THD
#ifdef _WIN32
@@ -60,30 +60,32 @@ int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables,
#endif
}
+namespace Show {
+
ST_FIELD_INFO query_profile_statistics_info[]=
{
- /* name, length, type, value, maybe_null, old_name, open_method */
- {"QUERY_ID", 20, MYSQL_TYPE_LONG, 0, false, "Query_id", SKIP_OPEN_TABLE},
- {"SEQ", 20, MYSQL_TYPE_LONG, 0, false, "Seq", SKIP_OPEN_TABLE},
- {"STATE", 30, MYSQL_TYPE_STRING, 0, false, "Status", SKIP_OPEN_TABLE},
- {"DURATION", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, false, "Duration", SKIP_OPEN_TABLE},
- {"CPU_USER", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_user", SKIP_OPEN_TABLE},
- {"CPU_SYSTEM", TIME_I_S_DECIMAL_SIZE, MYSQL_TYPE_DECIMAL, 0, true, "CPU_system", SKIP_OPEN_TABLE},
- {"CONTEXT_VOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_voluntary", SKIP_OPEN_TABLE},
- {"CONTEXT_INVOLUNTARY", 20, MYSQL_TYPE_LONG, 0, true, "Context_involuntary", SKIP_OPEN_TABLE},
- {"BLOCK_OPS_IN", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_in", SKIP_OPEN_TABLE},
- {"BLOCK_OPS_OUT", 20, MYSQL_TYPE_LONG, 0, true, "Block_ops_out", SKIP_OPEN_TABLE},
- {"MESSAGES_SENT", 20, MYSQL_TYPE_LONG, 0, true, "Messages_sent", SKIP_OPEN_TABLE},
- {"MESSAGES_RECEIVED", 20, MYSQL_TYPE_LONG, 0, true, "Messages_received", SKIP_OPEN_TABLE},
- {"PAGE_FAULTS_MAJOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_major", SKIP_OPEN_TABLE},
- {"PAGE_FAULTS_MINOR", 20, MYSQL_TYPE_LONG, 0, true, "Page_faults_minor", SKIP_OPEN_TABLE},
- {"SWAPS", 20, MYSQL_TYPE_LONG, 0, true, "Swaps", SKIP_OPEN_TABLE},
- {"SOURCE_FUNCTION", 30, MYSQL_TYPE_STRING, 0, true, "Source_function", SKIP_OPEN_TABLE},
- {"SOURCE_FILE", 20, MYSQL_TYPE_STRING, 0, true, "Source_file", SKIP_OPEN_TABLE},
- {"SOURCE_LINE", 20, MYSQL_TYPE_LONG, 0, true, "Source_line", SKIP_OPEN_TABLE},
- {NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
+ Column("QUERY_ID", SLong(20), NOT_NULL, "Query_id"),
+ Column("SEQ", SLong(20), NOT_NULL, "Seq"),
+ Column("STATE", Varchar(30), NOT_NULL, "Status"),
+ Column("DURATION", Decimal(TIME_I_S_DECIMAL_SIZE), NOT_NULL, "Duration"),
+ Column("CPU_USER", Decimal(TIME_I_S_DECIMAL_SIZE), NULLABLE, "CPU_user"),
+ Column("CPU_SYSTEM", Decimal(TIME_I_S_DECIMAL_SIZE), NULLABLE, "CPU_system"),
+ Column("CONTEXT_VOLUNTARY", SLong(20), NULLABLE, "Context_voluntary"),
+ Column("CONTEXT_INVOLUNTARY", SLong(20), NULLABLE, "Context_involuntary"),
+ Column("BLOCK_OPS_IN", SLong(20), NULLABLE, "Block_ops_in"),
+ Column("BLOCK_OPS_OUT", SLong(20), NULLABLE, "Block_ops_out"),
+ Column("MESSAGES_SENT", SLong(20), NULLABLE, "Messages_sent"),
+ Column("MESSAGES_RECEIVED", SLong(20), NULLABLE, "Messages_received"),
+ Column("PAGE_FAULTS_MAJOR", SLong(20), NULLABLE, "Page_faults_major"),
+ Column("PAGE_FAULTS_MINOR", SLong(20), NULLABLE, "Page_faults_minor"),
+ Column("SWAPS", SLong(20), NULLABLE, "Swaps"),
+ Column("SOURCE_FUNCTION", Varchar(30), NULLABLE, "Source_function"),
+ Column("SOURCE_FILE", Varchar(20), NULLABLE, "Source_file"),
+ Column("SOURCE_LINE", SLong(20), NULLABLE, "Source_line"),
+ CEnd()
};
+} // namespace Show
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
@@ -113,21 +115,17 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table)
Name_resolution_context *context= &thd->lex->first_select_lex()->context;
int i;
- for (i= 0; schema_table->fields_info[i].field_name != NULL; i++)
+ for (i= 0; !schema_table->fields_info[i].end_marker(); i++)
{
if (! fields_include_condition_truth_values[i])
continue;
field_info= &schema_table->fields_info[i];
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name) };
Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, &field_name);
+ field_info->name());
if (field)
{
- field->set_name(thd, field_info->old_name,
- (uint) strlen(field_info->old_name),
- system_charset_info);
+ field->set_name(thd, field_info->old_name());
if (add_item_to_list(thd, field))
return 1;
}
@@ -202,7 +200,8 @@ void PROF_MEASUREMENT::set_label(const char *status_arg,
sizes[1]= (function_arg == NULL) ? 0 : strlen(function_arg) + 1;
sizes[2]= (file_arg == NULL) ? 0 : strlen(file_arg) + 1;
- allocated_status_memory= (char *) my_malloc(sizes[0] + sizes[1] + sizes[2], MYF(0));
+ allocated_status_memory= (char *) my_malloc(key_memory_PROFILE, sizes[0] +
+ sizes[1] + sizes[2], MYF(0));
DBUG_ASSERT(allocated_status_memory != NULL);
cursor= allocated_status_memory;
@@ -291,7 +290,7 @@ void QUERY_PROFILE::set_query_source(char *query_source_arg, size_t query_length
DBUG_ASSERT(query_source == NULL); /* we don't leak memory */
if (query_source_arg != NULL)
- query_source= my_strndup(query_source_arg, length, MYF(0));
+ query_source= my_strndup(key_memory_PROFILE, query_source_arg, length, MYF(0));
}
void QUERY_PROFILE::new_status(const char *status_arg,
@@ -404,7 +403,7 @@ bool PROFILING::show_profiles()
MEM_ROOT *mem_root= thd->mem_root;
SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows idx= 0;
+ ha_rows idx;
Protocol *protocol= thd->protocol;
void *iterator;
DBUG_ENTER("PROFILING::show_profiles");
@@ -428,25 +427,23 @@ bool PROFILING::show_profiles()
unit->set_limit(sel);
- for (iterator= history.new_iterator();
+ for (iterator= history.new_iterator(), idx= 1;
iterator != NULL;
- iterator= history.iterator_next(iterator))
+ iterator= history.iterator_next(iterator), idx++)
{
prof= history.iterator_value(iterator);
- String elapsed;
-
double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
- if (++idx <= unit->offset_limit_cnt)
+ if (unit->lim.check_offset(idx))
continue;
- if (idx > unit->select_limit_cnt)
+ if (idx > unit->lim.get_select_limit())
break;
protocol->prepare_for_resend();
protocol->store((uint32)(prof->profiling_query_id));
- protocol->store((double)(query_time_usecs/(1000.0*1000)),
- (uint32) TIME_FLOAT_DIGITS-1, &elapsed);
+ protocol->store_double(query_time_usecs/(1000.0*1000),
+ (uint32) TIME_FLOAT_DIGITS-1);
if (prof->query_source != NULL)
protocol->store(prof->query_source, strlen(prof->query_source),
system_charset_info);
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
index 5b03acf59c0..85018a2598b 100644
--- a/sql/sql_profile.h
+++ b/sql/sql_profile.h
@@ -19,10 +19,13 @@
class Item;
struct TABLE_LIST;
class THD;
-typedef struct st_field_info ST_FIELD_INFO;
+class ST_FIELD_INFO;
typedef struct st_schema_table ST_SCHEMA_TABLE;
+namespace Show {
extern ST_FIELD_INFO query_profile_statistics_info[];
+} // namespace Show
+
int fill_query_profile_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond);
int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table);
@@ -51,6 +54,7 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table);
#include <sys/resource.h>
#endif
+extern PSI_memory_key key_memory_queue_item;
class PROF_MEASUREMENT;
class QUERY_PROFILE;
@@ -97,7 +101,8 @@ public:
{
struct queue_item *new_item;
- new_item= (struct queue_item *) my_malloc(sizeof(struct queue_item), MYF(0));
+ new_item= (struct queue_item *) my_malloc(key_memory_queue_item,
+ sizeof(struct queue_item), MYF(0));
new_item->payload= payload;
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index 5b4600ece9a..8f87d633d19 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -22,6 +22,7 @@
#include "sql_acl.h" // acl_reload
#include "sql_servers.h" // servers_reload
#include "sql_connect.h" // reset_mqh
+#include "thread_cache.h"
#include "sql_base.h" // close_cached_tables
#include "sql_db.h" // my_dbopt_cleanup
#include "hostname.h" // hostname_cache_refresh
@@ -133,7 +134,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
logger.flush_general_log();
if (options & REFRESH_ENGINE_LOG)
- if (ha_flush_logs(NULL))
+ if (ha_flush_logs())
result= 1;
if (options & REFRESH_BINARY_LOG)
@@ -352,7 +353,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
if (thd && (options & REFRESH_STATUS))
refresh_status(thd);
if (options & REFRESH_THREADS)
- flush_thread_cache();
+ thread_cache.flush();
#ifdef HAVE_REPLICATION
if (options & REFRESH_MASTER)
{
@@ -515,7 +516,6 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
{
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
- TABLE_LIST *table_list;
/*
This is called from SQLCOM_FLUSH, the transaction has
@@ -555,16 +555,10 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks");
- for (table_list= all_tables; table_list;
+ /* Reset ticket to satisfy asserts in open_tables(). */
+ for (auto 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.str,
- table_list->table_name.str, FALSE);
- /* Reset ticket to satisfy asserts in open_tables(). */
table_list->mdl_request.ticket= NULL;
- }
}
thd->variables.option_bits|= OPTION_TABLE_LOCK;
@@ -599,6 +593,16 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
}
}
+ if (thd->lex->type & REFRESH_READ_LOCK)
+ {
+ for (auto table_list= all_tables; table_list;
+ table_list= table_list->next_global)
+ {
+ if (table_list->table->file->extra(HA_EXTRA_FLUSH))
+ goto error_reset_bits;
+ }
+ }
+
if (thd->locked_tables_list.init_locked_tables(thd))
goto error_reset_bits;
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index ada373546be..77a1e46a75a 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -32,10 +32,13 @@
#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, const LEX_CSTRING *new_db,
- const LEX_CSTRING *new_table_name, const LEX_CSTRING *new_table_alias,
- bool skip_error);
+ bool skip_error, bool if_exits,
+ bool *force_if_exists);
+static bool do_rename(THD *thd, TABLE_LIST *ren_table,
+ const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_table_name,
+ const LEX_CSTRING *new_table_alias,
+ bool skip_error, bool if_exists, bool *force_if_exists);
static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
@@ -44,10 +47,11 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
the new name.
*/
-bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent,
+ bool if_exists)
{
bool error= 1;
- bool binlog_error= 0;
+ bool binlog_error= 0, force_if_exists;
TABLE_LIST *ren_table= 0;
int to_table;
const char *rename_log_table[2]= {NULL, NULL};
@@ -151,7 +155,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
An exclusive lock on table names is satisfactory to ensure
no other thread accesses this table.
*/
- if ((ren_table=rename_tables(thd,table_list,0)))
+ if ((ren_table= rename_tables(thd, table_list, 0, if_exists,
+ &force_if_exists)))
{
/* Rename didn't succeed; rename back the tables in reverse order */
TABLE_LIST *table;
@@ -165,7 +170,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
table= table->next_local->next_local) ;
table= table->next_local->next_local; // Skip error table
/* Revert to old names */
- rename_tables(thd, table, 1);
+ rename_tables(thd, table, 1, if_exists, &force_if_exists);
/* Revert the table list (for prepared statements) */
table_list= reverse_table_list(table_list);
@@ -175,7 +180,15 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
if (likely(!silent && !error))
{
+ ulonglong save_option_bits= thd->variables.option_bits;
+ if (force_if_exists && ! if_exists)
+ {
+ /* Add IF EXISTS to binary log */
+ thd->variables.option_bits|= OPTION_IF_EXISTS;
+ }
binlog_error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+ thd->variables.option_bits= save_option_bits;
+
if (likely(!binlog_error))
my_ok(thd);
}
@@ -246,6 +259,9 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
new_table_name The new table/view name
new_table_alias The new table/view alias
skip_error Whether to skip error
+ if_exists Skip error, but only if the table didn't exists
+ force_if_exists Set to 1 if we have to log the query with 'IF EXISTS'
+ Otherwise don't touch the value
DESCRIPTION
Rename a single table or a view.
@@ -257,13 +273,16 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table,
static bool
do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db,
- const LEX_CSTRING *new_table_name, const LEX_CSTRING *new_table_alias,
- bool skip_error)
+ const LEX_CSTRING *new_table_name,
+ const LEX_CSTRING *new_table_alias,
+ bool skip_error, bool if_exists, bool *force_if_exists)
{
int rc= 1;
- handlerton *hton;
+ handlerton *hton, *new_hton;
LEX_CSTRING old_alias, new_alias;
DBUG_ENTER("do_rename");
+ DBUG_PRINT("enter", ("skip_error: %d if_exists: %d", (int) skip_error,
+ (int) if_exists));
if (lower_case_table_names == 2)
{
@@ -277,65 +296,86 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db,
}
DBUG_ASSERT(new_alias.str);
- if (ha_table_exists(thd, new_db, &new_alias))
+ if (!ha_table_exists(thd, &ren_table->db, &old_alias, &hton) || !hton)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF((skip_error | if_exists) ? ME_NOTE : 0),
+ ren_table->db.str, old_alias.str);
+ DBUG_RETURN(skip_error || if_exists ? 0 : 1);
+ }
+
+ if (hton != view_pseudo_hton &&
+ ha_check_if_updates_are_ignored(thd, hton, "RENAME"))
+ {
+ /*
+ Shared table. Just drop the old .frm as it's not correct anymore
+ Discovery will find the old table when it's accessed
+ */
+ tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str);
+ quick_rm_table(thd, 0, &ren_table->db, &old_alias, FRM_ONLY, 0);
+ DBUG_RETURN(0);
+ }
+
+ if (ha_table_exists(thd, new_db, &new_alias, &new_hton))
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias.str);
DBUG_RETURN(1); // This can't be skipped
}
- if (ha_table_exists(thd, &ren_table->db, &old_alias, &hton) && hton)
+ DBUG_ASSERT(!thd->locked_tables_mode);
+
+#ifdef WITH_WSREP
+ if (WSREP(thd) && hton && hton != view_pseudo_hton &&
+ !wsrep_should_replicate_ddl(thd, hton->db_type))
+ DBUG_RETURN(1);
+#endif
+
+ tdc_remove_table(thd, ren_table->db.str, ren_table->table_name.str);
+
+ if (hton != view_pseudo_hton)
{
- DBUG_ASSERT(!thd->locked_tables_mode);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- ren_table->db.str, ren_table->table_name.str, false);
+ if (hton->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
+ *force_if_exists= 1;
- if (hton != view_pseudo_hton)
+ thd->replication_flags= 0;
+ if (!(rc= mysql_rename_table(hton, &ren_table->db, &old_alias,
+ new_db, &new_alias, 0)))
{
- if (!(rc= mysql_rename_table(hton, &ren_table->db, &old_alias,
- new_db, &new_alias, 0)))
+ (void) rename_table_in_stat_tables(thd, &ren_table->db,
+ &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)))
{
- (void) rename_table_in_stat_tables(thd, &ren_table->db,
- &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(hton, 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);
}
}
- else
- {
- /*
- change of schema is not allowed
- except of ALTER ...UPGRADE DATA DIRECTORY NAME command
- because a view has valid internal db&table names in this case.
- */
- if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
- cmp(&ren_table->db, new_db))
- my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db.str, new_db->str);
- else
- rc= mysql_rename_view(thd, new_db, &new_alias, ren_table);
- }
+ if (thd->replication_flags & OPTION_IF_EXISTS)
+ *force_if_exists= 1;
}
else
{
- my_error(ER_NO_SUCH_TABLE, MYF(0), ren_table->db.str, old_alias.str);
+ /*
+ Change of schema is not allowed
+ except of ALTER ...UPGRADE DATA DIRECTORY NAME command
+ because a view has valid internal db&table names in this case.
+ */
+ if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
+ cmp(&ren_table->db, new_db))
+ my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db.str, new_db->str);
+ else
+ rc= mysql_rename_view(thd, new_db, &new_alias, ren_table);
}
- if (unlikely(rc && !skip_error))
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
+ DBUG_RETURN(rc && !skip_error ? 1 : 0);
}
@@ -352,6 +392,9 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db,
thd Thread handle
table_list List of tables to rename
skip_error Whether to skip errors
+ if_exists Don't give an error if table doesn't exists
+ force_if_exists Set to 1 if we have to log the query with 'IF EXISTS'
+ Otherwise set it to 0
DESCRIPTION
Take a table/view name from and odd list element and rename it to a
@@ -359,17 +402,19 @@ do_rename(THD *thd, TABLE_LIST *ren_table, const LEX_CSTRING *new_db,
empty.
RETURN
- false Ok
- true rename failed
+ 0 Ok
+ table pointer to the table list element which rename failed
*/
static TABLE_LIST *
-rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
+rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error,
+ bool if_exists, bool *force_if_exists)
{
TABLE_LIST *ren_table, *new_table;
-
DBUG_ENTER("rename_tables");
+ *force_if_exists= 0;
+
for (ren_table= table_list; ren_table; ren_table= new_table->next_local)
{
new_table= ren_table->next_local;
@@ -377,7 +422,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
if (is_temporary_table(ren_table) ?
do_rename_temporary(thd, ren_table, new_table, skip_error) :
do_rename(thd, ren_table, &new_table->db, &new_table->table_name,
- &new_table->alias, skip_error))
+ &new_table->alias, skip_error, if_exists, force_if_exists))
DBUG_RETURN(ren_table);
}
DBUG_RETURN(0);
diff --git a/sql/sql_rename.h b/sql/sql_rename.h
index 1b9fcfb12bc..1f5f94b0f5c 100644
--- a/sql/sql_rename.h
+++ b/sql/sql_rename.h
@@ -19,6 +19,7 @@
class THD;
struct TABLE_LIST;
-bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
+bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent,
+ bool if_exists);
#endif /* SQL_RENAME_INCLUDED */
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 5203e0f52a5..c7efcfd1074 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -24,7 +24,6 @@
#include "rpl_mi.h"
#include "rpl_rli.h"
#include "sql_repl.h"
-#include "sql_acl.h" // SUPER_ACL
#include "log_event.h"
#include "rpl_filter.h"
#include <my_dir.h>
@@ -1225,7 +1224,8 @@ check_slave_start_position(binlog_send_info *info, const char **errormsg,
if (!delete_list)
{
if (!(delete_list= (slave_connection_state::entry **)
- my_malloc(sizeof(*delete_list) * st->hash.records, MYF(MY_WME))))
+ my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(*delete_list) * st->hash.records, MYF(MY_WME))))
{
*errormsg= "Out of memory while checking slave start position";
err= ER_OUT_OF_RESOURCES;
@@ -1287,8 +1287,9 @@ gtid_find_binlog_file(slave_connection_state *state, char *out_name,
const char *errormsg= NULL;
char buf[FN_REFLEN];
- init_alloc_root(&memroot, "gtid_find_binlog_file",
- 8192, 0, MYF(MY_THREAD_SPECIFIC));
+ init_alloc_root(PSI_INSTRUMENT_ME, &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";
@@ -1655,7 +1656,7 @@ is_until_reached(binlog_send_info *info, ulong *ev_offset,
return false;
break;
case GTID_UNTIL_STOP_AFTER_TRANSACTION:
- if (event_type != XID_EVENT &&
+ if (event_type != XID_EVENT && event_type != XA_PREPARE_LOG_EVENT &&
(event_type != QUERY_EVENT || /* QUERY_COMPRESSED_EVENT would never be commmit or rollback */
!Query_log_event::peek_is_commit_rollback
(info->packet->ptr()+*ev_offset,
@@ -1890,7 +1891,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
info->gtid_skip_group= GTID_SKIP_NOT;
return NULL;
case GTID_SKIP_TRANSACTION:
- if (event_type == XID_EVENT ||
+ if (event_type == XID_EVENT || event_type == XA_PREPARE_LOG_EVENT ||
(event_type == QUERY_EVENT && /* QUERY_COMPRESSED_EVENT would never be commmit or rollback */
Query_log_event::peek_is_commit_rollback(packet->ptr() + ev_offset,
len - ev_offset,
@@ -3088,7 +3089,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("start_slave");
- if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
+ if (check_global_access(thd, PRIV_STMT_START_SLAVE))
DBUG_RETURN(-1);
create_logfile_name_with_suffix(master_info_file_tmp,
@@ -3291,7 +3292,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
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))
+ if (check_global_access(thd, PRIV_STMT_STOP_SLAVE))
DBUG_RETURN(-1);
THD_STAGE_INFO(thd, stage_killing_slave);
int thread_mask;
@@ -3515,7 +3516,7 @@ static bool get_string_parameter(char *to, const char *from, size_t length,
if (from) // Empty paramaters allowed
{
size_t from_length= strlen(from);
- size_t from_numchars= cs->cset->numchars(cs, from, from + from_length);
+ size_t from_numchars= cs->numchars(from, from + from_length);
if (from_numchars > length / cs->mbmaxlen)
{
my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name,
@@ -4034,7 +4035,7 @@ bool mysql_show_binlog_events(THD* thd)
if (binary_log->is_open())
{
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows event_count, limit_start, limit_end;
+ ha_rows event_count;
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;
@@ -4049,8 +4050,6 @@ bool mysql_show_binlog_events(THD* thd)
}
unit->set_limit(thd->lex->current_select);
- limit_start= unit->offset_limit_cnt;
- limit_end= unit->select_limit_cnt;
name= search_file_name;
if (log_file_name)
@@ -4177,13 +4176,13 @@ bool mysql_show_binlog_events(THD* thd)
(opt_master_verify_checksum ||
verify_checksum_once))); )
{
- if (event_count >= limit_start &&
- ev->net_send(protocol, linfo.log_file_name, pos))
+ if (!unit->lim.check_offset(event_count) &&
+ ev->net_send(protocol, linfo.log_file_name, pos))
{
- errmsg = "Net error";
- delete ev;
+ errmsg = "Net error";
+ delete ev;
mysql_mutex_unlock(log_lock);
- goto err;
+ goto err;
}
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
@@ -4212,11 +4211,11 @@ bool mysql_show_binlog_events(THD* thd)
verify_checksum_once= false;
pos = my_b_tell(&log);
- if (++event_count >= limit_end)
- break;
+ if (++event_count >= unit->lim.get_select_limit())
+ break;
}
- if (unlikely(event_count < limit_end && log.error))
+ if (unlikely(event_count < unit->lim.get_select_limit() && log.error))
{
errmsg = "Wrong offset or I/O error";
mysql_mutex_unlock(log_lock);
@@ -4273,7 +4272,7 @@ void show_binlog_info_get_fields(THD *thd, List<Item> *field_list)
/**
- Execute a SHOW MASTER STATUS statement.
+ Execute a SHOW BINLOG STATUS statement.
@param thd Pointer to THD object for the client thread executing the
statement.
@@ -4365,8 +4364,7 @@ bool show_binlogs(THD* thd)
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- init_alloc_root(&mem_root, "binlog_file_list", 8192, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_alloc_root(PSI_INSTRUMENT_ME, &mem_root, 8192, 0, MYF(MY_THREAD_SPECIFIC));
retry:
/*
The current mutex handling here is to ensure we get the current log position
diff --git a/sql/sql_schema.h b/sql/sql_schema.h
index 7c8f284d526..886a115cbc5 100644
--- a/sql/sql_schema.h
+++ b/sql/sql_schema.h
@@ -47,18 +47,8 @@ public:
*/
bool eq_name(const LEX_CSTRING &name) const
{
-#if MYSQL_VERSION_ID > 100500
-#error Remove the old code
return !table_alias_charset->strnncoll(m_name.str, m_name.length,
name.str, name.length);
-#else
- // Please remove this when merging to 10.5
- return !table_alias_charset->coll->strnncoll(table_alias_charset,
- (const uchar *) m_name.str,
- m_name.length,
- (const uchar *) name.str,
- name.length, FALSE);
-#endif
}
static Schema *find_by_name(const LEX_CSTRING &name);
static Schema *find_implied(THD *thd);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 8c27b023c93..2a856a0d51f 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -43,7 +43,6 @@
#include "sql_base.h" // setup_wild, setup_fields, fill_record
#include "sql_parse.h" // check_stack_overrun
#include "sql_partition.h" // make_used_partitions_str
-#include "sql_acl.h" // *_ACL
#include "sql_test.h" // print_where, print_keyuse_array,
// print_sjm, print_plan, TEST_join
#include "records.h" // init_read_record, end_read_record
@@ -233,7 +232,7 @@ static bool test_if_cheaper_ordering(const JOIN_TAB *tab,
uint *saved_best_key_parts= NULL);
static int test_if_order_by_key(JOIN *join,
ORDER *order, TABLE *table, uint idx,
- uint *used_key_parts= NULL);
+ uint *used_key_parts);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes,
const key_map *map);
@@ -300,6 +299,14 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab);
static Item **get_sargable_cond(JOIN *join, TABLE *table);
+static
+bool build_notnull_conds_for_range_scans(JOIN *join, COND *cond,
+ table_map allowed);
+static
+void build_notnull_conds_for_inner_nest_of_outer_join(JOIN *join,
+ TABLE_LIST *nest_tbl);
+
+
#ifndef DBUG_OFF
/*
@@ -409,7 +416,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
*/
res= mysql_select(thd,
select_lex->table_list.first,
- select_lex->with_wild, select_lex->item_list,
+ select_lex->item_list,
select_lex->where,
select_lex->order_list.elements +
select_lex->group_list.elements,
@@ -421,7 +428,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
setup_tables_done_option,
result, unit, select_lex);
}
- DBUG_PRINT("info",("res: %d report_error: %d", res,
+ DBUG_PRINT("info",("res: %d is_error(): %d", res,
thd->is_error()));
res|= thd->is_error();
if (unlikely(res))
@@ -566,9 +573,9 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
new_ref= direct_ref ?
new (thd->mem_root) Item_direct_ref(thd, ref->context, item_ref, ref->table_name,
- &ref->field_name, ref->alias_name_used) :
+ ref->field_name, ref->alias_name_used) :
new (thd->mem_root) Item_ref(thd, ref->context, item_ref, ref->table_name,
- &ref->field_name, ref->alias_name_used);
+ ref->field_name, ref->alias_name_used);
if (!new_ref)
return TRUE;
ref->outer_ref= new_ref;
@@ -777,11 +784,11 @@ Item* period_get_condition(THD *thd, TABLE_LIST *table, SELECT_LEX *select,
const LEX_CSTRING &fend= period->end_field(share)->field_name;
conds->field_start= newx Item_field(thd, &select->context,
- table->db.str, table->alias.str,
- thd->make_clex_string(fstart));
+ table->db, table->alias,
+ thd->strmake_lex_cstring(fstart));
conds->field_end= newx Item_field(thd, &select->context,
- table->db.str, table->alias.str,
- thd->make_clex_string(fend));
+ table->db, table->alias,
+ thd->strmake_lex_cstring(fend));
Item *cond1= NULL, *cond2= NULL, *cond3= NULL, *curr= NULL;
if (timestamp)
@@ -1060,7 +1067,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
storing vers_conditions as Item and make some magic related to
vers_system_time_t/VERS_TRX_ID at stage of fix_fields()
(this is large refactoring). */
- if (vers_conditions.resolve_units(thd))
+ if (vers_conditions.check_units(thd))
DBUG_RETURN(-1);
if (timestamps_only && (vers_conditions.start.unit == VERS_TRX_ID ||
vers_conditions.end.unit == VERS_TRX_ID))
@@ -1115,8 +1122,7 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
0 on success
*/
int
-JOIN::prepare(TABLE_LIST *tables_init,
- uint wild_num, COND *conds_init, uint og_num,
+JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
ORDER *order_init, bool skip_order_by,
ORDER *group_init, Item *having_init,
ORDER *proc_param_init, SELECT_LEX *select_lex_arg,
@@ -1240,8 +1246,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
real_og_num+= select_lex->order_list.elements;
DBUG_ASSERT(select_lex->hidden_bit_fields == 0);
- if (setup_wild(thd, tables_list, fields_list, &all_fields, wild_num,
- &select_lex->hidden_bit_fields))
+ if (setup_wild(thd, tables_list, fields_list, &all_fields, select_lex))
DBUG_RETURN(-1);
if (select_lex->setup_ref_array(thd, real_og_num))
DBUG_RETURN(-1);
@@ -1429,7 +1434,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list &&
!(select_lex->master_unit()->item &&
select_lex->master_unit()->item->is_in_predicate() &&
- ((Item_in_subselect*)select_lex->master_unit()->item)->
+ select_lex->master_unit()->item->get_IN_subquery()->
test_set_strategy(SUBS_MAXMIN_INJECTED)) &&
select_lex->non_agg_field_used() &&
select_lex->agg_func_used())
@@ -1601,10 +1606,13 @@ int JOIN::optimize()
join_optimization_state init_state= optimization_state;
if (select_lex->pushdown_select)
{
+ // Do same as JOIN::optimize_inner does:
+ fields= &select_lex->item_list;
+
if (!(select_options & SELECT_DESCRIBE))
{
/* Prepare to execute the query pushed into a foreign engine */
- res= select_lex->pushdown_select->init();
+ res= select_lex->pushdown_select->prepare();
}
with_two_phase_optimization= false;
}
@@ -1762,36 +1770,6 @@ JOIN::init_range_rowid_filters()
}
-int JOIN::init_join_caches()
-{
- JOIN_TAB *tab;
-
- for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
- tab;
- tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
- {
- TABLE *table= tab->table;
- if (table->file->keyread_enabled())
- {
- if (!(table->file->index_flags(table->file->keyread, 0, 1) & HA_CLUSTERED_INDEX))
- table->mark_columns_used_by_index(table->file->keyread, table->read_set);
- }
- else if ((tab->read_first_record == join_read_first ||
- tab->read_first_record == join_read_last) &&
- !tab->filesort && table->covering_keys.is_set(tab->index) &&
- !table->no_keyread)
- {
- table->prepare_for_keyread(tab->index, table->read_set);
- }
- if (tab->cache && tab->cache->init(select_options & SELECT_DESCRIBE))
- revise_cache_usage(tab);
- else
- tab->remove_redundant_bnl_scan_conds();
- }
- return 0;
-}
-
-
/**
global select optimisation.
@@ -1809,7 +1787,7 @@ JOIN::optimize_inner()
{
DBUG_ENTER("JOIN::optimize");
subq_exit_fl= false;
- do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
+ do_send_rows = (unit->lim.get_select_limit()) ? 1 : 0;
DEBUG_SYNC(thd, "before_join_optimize");
@@ -1884,9 +1862,9 @@ JOIN::optimize_inner()
DBUG_RETURN(-1);
row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
- unit->select_limit_cnt);
+ unit->lim.get_select_limit());
/* select_limit is used to decide if we are likely to scan the whole table */
- select_limit= unit->select_limit_cnt;
+ select_limit= unit->lim.get_select_limit();
if (having || (select_options & OPTION_FOUND_ROWS))
select_limit= HA_POS_ERROR;
#ifdef HAVE_REF_TO_FIELDS // Not done yet
@@ -2127,9 +2105,10 @@ JOIN::optimize_inner()
thd->change_item_tree(&sel->having, having);
}
if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
- (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
+ (!unit->lim.get_select_limit() &&
+ !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
- if (unit->select_limit_cnt)
+ if (unit->lim.get_select_limit())
{
DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE"));
@@ -2371,10 +2350,11 @@ int JOIN::optimize_stage2()
{
/*
Unlock all tables, except sequences, as accessing these may still
- require table updates
+ require table updates. It's safe to ignore result code as all
+ tables where opened for read only.
*/
- mysql_unlock_some_tables(thd, table, const_tables,
- GET_LOCK_SKIP_SEQUENCES);
+ (void) mysql_unlock_some_tables(thd, table, const_tables,
+ GET_LOCK_SKIP_SEQUENCES);
}
if (!conds && outer_join)
{
@@ -3012,8 +2992,7 @@ int JOIN::optimize_stage2()
if (make_aggr_tables_info())
DBUG_RETURN(1);
- if (init_join_caches())
- DBUG_RETURN(1);
+ init_join_cache_and_keyread();
if (init_range_rowid_filters())
DBUG_RETURN(1);
@@ -3233,7 +3212,8 @@ bool JOIN::make_aggr_tables_info()
{
/* Check if the storage engine can intercept the query */
Query query= {&all_fields, select_distinct, tables_list, conds,
- group_list, order ? order : group_list, having};
+ group_list, order ? order : group_list, having,
+ &select_lex->master_unit()->lim};
group_by_handler *gbh= ht->create_group_by(thd, &query);
if (gbh)
@@ -3688,8 +3668,8 @@ bool JOIN::make_aggr_tables_info()
unit->select_limit_cnt == 1 (we only need one row in the result set)
*/
sort_tab->filesort->limit=
- (has_group_by || (join_tab + table_count > curr_tab + 1)) ?
- select_limit : unit->select_limit_cnt;
+ (has_group_by || (join_tab + top_join_tab_count > curr_tab + 1)) ?
+ select_limit : unit->lim.get_select_limit();
}
if (!only_const_tables() &&
!join_tab[const_tables].filesort &&
@@ -4074,9 +4054,6 @@ JOIN::reinit()
{
DBUG_ENTER("JOIN::reinit");
- unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
- select_lex->offset_limit->val_uint() : 0);
-
first_record= false;
group_sent= false;
cleaned= false;
@@ -4198,9 +4175,8 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
If there is SELECT in this statement with the same number it must be the
same SELECT
*/
- DBUG_SLOW_ASSERT(select_lex->select_number == UINT_MAX ||
- select_lex->select_number == INT_MAX ||
- !output ||
+ DBUG_ASSERT(select_lex->select_number == UINT_MAX ||
+ select_lex->select_number == INT_MAX || !output ||
!output->get_select(select_lex->select_number) ||
output->get_select(select_lex->select_number)->select_lex ==
select_lex);
@@ -4260,9 +4236,9 @@ void JOIN::exec()
select_lex->select_number))
dbug_serve_apcs(thd, 1);
);
- ANALYZE_START_TRACKING(&explain->time_tracker);
+ ANALYZE_START_TRACKING(thd, &explain->time_tracker);
exec_inner();
- ANALYZE_STOP_TRACKING(&explain->time_tracker);
+ ANALYZE_STOP_TRACKING(thd, &explain->time_tracker);
DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
if (dbug_user_var_equals_int(thd,
@@ -4347,7 +4323,8 @@ void JOIN::exec_inner()
{
if (do_send_rows &&
(procedure ? (procedure->send_row(procedure_fields_list) ||
- procedure->end_of_records()) : result->send_data(fields_list)> 0))
+ procedure->end_of_records()):
+ result->send_data_with_check(fields_list, unit, 0)> 0))
error= 1;
else
send_records= ((select_options & OPTION_FOUND_ROWS) ? 1 :
@@ -4563,11 +4540,6 @@ void JOIN::cleanup_item_list(List<Item> &items) const
the top-level select_lex for this query
@param tables list of all tables used in this query.
The tables have been pre-opened.
- @param wild_num number of wildcards used in the top level
- select of this query.
- For example statement
- SELECT *, t1.*, catalog.t2.* FROM t0, t1, t2;
- has 3 wildcards.
@param fields list of items in SELECT list of the top-level
select
e.g. SELECT a, b, c FROM t1 will have Item_field
@@ -4600,12 +4572,10 @@ void JOIN::cleanup_item_list(List<Item> &items) const
*/
bool
-mysql_select(THD *thd,
- TABLE_LIST *tables, uint wild_num, List<Item> &fields,
- COND *conds, uint og_num, ORDER *order, ORDER *group,
- Item *having, ORDER *proc_param, ulonglong select_options,
- select_result *result, SELECT_LEX_UNIT *unit,
- SELECT_LEX *select_lex)
+mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
+ uint og_num, ORDER *order, ORDER *group, Item *having,
+ ORDER *proc_param, ulonglong select_options, select_result *result,
+ SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
{
int err= 0;
bool free_join= 1;
@@ -4635,9 +4605,8 @@ mysql_select(THD *thd,
}
else
{
- if ((err= join->prepare( tables, wild_num,
- conds, og_num, order, false, group, having,
- proc_param, select_lex, unit)))
+ if ((err= join->prepare(tables, conds, og_num, order, false, group,
+ having, proc_param, select_lex, unit)))
{
goto err;
}
@@ -4659,28 +4628,15 @@ mysql_select(THD *thd,
DBUG_RETURN(TRUE);
THD_STAGE_INFO(thd, stage_init);
thd->lex->used_tables=0;
- if ((err= join->prepare(tables, wild_num,
- conds, og_num, order, false, group, having, proc_param,
- select_lex, unit)))
+ if ((err= join->prepare(tables, conds, og_num, order, false, group, having,
+ proc_param, select_lex, unit)))
{
goto err;
}
}
/* Look for a table owned by an engine with the select_handler interface */
- select_lex->select_h= select_lex->find_select_handler(thd);
- if (select_lex->select_h)
- {
- /* Create a Pushdown_select object for later execution of the query */
- if (!(select_lex->pushdown_select=
- new (thd->mem_root) Pushdown_select(select_lex,
- select_lex->select_h)))
- {
- delete select_lex->select_h;
- select_lex->select_h= NULL;
- DBUG_RETURN(TRUE);
- }
- }
+ select_lex->pushdown_select= select_lex->find_select_handler(thd);
if ((err= join->optimize()))
{
@@ -4787,6 +4743,10 @@ void mark_join_nest_as_const(JOIN *join,
{
List_iterator<TABLE_LIST> it(join_nest->nested_join->join_list);
TABLE_LIST *tbl;
+ Json_writer_object emb_obj(join->thd);
+ Json_writer_object trace_obj(join->thd, "mark_join_nest_as_const");
+ Json_writer_array trace_array(join->thd, "members");
+
while ((tbl= it++))
{
if (tbl->nested_join)
@@ -4806,6 +4766,8 @@ void mark_join_nest_as_const(JOIN *join,
*found_const_table_map|= tab->table->map;
set_position(join,(*const_count)++,tab,(KEYUSE*) 0);
mark_as_null_row(tab->table); // All fields are NULL
+
+ trace_array.add_table_name(tab->table);
}
}
}
@@ -4935,7 +4897,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
table->file->print_error(error, MYF(0));
goto error;
}
- table->quick_keys.clear_all();
+ table->opt_range_keys.clear_all();
table->intersect_keys.clear_all();
table->reginfo.join_tab=s;
table->reginfo.not_exists_optimize=0;
@@ -4947,7 +4909,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->dependent= tables->dep_tables;
if (tables->schema_table)
table->file->stats.records= table->used_stat_records= 2;
- table->quick_condition_rows= table->stat_records();
+ table->opt_range_condition_rows= table->stat_records();
s->on_expr_ref= &tables->on_expr;
if (*s->on_expr_ref)
@@ -5084,7 +5046,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
*/
bool skip_unprefixed_keyparts=
!(join->is_in_subquery() &&
- ((Item_in_subselect*)join->unit->item)->test_strategy(SUBS_IN_TO_EXISTS));
+ join->unit->item->get_IN_subquery()->test_strategy(SUBS_IN_TO_EXISTS));
if (keyuse_array->elements &&
sort_and_filter_keyuse(thd, keyuse_array,
@@ -5375,6 +5337,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
}
+ join->join_tab= stat;
+ join->make_notnull_conds_for_range_scans();
+
/* Calc how many (possible) matched records in each table */
/*
@@ -5411,7 +5376,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
get_delayed_table_estimates(s->table, &s->records, &s->read_time,
&s->startup_cost);
s->found_records= s->records;
- table->quick_condition_rows=s->records;
+ table->opt_range_condition_rows=s->records;
}
else
s->scan_time();
@@ -5608,7 +5573,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (double rr= join->best_positions[i].records_read)
records= COST_MULT(records, rr);
ha_rows rows= records > (double) HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records;
- set_if_smaller(rows, unit->select_limit_cnt);
+ set_if_smaller(rows, unit->lim.get_select_limit());
join->select_lex->increase_derived_records(rows);
}
}
@@ -5834,7 +5799,8 @@ static uint get_semi_join_select_list_index(Field *field)
{
Item_in_subselect *subq_pred= emb_sj_nest->sj_subq_pred;
st_select_lex *subq_lex= subq_pred->unit->first_select();
- if (subq_pred->left_expr->cols() == 1)
+ uint ncols= subq_pred->left_exp()->cols();
+ if (ncols == 1)
{
Item *sel_item= subq_lex->ref_pointer_array[0];
if (sel_item->type() == Item::FIELD_ITEM &&
@@ -5845,7 +5811,7 @@ static uint get_semi_join_select_list_index(Field *field)
}
else
{
- for (uint i= 0; i < subq_pred->left_expr->cols(); i++)
+ for (uint i= 0; i < ncols; i++)
{
Item *sel_item= subq_lex->ref_pointer_array[i];
if (sel_item->type() == Item::FIELD_ITEM &&
@@ -6435,18 +6401,18 @@ max_part_bit(key_part_map bits)
/**
Add a new keuse to the specified array of KEYUSE objects
- @param[in,out] keyuse_array array of keyuses to be extended
+ @param[in,out] keyuse_array array of keyuses to be extended
@param[in] key_field info on the key use occurrence
@param[in] key key number for the keyse to be added
@param[in] part key part for the keyuse to be added
@note
The function builds a new KEYUSE object for a key use utilizing the info
- on the left and right parts of the given key use extracted from the
- structure key_field, the key number and key part for this key use.
+ on the left and right parts of the given key use extracted from the
+ structure key_field, the key number and key part for this key use.
The built object is added to the dynamic array keyuse_array.
- @retval 0 the built object is succesfully added
+ @retval 0 the built object is successfully added
@retval 1 otherwise
*/
@@ -6811,7 +6777,7 @@ 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_array2(keyuse, sizeof(KEYUSE),
+ if (my_init_dynamic_array2(thd->mem_root->m_psi_key, keyuse, sizeof(KEYUSE),
thd->alloc(sizeof(KEYUSE) * 20), 20, 64,
MYF(MY_THREAD_SPECIFIC)))
DBUG_RETURN(TRUE);
@@ -6999,7 +6965,6 @@ void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
}
}
-
/**
Check for the presence of AGGFN(DISTINCT a) queries that may be subject
to loose index scan.
@@ -7037,7 +7002,6 @@ is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args)
{
Item_sum **sum_item_ptr;
bool result= false;
- Field_map first_aggdistinct_fields;
if (join->table_count != 1 || /* reference more than 1 table */
join->select_distinct || /* or a DISTINCT */
@@ -7047,10 +7011,11 @@ is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args)
if (join->make_sum_func_list(join->all_fields, join->fields_list, true))
return false;
+ Bitmap<MAX_FIELDS> first_aggdistinct_fields;
+ bool first_aggdistinct_fields_initialized= false;
for (sum_item_ptr= join->sum_funcs; *sum_item_ptr; sum_item_ptr++)
{
Item_sum *sum_item= *sum_item_ptr;
- Field_map cur_aggdistinct_fields;
Item *expr;
/* aggregate is not AGGFN(DISTINCT) or more than 1 argument to it */
switch (sum_item->sum_func())
@@ -7073,6 +7038,8 @@ is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args)
We don't worry about duplicates as these will be sorted out later in
get_best_group_min_max
*/
+ Bitmap<MAX_FIELDS> cur_aggdistinct_fields;
+ cur_aggdistinct_fields.clear_all();
for (uint i= 0; i < sum_item->get_arg_count(); i++)
{
expr= sum_item->get_arg(i);
@@ -7091,8 +7058,11 @@ is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args)
If there are multiple aggregate functions, make sure that they all
refer to exactly the same set of columns.
*/
- if (first_aggdistinct_fields.is_clear_all())
- first_aggdistinct_fields.merge(cur_aggdistinct_fields);
+ if (!first_aggdistinct_fields_initialized)
+ {
+ first_aggdistinct_fields= cur_aggdistinct_fields;
+ first_aggdistinct_fields_initialized=true;
+ }
else if (first_aggdistinct_fields != cur_aggdistinct_fields)
return false;
}
@@ -7226,7 +7196,7 @@ double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
{
TABLE *table= s->table;
double sel= table->cond_selectivity;
- double table_records= (double)table->stat_records();
+ double table_records= rows2double(s->records);
dbl_records= table_records * sel;
return dbl_records;
}
@@ -7249,14 +7219,69 @@ double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
If applicable, get a more accurate estimate. Don't use the two
heuristics at once.
*/
- if (s->table->quick_condition_rows != s->found_records)
- records= s->table->quick_condition_rows;
+ if (s->table->opt_range_condition_rows != s->found_records)
+ records= s->table->opt_range_condition_rows;
dbl_records= (double)records;
return dbl_records;
}
+/*
+ Calculate the cost of reading a set of rows trough an index
+
+ Logically this is identical to the code in multi_range_read_info_const()
+ excepts the function also takes into account io_blocks and multiple
+ ranges.
+
+ One main difference between the functions is that
+ multi_range_read_info_const() adds a very small cost per range
+ (IDX_LOOKUP_COST) and also MULTI_RANGE_READ_SETUP_COST, to ensure that
+ 'ref' is preferred slightly over ranges.
+*/
+
+double cost_for_index_read(const THD *thd, const TABLE *table, uint key,
+ ha_rows records, ha_rows worst_seeks)
+{
+ DBUG_ENTER("cost_for_index_read");
+ double cost;
+ handler *file= table->file;
+
+ set_if_smaller(records, (ha_rows) thd->variables.max_seeks_for_key);
+ if (file->is_clustering_key(key))
+ cost= file->read_time(key, 1, records);
+ else
+ if (table->covering_keys.is_set(key))
+ cost= file->keyread_time(key, 1, records);
+ else
+ cost= ((file->keyread_time(key, 0, records) +
+ file->read_time(key, 1, MY_MIN(records, worst_seeks))));
+
+ DBUG_PRINT("statistics", ("cost: %.3f", cost));
+ DBUG_RETURN(cost);
+}
+
+
+/*
+ Adjust cost from table->quick_costs calculated by
+ multi_range_read_info_const() to be comparable with cost_for_index_read()
+
+ This functions is needed because best_access_patch doesn't add
+ TIME_FOR_COMPARE to it's costs until very late.
+ Preferably we should fix so that all costs are comparably.
+ (All compared costs should include TIME_FOR_COMPARE for all found
+ rows).
+*/
+
+double adjust_quick_cost(double quick_cost, ha_rows records)
+{
+ double cost= (quick_cost - MULTI_RANGE_READ_SETUP_COST -
+ rows2double(records)/TIME_FOR_COMPARE);
+ DBUG_ASSERT(cost > 0.0);
+ return cost;
+}
+
+
/**
Find the best access path for an extension of a partial execution
plan and add this path to the plan.
@@ -7478,10 +7503,16 @@ best_access_path(JOIN *join,
(!(key_flags & HA_NULL_PART_KEY) || // (2)
all_key_parts == notnull_part)) // (3)
{
+
+ /* TODO: Adjust cost for covering and clustering key */
type= JT_EQ_REF;
trace_access_idx.add("access_type", join_type_str[type])
.add("index", keyinfo->name);
- tmp = prev_record_reads(join_positions, idx, found_ref);
+ if (!found_ref && table->opt_range_keys.is_set(key))
+ tmp= adjust_quick_cost(table->opt_range[key].cost, 1);
+ else
+ tmp= table->file->avg_io_cost();
+ tmp*= prev_record_reads(join_positions, idx, found_ref);
records=1.0;
}
else
@@ -7508,10 +7539,13 @@ best_access_path(JOIN *join,
quick_cond is equivalent to ref_const_cond (if it was an
empty interval we wouldn't have got here).
*/
- if (table->quick_keys.is_set(key))
+ if (table->opt_range_keys.is_set(key))
{
- records= (double) table->quick_rows[key];
+ records= (double) table->opt_range[key].rows;
trace_access_idx.add("used_range_estimates", true);
+ tmp= adjust_quick_cost(table->opt_range[key].cost,
+ table->opt_range[key].rows);
+ goto got_cost;
}
else
{
@@ -7543,20 +7577,19 @@ best_access_path(JOIN *join,
can make an adjustment is a special case of the criteria used
in ReuseRangeEstimateForRef-3.
*/
- if (table->quick_keys.is_set(key) &&
+ if (table->opt_range_keys.is_set(key) &&
(const_part &
- (((key_part_map)1 << table->quick_key_parts[key])-1)) ==
- (((key_part_map)1 << table->quick_key_parts[key])-1) &&
- table->quick_n_ranges[key] == 1 &&
- records > (double) table->quick_rows[key])
+ (((key_part_map)1 << table->opt_range[key].key_parts)-1)) ==
+ (((key_part_map)1 << table->opt_range[key].key_parts)-1) &&
+ table->opt_range[key].ranges == 1 &&
+ records > (double) table->opt_range[key].rows)
{
-
- records= (double) table->quick_rows[key];
+ records= (double) table->opt_range[key].rows;
trace_access_idx.add("used_range_estimates", true);
}
else
{
- if (table->quick_keys.is_set(key))
+ if (table->opt_range_keys.is_set(key))
{
trace_access_idx.add("used_range_estimates",false)
.add("cause",
@@ -7570,13 +7603,9 @@ best_access_path(JOIN *join,
}
}
/* Limit the number of matched rows */
- tmp= records;
- set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
- if (table->covering_keys.is_set(key))
- tmp= table->file->keyread_time(key, 1, (ha_rows) tmp);
- else
- tmp= table->file->read_time(key, 1,
- (ha_rows) MY_MIN(tmp,s->worst_seeks));
+ tmp= cost_for_index_read(thd, table, key, (ha_rows) records,
+ (ha_rows) s->worst_seeks);
+ got_cost:
tmp= COST_MULT(tmp, record_count);
}
}
@@ -7634,12 +7663,15 @@ best_access_path(JOIN *join,
(C3) "range optimizer used (have ref_or_null?2:1) intervals"
*/
- 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 + MY_TEST(ref_or_null_part)) //(C3)
+ if (table->opt_range_keys.is_set(key) && !found_ref && //(C1)
+ table->opt_range[key].key_parts == max_key_part && //(C2)
+ table->opt_range[key].ranges == 1 + MY_TEST(ref_or_null_part)) //(C3)
{
- tmp= records= (double) table->quick_rows[key];
+ records= (double) table->opt_range[key].rows;
+ tmp= adjust_quick_cost(table->opt_range[key].cost,
+ table->opt_range[key].rows);
trace_access_idx.add("used_range_estimates", true);
+ goto got_cost2;
}
else
{
@@ -7662,24 +7694,24 @@ best_access_path(JOIN *join,
cheaper in some cases ?
TODO: figure this out and adjust the plan choice if needed.
*/
- if (!found_ref && table->quick_keys.is_set(key) && // (1)
- table->quick_key_parts[key] > max_key_part && // (2)
- records < (double)table->quick_rows[key]) // (3)
- {
- trace_access_idx.add("used_range_estimates", true);
- records= (double)table->quick_rows[key];
- }
- else
+ if (table->opt_range_keys.is_set(key))
{
- if (table->quick_keys.is_set(key) &&
- table->quick_key_parts[key] < max_key_part)
+ if (table->opt_range[key].key_parts >= max_key_part) // (2)
+ {
+ double rows= (double) table->opt_range[key].rows;
+ if (!found_ref && // (1)
+ records < rows) // (3)
+ {
+ trace_access_idx.add("used_range_estimates", true);
+ records= rows;
+ }
+ }
+ else /* (table->quick_key_parts[key] < max_key_part) */
{
- trace_access_idx.add("chosen", false);
- cause= "range uses more keyparts";
+ trace_access_idx.add("chosen", true);
+ cause= "range uses less keyparts";
}
}
-
- tmp= records;
}
else
{
@@ -7703,27 +7735,25 @@ best_access_path(JOIN *join,
rec_per_key=(double) s->records/rec+1;
if (!s->records)
- tmp = 0;
+ records= 0;
else if (rec_per_key/(double) s->records >= 0.01)
- tmp = rec_per_key;
+ records= rec_per_key;
else
{
double a=s->records*0.01;
if (keyinfo->user_defined_key_parts > 1)
- tmp= (max_key_part * (rec_per_key - a) +
+ records= (max_key_part * (rec_per_key - a) +
a*keyinfo->user_defined_key_parts - rec_per_key)/
(keyinfo->user_defined_key_parts-1);
else
- tmp= a;
- set_if_bigger(tmp,1.0);
+ records= a;
+ set_if_bigger(records, 1.0);
}
- records = (ulong) tmp;
}
if (ref_or_null_part)
{
- /* We need to do two key searches to find key */
- tmp *= 2.0;
+ /* We need to do two key searches to find row */
records *= 2.0;
}
@@ -7739,25 +7769,25 @@ best_access_path(JOIN *join,
optimizer is the same as in ReuseRangeEstimateForRef-3,
applied to first table->quick_key_parts[key] key parts.
*/
- if (table->quick_keys.is_set(key) &&
- table->quick_key_parts[key] <= max_key_part &&
+ if (table->opt_range_keys.is_set(key) &&
+ table->opt_range[key].key_parts <= max_key_part &&
const_part &
- ((key_part_map)1 << table->quick_key_parts[key]) &&
- table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part &
- const_part) &&
- records > (double) table->quick_rows[key])
+ ((key_part_map)1 << table->opt_range[key].key_parts) &&
+ table->opt_range[key].ranges == (1 +
+ MY_TEST(ref_or_null_part &
+ const_part)) &&
+ records > (double) table->opt_range[key].rows)
{
- tmp= records= (double) table->quick_rows[key];
+ records= (double) table->opt_range[key].rows;
}
}
/* Limit the number of matched rows */
+ tmp= records;
set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
- if (table->covering_keys.is_set(key))
- tmp= table->file->keyread_time(key, 1, (ha_rows) tmp);
- else
- tmp= table->file->read_time(key, 1,
- (ha_rows) MY_MIN(tmp,s->worst_seeks));
+ tmp= cost_for_index_read(thd, table, key, (ha_rows) tmp,
+ (ha_rows) s->worst_seeks);
+ got_cost2:
tmp= COST_MULT(tmp, record_count);
}
else
@@ -7787,15 +7817,15 @@ best_access_path(JOIN *join,
tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows);
DBUG_ASSERT(tmp >= 0);
trace_access_idx.add("rowid_filter_key",
- s->table->key_info[filter->key_no].name);
+ table->key_info[filter->key_no].name);
}
}
trace_access_idx.add("rows", records).add("cost", tmp);
- if (tmp + 0.0001 < best_time - records/(double) TIME_FOR_COMPARE)
+ if (tmp + 0.0001 < best_time - records/TIME_FOR_COMPARE)
{
trace_access_idx.add("chosen", true);
- best_time= COST_ADD(tmp, records/(double) TIME_FOR_COMPARE);
+ best_time= COST_ADD(tmp, records/TIME_FOR_COMPARE);
best= tmp;
best_records= records;
best_key= start_key;
@@ -7838,7 +7868,7 @@ best_access_path(JOIN *join,
use_cond_selectivity);
tmp= s->quick ? s->quick->read_time : s->scan_time();
- double cmp_time= (s->records - rnd_records)/(double) TIME_FOR_COMPARE;
+ double cmp_time= (s->records - rnd_records)/TIME_FOR_COMPARE;
tmp= COST_ADD(tmp, cmp_time);
/* We read the table as many times as join buffer becomes full. */
@@ -7902,7 +7932,7 @@ best_access_path(JOIN *join,
if ((records >= s->found_records || best > s->read_time) && // (1)
!(best_key && best_key->key == MAX_KEY) && // (2)
!(s->quick && best_key && s->quick->index == best_key->key && // (2)
- best_max_key_part >= s->table->quick_key_parts[best_key->key]) &&// (2)
+ best_max_key_part >= s->table->opt_range[best_key->key].key_parts) &&// (2)
!((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3)
! s->table->covering_keys.is_clear_all() && best_key && !s->quick) &&// (3)
!(s->table->force_index && best_key && !s->quick) && // (4)
@@ -7929,7 +7959,7 @@ best_access_path(JOIN *join,
access (see first else-branch below), but we don't take it into
account here for range/index_merge access. Find out why this is so.
*/
- double cmp_time= (s->found_records - rnd_records)/(double) TIME_FOR_COMPARE;
+ double cmp_time= (s->found_records - rnd_records)/TIME_FOR_COMPARE;
tmp= COST_MULT(record_count,
COST_ADD(s->quick->read_time, cmp_time));
@@ -7976,7 +8006,7 @@ best_access_path(JOIN *join,
- read the whole table record
- skip rows which does not satisfy join condition
*/
- double cmp_time= (s->records - rnd_records)/(double) TIME_FOR_COMPARE;
+ double cmp_time= (s->records - rnd_records)/TIME_FOR_COMPARE;
tmp= COST_MULT(record_count, COST_ADD(tmp,cmp_time));
}
else
@@ -7992,7 +8022,7 @@ best_access_path(JOIN *join,
we read the table (see flush_cached_records for details). Here we
take into account cost to read and skip these records.
*/
- double cmp_time= (s->records - rnd_records)/(double) TIME_FOR_COMPARE;
+ double cmp_time= (s->records - rnd_records)/TIME_FOR_COMPARE;
tmp= COST_ADD(tmp, cmp_time);
}
}
@@ -8019,10 +8049,10 @@ best_access_path(JOIN *join,
trace_access_scan.add("cost", tmp);
if (best == DBL_MAX ||
- COST_ADD(tmp, record_count/(double) TIME_FOR_COMPARE*rnd_records) <
+ COST_ADD(tmp, record_count/TIME_FOR_COMPARE*rnd_records) <
(best_key->is_for_hash_join() ? best_time :
COST_ADD(best - best_filter_cmp_gain,
- record_count/(double) TIME_FOR_COMPARE*records)))
+ record_count/TIME_FOR_COMPARE*records)))
{
/*
If the table has a range (s->quick is set) make_join_select()
@@ -8066,7 +8096,7 @@ best_access_path(JOIN *join,
if (!best_key &&
idx == join->const_tables &&
s->table == join->sort_by_table &&
- join->unit->select_limit_cnt >= records)
+ join->unit->lim.get_select_limit() >= records)
{
trace_access_scan.add("use_tmp_table", true);
join->sort_by_table= (TABLE*) 1; // Must use temporary table
@@ -8574,7 +8604,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
: 0;
read_time+= COST_ADD(read_time - filter_cmp_gain,
COST_ADD(position->read_time,
- record_count / (double) TIME_FOR_COMPARE));
+ record_count / TIME_FOR_COMPARE));
advance_sj_state(join, join_tables, idx, &record_count, &read_time,
&loose_scan_pos);
@@ -8767,7 +8797,7 @@ greedy_search(JOIN *join,
record_count= COST_MULT(record_count, join->positions[idx].records_read);
read_time= COST_ADD(read_time,
COST_ADD(join->positions[idx].read_time,
- record_count / (double) TIME_FOR_COMPARE));
+ record_count / TIME_FOR_COMPARE));
remaining_tables&= ~(best_table->table->map);
--size_remain;
@@ -8877,7 +8907,7 @@ void JOIN::get_partial_cost_and_fanout(int end_tab_idx,
record_count= COST_MULT(record_count, tab->records_read);
read_time= COST_ADD(read_time,
COST_ADD(tab->read_time,
- record_count / (double) TIME_FOR_COMPARE));
+ record_count / TIME_FOR_COMPARE));
if (tab->emb_sj_nest)
sj_inner_fanout= COST_MULT(sj_inner_fanout, tab->records_read);
}
@@ -9167,10 +9197,11 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
/*
Check if we have a prefix of key=const that matches a quick select.
*/
- if (!is_hash_join_key_no(key) && table->quick_keys.is_set(key))
+ if (!is_hash_join_key_no(key) && table->opt_range_keys.is_set(key))
{
- key_part_map quick_key_map= (key_part_map(1) << table->quick_key_parts[key]) - 1;
- if (table->quick_rows[key] &&
+ key_part_map quick_key_map= (key_part_map(1) <<
+ table->opt_range[key].key_parts) - 1;
+ if (table->opt_range[key].rows &&
!(quick_key_map & ~table->const_key_parts[key]))
{
/*
@@ -9197,7 +9228,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
However if sel becomes greater than 2 then with high probability
something went wrong.
*/
- sel /= (double)table->quick_rows[key] / (double) table->stat_records();
+ sel /= (double)table->opt_range[key].rows / (double) table->stat_records();
set_if_smaller(sel, 1.0);
used_range_selectivity= true;
}
@@ -9487,9 +9518,9 @@ best_extension_by_limited_search(JOIN *join,
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
{
table_map real_table_bit= s->table->map;
- if ((remaining_tables & real_table_bit) &&
+ if ((remaining_tables & real_table_bit) &&
(allowed_tables & real_table_bit) &&
- !(remaining_tables & s->dependent) &&
+ !(remaining_tables & s->dependent) &&
(!idx || !check_interleaving_with_nj(s)))
{
double current_record_count, current_read_time;
@@ -9516,7 +9547,7 @@ best_extension_by_limited_search(JOIN *join,
COST_ADD(position->read_time -
filter_cmp_gain,
current_record_count /
- (double) TIME_FOR_COMPARE));
+ TIME_FOR_COMPARE));
if (unlikely(thd->trace_started()))
{
@@ -10302,14 +10333,16 @@ bool JOIN::get_best_combination()
if (aggr_tables > 2)
aggr_tables= 2;
- if (!(join_tab= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*
- (top_join_tab_count + aggr_tables))))
- DBUG_RETURN(TRUE);
full_join=0;
hash_join= FALSE;
fix_semijoin_strategies_for_picked_join_order(this);
+ top_join_tab_count= get_number_of_tables_at_top_level(this);
+
+ if (!(join_tab= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*
+ (top_join_tab_count + aggr_tables))))
+ DBUG_RETURN(TRUE);
JOIN_TAB_RANGE *root_range;
if (!(root_range= new (thd->mem_root) JOIN_TAB_RANGE))
@@ -11554,11 +11587,16 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
HA_CAN_TABLE_CONDITION_PUSHDOWN) &&
!first_inner_tab)
{
+ Json_writer_object wrap(thd);
+ Json_writer_object trace_cp(thd, "table_condition_pushdown");
+ trace_cp.add_table_name(tab->table);
+
COND *push_cond=
make_cond_for_table(thd, tmp_cond, current_map, current_map,
-1, FALSE, FALSE);
if (push_cond)
{
+ trace_cp.add("push_cond", push_cond);
/* Push condition to handler */
if (!tab->table->file->cond_push(push_cond))
tab->table->file->pushed_cond= push_cond;
@@ -11638,7 +11676,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
!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 <
+ join->unit->lim.get_select_limit() <
join->best_positions[i].records_read &&
!(join->select_options & OPTION_FOUND_ROWS))))
{
@@ -11664,7 +11702,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt), 0,
+ join->unit->lim.get_select_limit()), 0,
FALSE, FALSE, FALSE) < 0)
{
/*
@@ -11678,7 +11716,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt),0,
+ join->unit->lim.get_select_limit()),0,
FALSE, FALSE, FALSE) < 0)
DBUG_RETURN(1); // Impossible WHERE
}
@@ -11693,7 +11731,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{
sel->needed_reg=tab->needed_reg;
}
- sel->quick_keys= tab->table->quick_keys;
+ sel->quick_keys= tab->table->opt_range_keys;
if (!sel->quick_keys.is_subset(tab->checked_keys) ||
!sel->needed_reg.is_subset(tab->checked_keys))
{
@@ -13091,17 +13129,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
/* Only happens with outer joins */
tab->read_first_record= tab->type == JT_SYSTEM ? join_read_system
: join_read_const;
- if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
- table->file->ha_start_keyread(tab->ref.key);
- else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
+ if (!(table->covering_keys.is_set(tab->ref.key) && !table->no_keyread) &&
+ (!jcl || jcl > 4) && !tab->ref.is_access_triggered())
push_index_cond(tab, tab->ref.key);
break;
case JT_EQ_REF:
tab->read_record.unlock_row= join_read_key_unlock_row;
/* fall through */
- if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
- table->file->ha_start_keyread(tab->ref.key);
- else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
+ if (!(table->covering_keys.is_set(tab->ref.key) && !table->no_keyread) &&
+ (!jcl || jcl > 4) && !tab->ref.is_access_triggered())
push_index_cond(tab, tab->ref.key);
break;
case JT_REF_OR_NULL:
@@ -13113,9 +13149,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
}
delete tab->quick;
tab->quick=0;
- if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
- table->file->ha_start_keyread(tab->ref.key);
- else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered())
+ if (!(table->covering_keys.is_set(tab->ref.key) && !table->no_keyread) &&
+ (!jcl || jcl > 4) && !tab->ref.is_access_triggered())
push_index_cond(tab, tab->ref.key);
break;
case JT_ALL:
@@ -13174,12 +13209,11 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
}
if (!table->no_keyread)
{
- if (tab->select && tab->select->quick &&
- tab->select->quick->index != MAX_KEY && //not index_merge
- table->covering_keys.is_set(tab->select->quick->index))
- table->file->ha_start_keyread(tab->select->quick->index);
- else if (!table->covering_keys.is_clear_all() &&
- !(tab->select && tab->select->quick))
+ if (!(tab->select && tab->select->quick &&
+ tab->select->quick->index != MAX_KEY && //not index_merge
+ table->covering_keys.is_set(tab->select->quick->index)) &&
+ (!table->covering_keys.is_clear_all() &&
+ !(tab->select && tab->select->quick)))
{ // Only read index tree
if (tab->loosescan_match_tab)
tab->index= tab->loosescan_key;
@@ -13193,8 +13227,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
See bug #26447: "Using the clustered index for a table scan
is always faster than using a secondary index".
*/
- if (table->s->primary_key != MAX_KEY &&
- table->file->primary_key_is_clustered())
+ if (table->file->pk_is_clustering_key(table->s->primary_key))
tab->index= table->s->primary_key;
else
#endif
@@ -13207,7 +13240,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
}
if (tab->select && tab->select->quick &&
tab->select->quick->index != MAX_KEY &&
- !tab->table->file->keyread_enabled())
+ !tab->table->covering_keys.is_set(tab->select->quick->index))
push_index_cond(tab, tab->select->quick->index);
}
break;
@@ -13336,7 +13369,7 @@ void JOIN_TAB::build_range_rowid_filter_if_needed()
Exec_time_tracker *table_tracker= table->file->get_time_tracker();
Rowid_filter_tracker *rowid_tracker= rowid_filter->get_tracker();
table->file->set_time_tracker(rowid_tracker->get_time_tracker());
- rowid_tracker->start_tracking();
+ rowid_tracker->start_tracking(join->thd);
if (!rowid_filter->build())
{
is_rowid_filter_built= true;
@@ -13346,7 +13379,7 @@ void JOIN_TAB::build_range_rowid_filter_if_needed()
delete rowid_filter;
rowid_filter= 0;
}
- rowid_tracker->stop_tracking();
+ rowid_tracker->stop_tracking(join->thd);
table->file->set_time_tracker(table_tracker);
}
}
@@ -13460,14 +13493,14 @@ double JOIN_TAB::scan_time()
get_delayed_table_estimates(table, &records, &read_time,
&startup_cost);
found_records= records;
- table->quick_condition_rows= records;
+ table->opt_range_condition_rows= records;
}
else
{
found_records= records= table->stat_records();
read_time= table->file->scan_time();
/*
- table->quick_condition_rows has already been set to
+ table->opt_range_condition_rows has already been set to
table->file->stats.records
*/
}
@@ -13549,8 +13582,7 @@ bool JOIN_TAB::preread_init()
if ((!derived->get_unit()->executed ||
derived->is_recursive_with_table() ||
derived->get_unit()->uncacheable) &&
- mysql_handle_single_derived(join->thd->lex,
- derived, DT_CREATE | DT_FILL))
+ mysql_handle_single_derived(join->thd->lex, derived, DT_CREATE | DT_FILL))
return TRUE;
if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) ||
@@ -13575,6 +13607,21 @@ bool JOIN_TAB::preread_init()
}
+bool JOIN_TAB::pfs_batch_update(JOIN *join)
+{
+ /*
+ Use PFS batch mode if
+ 1. tab is an inner-most table, or
+ 2. will read more than one row (not eq_ref or const access type)
+ 3. no subqueries
+ */
+
+ return join->join_tab + join->table_count - 1 == this && // 1
+ type != JT_EQ_REF && type != JT_CONST && type != JT_SYSTEM && // 2
+ (!select_cond || !select_cond->with_subquery()); // 3
+}
+
+
/**
Build a TABLE_REF structure for index lookup in the temporary table
@@ -14096,7 +14143,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
ORDER BY and GROUP BY
*/
for (JOIN_TAB *tab= join->join_tab + join->const_tables;
- tab < join->join_tab + join->table_count;
+ tab < join->join_tab + join->top_join_tab_count;
tab++)
tab->cached_eq_ref_table= FALSE;
@@ -14104,7 +14151,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order= head->on_expr_ref[0] == NULL;
if (*simple_order && head->table->file->ha_table_flags() & HA_SLOW_RND_POS)
{
- uint u1, u2, u3;
+ uint u1, u2, u3, u4;
/*
normally the condition is (see filesort_use_addons())
@@ -14115,7 +14162,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
TODO proper cost estimations
*/
- *simple_order= filesort_use_addons(head->table, 0, &u1, &u2, &u3);
+ *simple_order= filesort_use_addons(head->table, 0, &u1, &u2, &u3, &u4);
}
}
else
@@ -14381,7 +14428,7 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
{
bool send_error= FALSE;
if (send_row)
- send_error= result->send_data(fields) > 0;
+ send_error= result->send_data_with_check(fields, join->unit, 0) > 0;
if (likely(!send_error))
result->send_eof(); // Should be safe
}
@@ -14919,28 +14966,28 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
left_item, right_item, cond_equal);
}
-
+
/**
Item_xxx::build_equal_items()
-
+
Replace all equality predicates in a condition referenced by "this"
by multiple equality items.
At each 'and' level the function detects items for equality predicates
and replaced them by a set of multiple equality items of class Item_equal,
- taking into account inherited equalities from upper levels.
+ taking into account inherited equalities from upper levels.
If an equality predicate is used not in a conjunction it's just
replaced by a multiple equality predicate.
For each 'and' level the function set a pointer to the inherited
multiple equalities in the cond_equal field of the associated
- object of the type Item_cond_and.
+ object of the type Item_cond_and.
The function also traverses the cond tree and and for each field reference
sets a pointer to the multiple equality item containing the field, if there
is any. If this multiple equality equates fields to a constant the
- function replaces the field reference by the constant in the cases
+ function replaces the field reference by the constant in the cases
when the field is not of a string type or when the field reference is
just an argument of a comparison predicate.
- The function also determines the maximum number of members in
+ The function also determines the maximum number of members in
equality lists of each Item_cond_and object assigning it to
thd->lex->current_select->max_equal_elems.
@@ -14954,7 +15001,7 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
in a conjuction for a minimal set of multiple equality predicates.
This set can be considered as a canonical representation of the
sub-conjunction of the equality predicates.
- E.g. (t1.a=t2.b AND t2.b>5 AND t1.a=t3.c) is replaced by
+ E.g. (t1.a=t2.b AND t2.b>5 AND t1.a=t3.c) is replaced by
(=(t1.a,t2.b,t3.c) AND t2.b>5), not by
(=(t1.a,t2.b) AND =(t1.a,t3.c) AND t2.b>5);
while (t1.a=t2.b AND t2.b>5 AND t3.c=t4.d) is replaced by
@@ -14965,16 +15012,16 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
The function performs the substitution in a recursive descent by
the condtion tree, passing to the next AND level a chain of multiple
equality predicates which have been built at the upper levels.
- The Item_equal items built at the level are attached to other
+ The Item_equal items built at the level are attached to other
non-equality conjucts as a sublist. The pointer to the inherited
multiple equalities is saved in the and condition object (Item_cond_and).
- This chain allows us for any field reference occurence easyly to find a
- multiple equality that must be held for this occurence.
+ This chain allows us for any field reference occurrence easily to find a
+ multiple equality that must be held for this occurrence.
For each AND level we do the following:
- scan it for all equality predicate (=) items
- join them into disjoint Item_equal() groups
- - process the included OR conditions recursively to do the same for
- lower AND levels.
+ - process the included OR conditions recursively to do the same for
+ lower AND levels.
We need to do things in this order as lower AND levels need to know about
all possible Item_equal objects in upper levels.
@@ -15010,7 +15057,7 @@ COND *Item_cond_and::build_equal_items(THD *thd,
/*
Retrieve all conjuncts of this level detecting the equality
that are subject to substitution by multiple equality items and
- removing each such predicate from the conjunction after having
+ removing each such predicate from the conjunction after having
found/created a multiple equality whose inference the predicate is.
*/
while ((item= li++))
@@ -16909,7 +16956,7 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
reopt_remaining_tables &= ~rs->table->map;
rec_count= COST_MULT(rec_count, pos.records_read);
cost= COST_ADD(cost, pos.read_time);
- cost= COST_ADD(cost, rec_count / (double) TIME_FOR_COMPARE);
+ cost= COST_ADD(cost, rec_count / TIME_FOR_COMPARE);
//TODO: take into account join condition selectivity here
double pushdown_cond_selectivity= 1.0;
table_map real_table_bit= rs->table->map;
@@ -17743,16 +17790,20 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
Create internal temporary table
****************************************************************************/
-Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length)
+Field *Item::create_tmp_field_int(MEM_ROOT *root, TABLE *table,
+ uint convert_int_length)
{
- const Type_handler *h= &type_handler_long;
+ const Type_handler *h= &type_handler_slong;
if (max_char_length() > convert_int_length)
- h= &type_handler_longlong;
- return h->make_and_init_table_field(&name, Record_addr(maybe_null),
+ h= &type_handler_slonglong;
+ if (unsigned_flag)
+ h= h->type_handler_unsigned();
+ return h->make_and_init_table_field(root, &name, Record_addr(maybe_null),
*this, table);
}
-Field *Item::tmp_table_field_from_field_type_maybe_null(TABLE *table,
+Field *Item::tmp_table_field_from_field_type_maybe_null(MEM_ROOT *root,
+ TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param,
bool is_explicit_null)
@@ -17764,7 +17815,7 @@ Field *Item::tmp_table_field_from_field_type_maybe_null(TABLE *table,
DBUG_ASSERT(!param->make_copy_field() || type() == CONST_ITEM);
DBUG_ASSERT(!is_result_field());
Field *result;
- if ((result= tmp_table_field_from_field_type(table)))
+ if ((result= tmp_table_field_from_field_type(root, table)))
{
if (result && is_explicit_null)
result->is_created_from_null_item= true;
@@ -17773,15 +17824,14 @@ Field *Item::tmp_table_field_from_field_type_maybe_null(TABLE *table,
}
-Field *Item_sum::create_tmp_field(bool group, TABLE *table)
+Field *Item_sum::create_tmp_field(MEM_ROOT *root, bool group, TABLE *table)
{
Field *UNINIT_VAR(new_field);
- MEM_ROOT *mem_root= table->in_use->mem_root;
switch (cmp_type()) {
case REAL_RESULT:
{
- new_field= new (mem_root)
+ new_field= new (root)
Field_double(max_char_length(), maybe_null, &name, decimals, TRUE);
break;
}
@@ -17789,7 +17839,7 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table)
case TIME_RESULT:
case DECIMAL_RESULT:
case STRING_RESULT:
- new_field= tmp_table_field_from_field_type(table);
+ new_field= tmp_table_field_from_field_type(root, table);
break;
case ROW_RESULT:
// This case should never be choosen
@@ -17804,43 +17854,11 @@ Field *Item_sum::create_tmp_field(bool group, TABLE *table)
/**
- Create field for information schema table.
-
- @param thd Thread handler
- @param table Temporary table
- @param item Item to create a field for
-
- @retval
- 0 on error
- @retval
- new_created field
-*/
-
-Field *Item::create_field_for_schema(THD *thd, TABLE *table)
-{
- if (field_type() == MYSQL_TYPE_VARCHAR)
- {
- Field *field;
- if (max_length > MAX_FIELD_VARCHARLENGTH)
- field= new Field_blob(max_length, maybe_null, &name,
- collation.collation);
- else
- field= new Field_varstring(max_length, maybe_null, &name,
- table->s, collation.collation);
- if (field)
- field->init(table);
- return field;
- }
- return tmp_table_field_from_field_type(table);
-}
-
-
-/**
Create a temporary field for Item_field (or its descendant),
either direct or referenced by an Item_ref.
*/
Field *
-Item_field::create_tmp_field_from_item_field(TABLE *new_table,
+Item_field::create_tmp_field_from_item_field(MEM_ROOT *root, TABLE *new_table,
Item_ref *orig_item,
const Tmp_field_param *param)
{
@@ -17864,14 +17882,16 @@ Item_field::create_tmp_field_from_item_field(TABLE *new_table,
Record_addr rec(orig_item ? orig_item->maybe_null : maybe_null);
const Type_handler *handler= type_handler()->
type_handler_for_tmp_table(this);
- result= handler->make_and_init_table_field(orig_item ? &orig_item->name : &name,
+ result= handler->make_and_init_table_field(root,
+ orig_item ? &orig_item->name : &name,
rec, *this, new_table);
}
else if (param->table_cant_handle_bit_fields() &&
field->type() == MYSQL_TYPE_BIT)
{
- const Type_handler *handler= type_handler_long_or_longlong();
- result= handler->make_and_init_table_field(&name,
+ const Type_handler *handler=
+ Type_handler::type_handler_long_or_longlong(max_char_length(), true);
+ result= handler->make_and_init_table_field(root, &name,
Record_addr(maybe_null),
*this, new_table);
}
@@ -17880,8 +17900,7 @@ Item_field::create_tmp_field_from_item_field(TABLE *new_table,
LEX_CSTRING *tmp= orig_item ? &orig_item->name : &name;
bool tmp_maybe_null= param->modify_item() ? maybe_null :
field->maybe_null();
- result= field->create_tmp_field(new_table->in_use->mem_root, new_table,
- tmp_maybe_null);
+ result= field->create_tmp_field(root, new_table, tmp_maybe_null);
if (result)
result->field_name= *tmp;
}
@@ -17891,14 +17910,14 @@ Item_field::create_tmp_field_from_item_field(TABLE *new_table,
}
-Field *Item_field::create_tmp_field_ex(TABLE *table,
+Field *Item_field::create_tmp_field_ex(MEM_ROOT *root, TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
DBUG_ASSERT(!is_result_field());
Field *result;
src->set_field(field);
- if (!(result= create_tmp_field_from_item_field(table, NULL, param)))
+ if (!(result= create_tmp_field_from_item_field(root, table, NULL, param)))
return NULL;
/*
Fields that are used as arguments to the DEFAULT() function already have
@@ -17911,7 +17930,7 @@ Field *Item_field::create_tmp_field_ex(TABLE *table,
}
-Field *Item_ref::create_tmp_field_ex(TABLE *table,
+Field *Item_ref::create_tmp_field_ex(MEM_ROOT *root, TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
@@ -17924,13 +17943,14 @@ Field *Item_ref::create_tmp_field_ex(TABLE *table,
Tmp_field_param prm2(*param);
prm2.set_modify_item(false);
src->set_field(field->field);
- if (!(result= field->create_tmp_field_from_item_field(table, this, &prm2)))
+ if (!(result= field->create_tmp_field_from_item_field(root, table,
+ this, &prm2)))
return NULL;
if (param->modify_item())
result_field= result;
return result;
}
- return Item_result_field::create_tmp_field_ex(table, src, param);
+ return Item_result_field::create_tmp_field_ex(root, table, src, param);
}
@@ -17949,9 +17969,13 @@ void Item_result_field::get_tmp_field_src(Tmp_field_src *src,
}
-Field *Item_result_field::create_tmp_field_ex(TABLE *table,
- Tmp_field_src *src,
- const Tmp_field_param *param)
+Field *
+Item_result_field::create_tmp_field_ex_from_handler(
+ MEM_ROOT *root,
+ TABLE *table,
+ Tmp_field_src *src,
+ const Tmp_field_param *param,
+ const Type_handler *h)
{
/*
Possible Item types:
@@ -17959,38 +17983,27 @@ Field *Item_result_field::create_tmp_field_ex(TABLE *table,
- Item_func
- Item_subselect
*/
+ DBUG_ASSERT(fixed);
DBUG_ASSERT(is_result_field());
DBUG_ASSERT(type() != NULL_ITEM);
get_tmp_field_src(src, param);
Field *result;
- if ((result= tmp_table_field_from_field_type(table)) && param->modify_item())
- result_field= result;
- return result;
-}
-
-
-Field *Item_func_user_var::create_tmp_field_ex(TABLE *table,
- Tmp_field_src *src,
- const Tmp_field_param *param)
-{
- DBUG_ASSERT(is_result_field());
- DBUG_ASSERT(type() != NULL_ITEM);
- get_tmp_field_src(src, param);
- Field *result;
- if ((result= create_table_field_from_handler(table)) && param->modify_item())
+ if ((result= h->make_and_init_table_field(root, &name,
+ Record_addr(maybe_null),
+ *this, table)) &&
+ param->modify_item())
result_field= result;
return result;
}
-Field *Item_func_sp::create_tmp_field_ex(TABLE *table,
+Field *Item_func_sp::create_tmp_field_ex(MEM_ROOT *root, TABLE *table,
Tmp_field_src *src,
const Tmp_field_param *param)
{
Field *result;
get_tmp_field_src(src, param);
- if ((result= sp_result_field->create_tmp_field(table->in_use->mem_root,
- table)))
+ if ((result= sp_result_field->create_tmp_field(root, table)))
{
result->field_name= name;
if (param->modify_item())
@@ -18036,7 +18049,8 @@ Field *create_tmp_field(TABLE *table, Item *item,
Tmp_field_src src;
Tmp_field_param prm(group, modify_item, table_cant_handle_bit_fields,
make_copy_field);
- Field *result= item->create_tmp_field_ex(table, &src, &prm);
+ Field *result= item->create_tmp_field_ex(table->in_use->mem_root,
+ table, &src, &prm);
*from_field= src.field();
*default_field= src.default_field();
if (src.item_result_field())
@@ -18088,6 +18102,113 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
}
+class Create_tmp_table: public Data_type_statistics
+{
+ // The following members are initialized only in start()
+ Field **m_from_field, **m_default_field;
+ KEY_PART_INFO *m_key_part_info;
+ uchar *m_group_buff, *m_bitmaps;
+ // The following members are initialized in ctor
+ uint m_alloced_field_count;
+ bool m_using_unique_constraint;
+ uint m_temp_pool_slot;
+ ORDER *m_group;
+ bool m_distinct;
+ bool m_save_sum_fields;
+ bool m_with_cycle;
+ ulonglong m_select_options;
+ ha_rows m_rows_limit;
+ uint m_group_null_items;
+
+ // counter for distinct/other fields
+ uint m_field_count[2];
+ // counter for distinct/other fields which can be NULL
+ uint m_null_count[2];
+ // counter for distinct/other blob fields
+ uint m_blobs_count[2];
+ // counter for "tails" of bit fields which do not fit in a byte
+ uint m_uneven_bit[2];
+
+public:
+ enum counter {distinct, other};
+ /*
+ shows which field we are processing: distinct/other (set in processing
+ cycles)
+ */
+ counter current_counter;
+ Create_tmp_table(const TMP_TABLE_PARAM *param,
+ ORDER *group, bool distinct, bool save_sum_fields,
+ ulonglong select_options, ha_rows rows_limit)
+ :m_alloced_field_count(0),
+ m_using_unique_constraint(false),
+ m_temp_pool_slot(MY_BIT_NONE),
+ m_group(group),
+ m_distinct(distinct),
+ m_save_sum_fields(save_sum_fields),
+ m_with_cycle(false),
+ m_select_options(select_options),
+ m_rows_limit(rows_limit),
+ m_group_null_items(0),
+ current_counter(other)
+ {
+ m_field_count[Create_tmp_table::distinct]= 0;
+ m_field_count[Create_tmp_table::other]= 0;
+ m_null_count[Create_tmp_table::distinct]= 0;
+ m_null_count[Create_tmp_table::other]= 0;
+ m_blobs_count[Create_tmp_table::distinct]= 0;
+ m_blobs_count[Create_tmp_table::other]= 0;
+ m_uneven_bit[Create_tmp_table::distinct]= 0;
+ m_uneven_bit[Create_tmp_table::other]= 0;
+ }
+
+ void add_field(TABLE *table, Field *field, uint fieldnr, bool force_not_null_cols);
+
+ TABLE *start(THD *thd,
+ TMP_TABLE_PARAM *param,
+ const LEX_CSTRING *table_alias);
+
+ bool add_fields(THD *thd, TABLE *table,
+ TMP_TABLE_PARAM *param, List<Item> &fields);
+
+ bool add_schema_fields(THD *thd, TABLE *table,
+ TMP_TABLE_PARAM *param,
+ const ST_SCHEMA_TABLE &schema_table);
+
+ bool finalize(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
+ bool do_not_open, bool keep_row_order);
+ void cleanup_on_failure(THD *thd, TABLE *table);
+};
+
+
+void Create_tmp_table::add_field(TABLE *table, Field *field, uint fieldnr, bool force_not_null_cols)
+{
+ DBUG_ASSERT(!field->field_name.str || strlen(field->field_name.str) == field->field_name.length);
+
+ if (force_not_null_cols)
+ {
+ field->flags|= NOT_NULL_FLAG;
+ field->null_ptr= NULL;
+ }
+
+ if (!(field->flags & NOT_NULL_FLAG))
+ m_null_count[current_counter]++;
+
+ table->s->reclength+= field->pack_length();
+
+ // Assign it here, before update_data_type_statistics() changes m_blob_count
+ if (field->flags & BLOB_FLAG)
+ {
+ table->s->blob_field[m_blob_count]= fieldnr;
+ m_blobs_count[current_counter]++;
+ }
+
+ table->field[fieldnr]= field;
+ field->field_index= fieldnr;
+
+ field->update_data_type_statistics(this);
+}
+
+
/**
Create a temp table according to a field list.
@@ -18122,63 +18243,38 @@ setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
inserted, the engine should preserve this order
*/
-TABLE *
-create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
- ORDER *group, bool distinct, bool save_sum_fields,
- ulonglong select_options, ha_rows rows_limit,
- const LEX_CSTRING *table_alias, bool do_not_open,
- bool keep_row_order)
+TABLE *Create_tmp_table::start(THD *thd,
+ TMP_TABLE_PARAM *param,
+ const LEX_CSTRING *table_alias)
{
MEM_ROOT *mem_root_save, own_root;
TABLE *table;
TABLE_SHARE *share;
- uint i,field_count,null_count,null_pack_length;
uint copy_func_count= param->func_count;
- uint hidden_null_count, hidden_null_pack_length, hidden_field_count;
- uint blob_count,group_null_items, string_count;
- uint temp_pool_slot=MY_BIT_NONE;
- uint fieldnr= 0;
- ulong reclength, string_total_length;
- bool using_unique_constraint= false;
- bool use_packed_rows= false;
- bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS);
- bool save_abort_on_warning;
char *tmpname,path[FN_REFLEN];
- uchar *pos, *group_buff, *bitmaps;
- uchar *null_flags;
- Field **reg_field, **from_field, **default_field;
+ Field **reg_field;
uint *blob_field;
- Copy_field *copy=0;
- KEY *keyinfo;
- KEY_PART_INFO *key_part_info;
- Item **copy_func;
- 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
- */
- uint total_uneven_bit_length= 0, hidden_uneven_bit_length= 0;
- bool force_copy_fields= param->force_copy_fields;
+ key_part_map *const_key_parts;
/* Treat sum functions as normal ones when loose index scan is used. */
- save_sum_fields|= param->precomputed_group_by;
- DBUG_ENTER("create_tmp_table");
+ m_save_sum_fields|= param->precomputed_group_by;
+ DBUG_ENTER("Create_tmp_table::start");
DBUG_PRINT("enter",
("table_alias: '%s' distinct: %d save_sum_fields: %d "
"rows_limit: %lu group: %d", table_alias->str,
- (int) distinct, (int) save_sum_fields,
- (ulong) rows_limit, MY_TEST(group)));
+ (int) m_distinct, (int) m_save_sum_fields,
+ (ulong) m_rows_limit, MY_TEST(m_group)));
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
- temp_pool_slot = bitmap_lock_set_next(&temp_pool);
+ m_temp_pool_slot = bitmap_lock_set_next(&temp_pool);
- if (temp_pool_slot != MY_BIT_NONE) // we got a slot
- sprintf(path, "%s_%lx_%i", tmp_file_prefix,
- current_pid, temp_pool_slot);
+ if (m_temp_pool_slot != MY_BIT_NONE) // we got a slot
+ sprintf(path, "%s-temptable-%lx-%i", tmp_file_prefix,
+ current_pid, m_temp_pool_slot);
else
{
/* if we run out of slots or we are not using tempool */
- sprintf(path, "%s%lx_%lx_%x", tmp_file_prefix,current_pid,
- (ulong) thd->thread_id, thd->tmp_table++);
+ sprintf(path, "%s-temptable-%lx-%llx-%x", tmp_file_prefix,current_pid,
+ thd->thread_id, thd->tmp_table++);
}
/*
@@ -18187,12 +18283,12 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME);
- if (group)
+ if (m_group)
{
- ORDER **prev= &group;
+ ORDER **prev= &m_group;
if (!param->quick_group)
- group=0; // Can't use group key
- else for (ORDER *tmp=group ; tmp ; tmp=tmp->next)
+ m_group= 0; // Can't use group key
+ else for (ORDER *tmp= m_group ; tmp ; tmp= tmp->next)
{
/* Exclude found constant from the list */
if ((*tmp->item)->const_item())
@@ -18211,16 +18307,17 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
(*tmp->item)->marker=4; // Store null in key
if ((*tmp->item)->too_big_for_varchar())
- using_unique_constraint= true;
+ m_using_unique_constraint= true;
}
if (param->group_length >= MAX_BLOB_WIDTH)
- using_unique_constraint= true;
- if (group)
- distinct=0; // Can't use distinct
+ m_using_unique_constraint= true;
+ if (m_group)
+ m_distinct= 0; // Can't use distinct
}
- field_count=param->field_count+param->func_count+param->sum_func_count;
- hidden_field_count=param->hidden_field_count;
+ m_alloced_field_count= param->field_count+param->func_count+param->sum_func_count;
+ DBUG_ASSERT(m_alloced_field_count);
+ const uint field_count= m_alloced_field_count;
/*
When loose index scan is employed as access method, it already
@@ -18232,64 +18329,62 @@ 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, "tmp_table", TABLE_ALLOC_BLOCK_SIZE, 0,
+ init_sql_alloc(key_memory_TABLE, &own_root, TABLE_ALLOC_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
&share, sizeof(*share),
&reg_field, sizeof(Field*) * (field_count+1),
- &default_field, sizeof(Field*) * (field_count),
+ &m_default_field, sizeof(Field*) * (field_count),
&blob_field, sizeof(uint)*(field_count+1),
- &from_field, sizeof(Field*)*field_count,
- &copy_func, sizeof(*copy_func)*(copy_func_count+1),
+ &m_from_field, sizeof(Field*)*field_count,
+ &param->items_to_copy,
+ sizeof(param->items_to_copy[0])*(copy_func_count+1),
&param->keyinfo, sizeof(*param->keyinfo),
- &key_part_info,
- sizeof(*key_part_info)*(param->group_parts+1),
+ &m_key_part_info,
+ sizeof(*m_key_part_info)*(param->group_parts+1),
&param->start_recinfo,
sizeof(*param->recinfo)*(field_count*2+4),
&tmpname, (uint) strlen(path)+1,
- &group_buff, (group && ! using_unique_constraint ?
+ &m_group_buff, (m_group && ! m_using_unique_constraint ?
param->group_length : 0),
- &bitmaps, bitmap_buffer_size(field_count)*6,
+ &m_bitmaps, bitmap_buffer_size(field_count)*6,
+ &const_key_parts, sizeof(*const_key_parts),
NullS))
{
- if (temp_pool_slot != MY_BIT_NONE)
- bitmap_lock_clear_bit(&temp_pool, temp_pool_slot);
DBUG_RETURN(NULL); /* purecov: inspected */
}
/* Copy_field belongs to TMP_TABLE_PARAM, allocate it in THD mem_root */
- if (!(param->copy_field= copy= new (thd->mem_root) Copy_field[field_count]))
+ if (!(param->copy_field= new (thd->mem_root) Copy_field[field_count]))
{
- if (temp_pool_slot != MY_BIT_NONE)
- bitmap_lock_clear_bit(&temp_pool, temp_pool_slot);
free_root(&own_root, MYF(0)); /* purecov: inspected */
DBUG_RETURN(NULL); /* purecov: inspected */
}
- param->items_to_copy= copy_func;
strmov(tmpname, path);
/* make table according to fields */
bzero((char*) table,sizeof(*table));
- bzero((char*) reg_field,sizeof(Field*)*(field_count+1));
- bzero((char*) default_field, sizeof(Field*) * (field_count));
- bzero((char*) from_field,sizeof(Field*)*field_count);
+ bzero((char*) reg_field, sizeof(Field*) * (field_count+1));
+ bzero((char*) m_default_field, sizeof(Field*) * (field_count));
+ bzero((char*) m_from_field, sizeof(Field*) * field_count);
+ /* const_key_parts is used in sort_and_filter_keyuse */
+ bzero((char*) const_key_parts, sizeof(*const_key_parts));
table->mem_root= own_root;
mem_root_save= thd->mem_root;
thd->mem_root= &table->mem_root;
table->field=reg_field;
+ table->const_key_parts= const_key_parts;
table->alias.set(table_alias->str, table_alias->length, table_alias_charset);
table->reginfo.lock_type=TL_WRITE; /* Will be updated */
table->map=1;
- table->temp_pool_slot = temp_pool_slot;
+ table->temp_pool_slot= m_temp_pool_slot;
table->copy_blobs= 1;
table->in_use= thd;
table->no_rows_with_nulls= param->force_not_null_cols;
- table->update_handler= NULL;
- table->check_unique_buf= NULL;
table->s= share;
init_tmp_table_share(thd, share, "", 0, "(temporary)", tmpname);
@@ -18299,17 +18394,60 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (param->schema_table)
share->db= INFORMATION_SCHEMA_NAME;
- /* Calculate which type of fields we will store in the temporary table */
-
- reclength= string_total_length= 0;
- blob_count= string_count= null_count= hidden_null_count= group_null_items= 0;
param->using_outer_summary_function= 0;
+ thd->mem_root= mem_root_save;
+ DBUG_RETURN(table);
+}
+
+
+bool Create_tmp_table::add_fields(THD *thd,
+ TABLE *table,
+ TMP_TABLE_PARAM *param,
+ List<Item> &fields)
+{
+ DBUG_ENTER("Create_tmp_table::add_fields");
+ DBUG_ASSERT(table);
+ DBUG_ASSERT(table->field);
+ DBUG_ASSERT(table->s->blob_field);
+ DBUG_ASSERT(table->s->reclength == 0);
+ DBUG_ASSERT(table->s->fields == 0);
+ DBUG_ASSERT(table->s->blob_fields == 0);
+
+ const bool not_all_columns= !(m_select_options & TMP_TABLE_ALL_COLUMNS);
+ bool distinct_record_structure= m_distinct;
+ uint fieldnr= 0;
+ TABLE_SHARE *share= table->s;
+ Item **copy_func= param->items_to_copy;
+
+ MEM_ROOT *mem_root_save= thd->mem_root;
+ thd->mem_root= &table->mem_root;
List_iterator_fast<Item> li(fields);
Item *item;
- Field **tmp_from_field=from_field;
+ Field **tmp_from_field= m_from_field;
+ while (!m_with_cycle && (item= li++))
+ if (item->common_flags & IS_IN_WITH_CYCLE)
+ {
+ m_with_cycle= true;
+ /*
+ Following distinct_record_structure is (m_distinct || m_with_cycle)
+
+ Note: distinct_record_structure can be true even if m_distinct is
+ false, for example for incr_table in recursive CTE
+ (see select_union_recursive::create_result_table)
+ */
+ distinct_record_structure= true;
+ }
+ li.rewind();
+ uint uneven_delta= 0;
while ((item=li++))
{
+ current_counter= (((param->hidden_field_count < (fieldnr + 1)) &&
+ distinct_record_structure &&
+ (!m_with_cycle ||
+ (item->common_flags & IS_IN_WITH_CYCLE)))?
+ distinct :
+ other);
Item::Type type= item->type();
if (type == Item::COPY_STR_ITEM)
{
@@ -18325,98 +18463,85 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if ((item->real_type() == Item::SUBSELECT_ITEM) ||
(item->used_tables() & ~OUTER_REF_TABLE_BIT))
{
- /*
- Mark that the we have ignored an item that refers to a summary
- function. We need to know this if someone is going to use
- DISTINCT on the result.
- */
- param->using_outer_summary_function=1;
- continue;
+ /*
+ Mark that the we have ignored an item that refers to a summary
+ function. We need to know this if someone is going to use
+ DISTINCT on the result.
+ */
+ param->using_outer_summary_function=1;
+ continue;
}
}
- if (item->const_item() && (int) hidden_field_count <= 0)
+ if (item->const_item() &&
+ param->hidden_field_count < (fieldnr + 1))
continue; // We don't have to store this
}
- if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields)
+ if (type == Item::SUM_FUNC_ITEM && !m_group && !m_save_sum_fields)
{ /* Can't calc group yet */
Item_sum *sum_item= (Item_sum *) item;
sum_item->result_field=0;
- for (i=0 ; i < sum_item->get_arg_count() ; i++)
+ for (uint i= 0 ; i < sum_item->get_arg_count() ; i++)
{
- Item *arg= sum_item->get_arg(i);
- if (!arg->const_item())
- {
+ Item *arg= sum_item->get_arg(i);
+ if (!arg->const_item())
+ {
Item *tmp_item;
Field *new_field=
create_tmp_field(table, arg, &copy_func,
- tmp_from_field, &default_field[fieldnr],
- group != 0,not_all_columns,
- distinct, false);
- if (!new_field)
- goto err; // Should be OOM
- DBUG_ASSERT(!new_field->field_name.str || strlen(new_field->field_name.str) == new_field->field_name.length);
- tmp_from_field++;
- reclength+=new_field->pack_length();
- if (new_field->flags & BLOB_FLAG)
- {
- *blob_field++= fieldnr;
- blob_count++;
- }
- if (new_field->type() == MYSQL_TYPE_BIT)
- total_uneven_bit_length+= new_field->field_length & 7;
- *(reg_field++)= new_field;
- if (new_field->real_type() == MYSQL_TYPE_STRING ||
- new_field->real_type() == MYSQL_TYPE_VARCHAR)
- {
- string_count++;
- string_total_length+= new_field->pack_length();
- }
+ tmp_from_field, &m_default_field[fieldnr],
+ m_group != 0, not_all_columns,
+ distinct_record_structure , false);
+ if (!new_field)
+ goto err; // Should be OOM
+ tmp_from_field++;
+
thd->mem_root= mem_root_save;
if (!(tmp_item= new (thd->mem_root)
Item_temptable_field(thd, new_field)))
goto err;
arg= sum_item->set_arg(i, thd, tmp_item);
thd->mem_root= &table->mem_root;
- if (param->force_not_null_cols)
- {
- new_field->flags|= NOT_NULL_FLAG;
- new_field->null_ptr= NULL;
- }
- if (!(new_field->flags & NOT_NULL_FLAG))
+
+ uneven_delta= m_uneven_bit_length;
+ add_field(table, new_field, fieldnr++, param->force_not_null_cols);
+ uneven_delta= m_uneven_bit_length - uneven_delta;
+ m_field_count[current_counter]++;
+
+ if (!(new_field->flags & NOT_NULL_FLAG))
{
- null_count++;
/*
new_field->maybe_null() is still false, it will be
changed below. But we have to setup Item_field correctly
*/
arg->maybe_null=1;
}
- new_field->field_index= fieldnr++;
- }
+ if (current_counter == distinct)
+ new_field->flags|= FIELD_PART_OF_TMP_UNIQUE;
+ }
}
}
else
{
/*
- The last parameter to create_tmp_field_ex() is a bit tricky:
+ The last parameter to create_tmp_field_ex() is a bit tricky:
- We need to set it to 0 in union, to get fill_record() to modify the
- temporary table.
- We need to set it to 1 on multi-table-update and in select to
- write rows to the temporary table.
- We here distinguish between UNION and multi-table-updates by the fact
- that in the later case group is set to the row pointer.
+ We need to set it to 0 in union, to get fill_record() to modify the
+ temporary table.
+ We need to set it to 1 on multi-table-update and in select to
+ write rows to the temporary table.
+ We here distinguish between UNION and multi-table-updates by the fact
+ that in the later case group is set to the row pointer.
The test for item->marker == 4 is ensure we don't create a group-by
key over a bit field as heap tables can't handle that.
*/
- Field *new_field= (param->schema_table) ?
- item->create_field_for_schema(thd, table) :
+ DBUG_ASSERT(!param->schema_table);
+ Field *new_field=
create_tmp_field(table, item, &copy_func,
- tmp_from_field, &default_field[fieldnr],
- group != 0,
- !force_copy_fields &&
- (not_all_columns || group !=0),
+ tmp_from_field, &m_default_field[fieldnr],
+ m_group != 0,
+ !param->force_copy_fields &&
+ (not_all_columns || m_group !=0),
/*
If item->marker == 4 then we force create_tmp_field
to create a 64-bit longs for BIT fields because HEAP
@@ -18425,14 +18550,13 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
to be usable in this case too.
*/
item->marker == 4 || param->bit_fields_as_long,
- force_copy_fields);
+ param->force_copy_fields);
if (!new_field)
{
- if (unlikely(thd->is_fatal_error))
- goto err; // Got OOM
- continue; // Some kind of const item
+ if (unlikely(thd->is_fatal_error))
+ goto err; // Got OOM
+ continue; // Some kind of const item
}
- DBUG_ASSERT(!new_field->field_name.str || strlen(new_field->field_name.str) == new_field->field_name.length);
if (type == Item::SUM_FUNC_ITEM)
{
Item_sum *agg_item= (Item_sum *) item;
@@ -18459,82 +18583,81 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
agg_item->result_field= new_field;
}
tmp_from_field++;
- if (param->force_not_null_cols)
- {
- new_field->flags|= NOT_NULL_FLAG;
- new_field->null_ptr= NULL;
- }
- reclength+=new_field->pack_length();
- if (!(new_field->flags & NOT_NULL_FLAG))
- null_count++;
- if (new_field->type() == MYSQL_TYPE_BIT)
- total_uneven_bit_length+= new_field->field_length & 7;
- if (new_field->flags & BLOB_FLAG)
- {
- *blob_field++= fieldnr;
- blob_count++;
- }
- if (new_field->real_type() == MYSQL_TYPE_STRING ||
- new_field->real_type() == MYSQL_TYPE_VARCHAR)
- {
- string_count++;
- string_total_length+= new_field->pack_length();
- }
+ uneven_delta= m_uneven_bit_length;
+ add_field(table, new_field, fieldnr++, param->force_not_null_cols);
+ uneven_delta= m_uneven_bit_length - uneven_delta;
+ m_field_count[current_counter]++;
if (item->marker == 4 && item->maybe_null)
{
- group_null_items++;
- new_field->flags|= GROUP_FLAG;
+ m_group_null_items++;
+ new_field->flags|= GROUP_FLAG;
}
- new_field->field_index= fieldnr++;
- *(reg_field++)= new_field;
- }
- if (!--hidden_field_count)
- {
- /*
- This was the last hidden field; Remember how many hidden fields could
- have null
- */
- hidden_null_count=null_count;
- /*
- We need to update hidden_field_count as we may have stored group
- functions with constant arguments
- */
- param->hidden_field_count= fieldnr;
- null_count= 0;
- /*
- On last hidden field we store uneven bit length in
- hidden_uneven_bit_length and proceed calculation of
- uneven bits for visible fields into
- total_uneven_bit_length variable.
- */
- hidden_uneven_bit_length= total_uneven_bit_length;
- total_uneven_bit_length= 0;
+ if (current_counter == distinct)
+ new_field->flags|= FIELD_PART_OF_TMP_UNIQUE;
}
+ m_uneven_bit[current_counter]+= uneven_delta;
}
- DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field));
- DBUG_ASSERT(field_count >= (uint) (reg_field - table->field));
- field_count= fieldnr;
- *reg_field= 0;
- *blob_field= 0; // End marker
- share->fields= field_count;
+ DBUG_ASSERT(fieldnr == m_field_count[other] + m_field_count[distinct]);
+ DBUG_ASSERT(m_blob_count == m_blobs_count[other] + m_blobs_count[distinct]);
+ share->fields= fieldnr;
+ share->blob_fields= m_blob_count;
+ table->field[fieldnr]= 0; // End marker
+ share->blob_field[m_blob_count]= 0; // End marker
+ copy_func[0]= 0; // End marker
+ param->func_count= (uint) (copy_func - param->items_to_copy);
share->column_bitmap_size= bitmap_buffer_size(share->fields);
+ thd->mem_root= mem_root_save;
+ DBUG_RETURN(false);
+
+err:
+ thd->mem_root= mem_root_save;
+ DBUG_RETURN(true);
+}
+
+
+bool Create_tmp_table::finalize(THD *thd,
+ TABLE *table,
+ TMP_TABLE_PARAM *param,
+ bool do_not_open, bool keep_row_order)
+{
+ DBUG_ENTER("Create_tmp_table::finalize");
+ DBUG_ASSERT(table);
+
+ uint null_pack_length[2];
+ uint null_pack_base[2];
+ uint null_counter[2]= {0, 0};
+ uint whole_null_pack_length;
+ bool use_packed_rows= false;
+ bool save_abort_on_warning;
+ uchar *pos;
+ uchar *null_flags;
+ KEY *keyinfo;
+ TMP_ENGINE_COLUMNDEF *recinfo;
+ TABLE_SHARE *share= table->s;
+ Copy_field *copy= param->copy_field;
+ MEM_ROOT *mem_root_save= thd->mem_root;
+ thd->mem_root= &table->mem_root;
+
+ DBUG_ASSERT(m_alloced_field_count >= share->fields);
+ DBUG_ASSERT(m_alloced_field_count >= share->blob_fields);
+
/* If result table is small; use a heap */
/* future: storage engine selection can be made dynamic? */
- if (blob_count || using_unique_constraint
- || (thd->variables.big_tables && !(select_options & SELECT_SMALL_RESULT))
- || (select_options & TMP_TABLE_FORCE_MYISAM)
+ if (share->blob_fields || m_using_unique_constraint
+ || (thd->variables.big_tables && !(m_select_options & SELECT_SMALL_RESULT))
+ || (m_select_options & TMP_TABLE_FORCE_MYISAM)
|| thd->variables.tmp_memory_table_size == 0)
{
share->db_plugin= ha_lock_engine(0, TMP_ENGINE_HTON);
table->file= get_new_handler(share, &table->mem_root,
share->db_type());
- if (group &&
+ if (m_group &&
(param->group_parts > table->file->max_key_parts() ||
param->group_length > table->file->max_key_length()))
- using_unique_constraint= true;
+ m_using_unique_constraint= true;
}
else
{
@@ -18550,36 +18673,41 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
delete table->file;
goto err;
}
+ table->file->set_table(table);
- if (!using_unique_constraint)
- reclength+= group_null_items; // null flag is stored separately
+ if (!m_using_unique_constraint)
+ share->reclength+= m_group_null_items; // null flag is stored separately
- share->blob_fields= blob_count;
- if (blob_count == 0)
+ if (share->blob_fields == 0)
{
/* We need to ensure that first byte is not 0 for the delete link */
- if (param->hidden_field_count)
- hidden_null_count++;
+ if (m_field_count[other])
+ m_null_count[other]++;
else
- null_count++;
- }
- hidden_null_pack_length= (hidden_null_count + 7 +
- hidden_uneven_bit_length) / 8;
- null_pack_length= (hidden_null_pack_length +
- (null_count + total_uneven_bit_length + 7) / 8);
- reclength+=null_pack_length;
- if (!reclength)
- reclength=1; // Dummy select
+ m_null_count[distinct]++;
+ }
+
+ null_pack_length[other]= (m_null_count[other] + 7 +
+ m_uneven_bit[other]) / 8;
+ null_pack_base[other]= 0;
+ null_pack_length[distinct]= (m_null_count[distinct] + 7 +
+ m_uneven_bit[distinct]) / 8;
+ null_pack_base[distinct]= null_pack_length[other];
+ whole_null_pack_length= null_pack_length[other] +
+ null_pack_length[distinct];
+ share->reclength+= whole_null_pack_length;
+ if (!share->reclength)
+ share->reclength= 1; // Dummy select
+ share->stored_rec_length= share->reclength;
/* Use packed rows if there is blobs or a lot of space to gain */
- if (blob_count ||
- (string_total_length >= STRING_TOTAL_LENGTH_TO_PACK_ROWS &&
- (reclength / string_total_length <= RATIO_TO_PACK_ROWS ||
- string_total_length / string_count >= AVG_STRING_LENGTH_TO_PACK_ROWS)))
+ if (share->blob_fields ||
+ (string_total_length() >= STRING_TOTAL_LENGTH_TO_PACK_ROWS &&
+ (share->reclength / string_total_length() <= RATIO_TO_PACK_ROWS ||
+ string_total_length() / string_count() >= AVG_STRING_LENGTH_TO_PACK_ROWS)))
use_packed_rows= 1;
- share->reclength= reclength;
{
- uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1);
+ uint alloc_length= ALIGN_SIZE(share->reclength + MI_UNIQUE_HASH_LENGTH+1);
share->rec_buff_length= alloc_length;
if (!(table->record[0]= (uchar*)
alloc_root(&table->mem_root, alloc_length*3)))
@@ -18587,55 +18715,63 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
table->record[1]= table->record[0]+alloc_length;
share->default_values= table->record[1]+alloc_length;
}
- copy_func[0]=0; // End marker
- param->func_count= (uint)(copy_func - param->items_to_copy);
- setup_tmp_table_column_bitmaps(table, bitmaps);
+ setup_tmp_table_column_bitmaps(table, m_bitmaps);
recinfo=param->start_recinfo;
null_flags=(uchar*) table->record[0];
- pos=table->record[0]+ null_pack_length;
- if (null_pack_length)
+ pos=table->record[0]+ whole_null_pack_length;
+ if (whole_null_pack_length)
{
bzero((uchar*) recinfo,sizeof(*recinfo));
recinfo->type=FIELD_NORMAL;
- recinfo->length=null_pack_length;
+ recinfo->length= whole_null_pack_length;
recinfo++;
- bfill(null_flags,null_pack_length,255); // Set null fields
+ bfill(null_flags, whole_null_pack_length, 255); // Set null fields
table->null_flags= (uchar*) table->record[0];
- share->null_fields= null_count+ hidden_null_count;
- share->null_bytes= share->null_bytes_for_compare= null_pack_length;
+ share->null_fields= m_null_count[other] + m_null_count[distinct];
+ share->null_bytes= share->null_bytes_for_compare= whole_null_pack_length;
+ }
+
+ if (share->blob_fields == 0)
+ {
+ null_counter[(m_field_count[other] ? other : distinct)]++;
}
- null_count= (blob_count == 0) ? 1 : 0;
- hidden_field_count=param->hidden_field_count;
/* Protect against warnings in field_conv() in the next loop*/
save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= 0;
- for (i=0,reg_field=table->field; i < field_count; i++,reg_field++,recinfo++)
+ for (uint i= 0; i < share->fields; i++, recinfo++)
{
- Field *field= *reg_field;
+ Field *field= table->field[i];
uint length;
bzero((uchar*) recinfo,sizeof(*recinfo));
+ current_counter= ((field->flags & FIELD_PART_OF_TMP_UNIQUE) ?
+ distinct :
+ other);
+
if (!(field->flags & NOT_NULL_FLAG))
{
- recinfo->null_bit= (uint8)1 << (null_count & 7);
- recinfo->null_pos= null_count/8;
- field->move_field(pos,null_flags+null_count/8,
- (uint8)1 << (null_count & 7));
- null_count++;
+
+ recinfo->null_bit= (uint8)1 << (null_counter[current_counter] & 7);
+ recinfo->null_pos= (null_pack_base[current_counter] +
+ null_counter[current_counter]/8);
+ field->move_field(pos, null_flags + recinfo->null_pos, recinfo->null_bit);
+ null_counter[current_counter]++;
}
else
field->move_field(pos,(uchar*) 0,0);
if (field->type() == MYSQL_TYPE_BIT)
{
/* We have to reserve place for extra bits among null bits */
- ((Field_bit*) field)->set_bit_ptr(null_flags + null_count / 8,
- null_count & 7);
- null_count+= (field->field_length & 7);
+ ((Field_bit*) field)->set_bit_ptr(null_flags +
+ null_pack_base[current_counter] +
+ null_counter[current_counter]/8,
+ null_counter[current_counter] & 7);
+ null_counter[current_counter]+= (field->field_length & 7);
}
field->reset();
@@ -18643,14 +18779,14 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
Test if there is a default field value. The test for ->ptr is to skip
'offset' fields generated by initialize_tables
*/
- if (default_field[i] && default_field[i]->ptr)
+ if (m_default_field[i] && m_default_field[i]->ptr)
{
/*
default_field[i] is set only in the cases when 'field' can
inherit the default value that is defined for the field referred
by the Item_field object from which 'field' has been created.
*/
- Field *orig_field= default_field[i];
+ Field *orig_field= m_default_field[i];
/* Get the value from default_values */
if (orig_field->is_null_in_record(orig_field->table->s->default_values))
field->set_null();
@@ -18669,9 +18805,9 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
}
}
- if (from_field[i])
+ if (m_from_field[i])
{ /* Not a table Item */
- copy->set(field,from_field[i],save_sum_fields);
+ copy->set(field, m_from_field[i], m_save_sum_fields);
copy++;
}
length=field->pack_length_in_rec();
@@ -18679,19 +18815,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
/* Make entry for create table */
recinfo->length=length;
- if (field->flags & BLOB_FLAG)
- recinfo->type= FIELD_BLOB;
- else if (use_packed_rows &&
- field->real_type() == MYSQL_TYPE_STRING &&
- length >= MIN_STRING_LENGTH_TO_PACK_ROWS)
- recinfo->type= FIELD_SKIP_ENDSPACE;
- else if (field->real_type() == MYSQL_TYPE_VARCHAR)
- recinfo->type= FIELD_VARCHAR;
- else
- recinfo->type= FIELD_NORMAL;
-
- if (!--hidden_field_count)
- null_count=(null_count+7) & ~7; // move to next byte
+ recinfo->type= field->tmp_engine_column_type(use_packed_rows);
// fix table name in field entry
field->set_table_name(&table->alias);
@@ -18701,7 +18825,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
MEM_CHECK_DEFINED(table->record[0], table->s->reclength);
thd->abort_on_warning= save_abort_on_warning;
- param->copy_field_end=copy;
+ param->copy_field_end= copy;
param->recinfo= recinfo; // Pointer to after last field
store_record(table,s->default_values); // Make empty default record
@@ -18711,29 +18835,29 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->max_rows= (ha_rows) (((share->db_type() == heap_hton) ?
MY_MIN(thd->variables.tmp_memory_table_size,
thd->variables.max_heap_table_size) :
- thd->variables.tmp_memory_table_size) /
+ thd->variables.tmp_disk_table_size) /
share->reclength);
set_if_bigger(share->max_rows,1); // For dummy start options
/*
Push the LIMIT clause to the temporary table creation, so that we
materialize only up to 'rows_limit' records instead of all result records.
*/
- set_if_smaller(share->max_rows, rows_limit);
- param->end_write_records= rows_limit;
+ set_if_smaller(share->max_rows, m_rows_limit);
+ param->end_write_records= m_rows_limit;
keyinfo= param->keyinfo;
- if (group)
+ if (m_group)
{
DBUG_PRINT("info",("Creating group key in temporary table"));
- table->group=group; /* Table is grouped by key */
- param->group_buff=group_buff;
+ table->group= m_group; /* Table is grouped by key */
+ param->group_buff= m_group_buff;
share->keys=1;
- share->uniques= MY_TEST(using_unique_constraint);
+ share->uniques= MY_TEST(m_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->key_part= m_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->user_defined_key_parts= param->group_parts;
@@ -18745,29 +18869,29 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->name= group_key;
- ORDER *cur_group= group;
- for (; cur_group ; cur_group= cur_group->next, key_part_info++)
+ ORDER *cur_group= m_group;
+ for (; cur_group ; cur_group= cur_group->next, m_key_part_info++)
{
Field *field=(*cur_group->item)->get_tmp_table_field();
DBUG_ASSERT(field->table == table);
bool maybe_null=(*cur_group->item)->maybe_null;
- key_part_info->null_bit=0;
- key_part_info->field= field;
- key_part_info->fieldnr= field->field_index + 1;
- if (cur_group == group)
+ m_key_part_info->null_bit=0;
+ m_key_part_info->field= field;
+ m_key_part_info->fieldnr= field->field_index + 1;
+ if (cur_group == m_group)
field->key_start.set_bit(0);
- key_part_info->offset= field->offset(table->record[0]);
- key_part_info->length= (uint16) field->key_length();
- key_part_info->type= (uint8) field->key_type();
- key_part_info->key_type =
- ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
+ m_key_part_info->offset= field->offset(table->record[0]);
+ m_key_part_info->length= (uint16) field->key_length();
+ m_key_part_info->type= (uint8) field->key_type();
+ m_key_part_info->key_type =
+ ((ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_TEXT ||
+ (ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
+ (ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
0 : FIELDFLAG_BINARY;
- key_part_info->key_part_flag= 0;
- if (!using_unique_constraint)
+ m_key_part_info->key_part_flag= 0;
+ if (!m_using_unique_constraint)
{
- cur_group->buff=(char*) group_buff;
+ cur_group->buff=(char*) m_group_buff;
if (maybe_null && !field->null_bit)
{
@@ -18782,9 +18906,9 @@ 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 +
+ m_group_buff +
MY_TEST(maybe_null),
- key_part_info->length,
+ m_key_part_info->length,
field->null_ptr,
field->null_bit)))
goto err; /* purecov: inspected */
@@ -18798,26 +18922,29 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
The NULL flag is updated in 'end_update()' and 'end_write()'
*/
keyinfo->flags|= HA_NULL_ARE_EQUAL; // def. that NULL == NULL
- key_part_info->null_bit=field->null_bit;
- key_part_info->null_offset= (uint) (field->null_ptr -
+ m_key_part_info->null_bit=field->null_bit;
+ m_key_part_info->null_offset= (uint) (field->null_ptr -
(uchar*) table->record[0]);
cur_group->buff++; // Pointer to field data
- group_buff++; // Skipp null flag
+ m_group_buff++; // Skipp null flag
}
- group_buff+= cur_group->field->pack_length();
+ m_group_buff+= cur_group->field->pack_length();
}
- keyinfo->key_length+= key_part_info->length;
+ keyinfo->key_length+= m_key_part_info->length;
}
/*
Ensure we didn't overrun the group buffer. The < is only true when
some maybe_null fields was changed to be not null fields.
*/
- DBUG_ASSERT(using_unique_constraint ||
- group_buff <= param->group_buff + param->group_length);
+ DBUG_ASSERT(m_using_unique_constraint ||
+ m_group_buff <= param->group_buff + param->group_length);
}
- if (distinct && field_count != param->hidden_field_count)
+ if (m_distinct && (share->fields != param->hidden_field_count ||
+ m_with_cycle))
{
+ uint i;
+ Field **reg_field;
/*
Create an unique key or an unique constraint over all columns
that should be in the result. In the temporary table, there are
@@ -18826,7 +18953,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
DBUG_PRINT("info",("hidden_field_count: %d", param->hidden_field_count));
- if (blob_count)
+ if (m_blobs_count[distinct])
{
/*
Special mode for index creation in MyISAM used to support unique
@@ -18835,23 +18962,21 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
*/
share->uniques= 1;
}
- null_pack_length-=hidden_null_pack_length;
- keyinfo->user_defined_key_parts=
- ((field_count-param->hidden_field_count)+
- (share->uniques ? MY_TEST(null_pack_length) : 0));
+ keyinfo->user_defined_key_parts= m_field_count[distinct] +
+ (share->uniques ? MY_TEST(null_pack_length[distinct]) : 0);
keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->usable_key_parts= keyinfo->user_defined_key_parts;
table->distinct= 1;
share->keys= 1;
- if (!(key_part_info= (KEY_PART_INFO*)
+ if (!(m_key_part_info= (KEY_PART_INFO*)
alloc_root(&table->mem_root,
keyinfo->user_defined_key_parts * sizeof(KEY_PART_INFO))))
goto err;
- bzero((void*) key_part_info, keyinfo->user_defined_key_parts * sizeof(KEY_PART_INFO));
+ bzero((void*) m_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;
- keyinfo->key_part=key_part_info;
+ keyinfo->key_part= m_key_part_info;
keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY;
keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->key_length= 0; // Will compute the sum of the parts below.
@@ -18881,41 +19006,43 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
blobs can distinguish NULL from 0. This extra field is not needed
when we do not use UNIQUE indexes for blobs.
*/
- if (null_pack_length && share->uniques)
+ if (null_pack_length[distinct] && share->uniques)
{
- key_part_info->null_bit=0;
- key_part_info->offset=hidden_null_pack_length;
- key_part_info->length=null_pack_length;
- key_part_info->field= new Field_string(table->record[0],
- (uint32) key_part_info->length,
+ m_key_part_info->null_bit=0;
+ m_key_part_info->offset= null_pack_base[distinct];
+ m_key_part_info->length= null_pack_length[distinct];
+ m_key_part_info->field= new Field_string(table->record[0],
+ (uint32) m_key_part_info->length,
(uchar*) 0,
(uint) 0,
Field::NONE,
&null_clex_str, &my_charset_bin);
- if (!key_part_info->field)
+ if (!m_key_part_info->field)
goto err;
- key_part_info->field->init(table);
- key_part_info->key_type=FIELDFLAG_BINARY;
- key_part_info->type= HA_KEYTYPE_BINARY;
- key_part_info->fieldnr= key_part_info->field->field_index + 1;
- key_part_info++;
+ m_key_part_info->field->init(table);
+ m_key_part_info->key_type=FIELDFLAG_BINARY;
+ m_key_part_info->type= HA_KEYTYPE_BINARY;
+ m_key_part_info->fieldnr= m_key_part_info->field->field_index + 1;
+ m_key_part_info++;
}
/* Create a distinct key over the columns we are going to return */
- for (i=param->hidden_field_count, reg_field=table->field + i ;
- i < field_count;
- i++, reg_field++, key_part_info++)
+ for (i= param->hidden_field_count, reg_field= table->field + i ;
+ i < share->fields;
+ i++, reg_field++)
{
- key_part_info->field= *reg_field;
+ if (!((*reg_field)->flags & FIELD_PART_OF_TMP_UNIQUE))
+ continue;
+ m_key_part_info->field= *reg_field;
(*reg_field)->flags |= PART_KEY_FLAG;
- if (key_part_info == keyinfo->key_part)
+ if (m_key_part_info == keyinfo->key_part)
(*reg_field)->key_start.set_bit(0);
- key_part_info->null_bit= (*reg_field)->null_bit;
- key_part_info->null_offset= (uint) ((*reg_field)->null_ptr -
+ m_key_part_info->null_bit= (*reg_field)->null_bit;
+ m_key_part_info->null_offset= (uint) ((*reg_field)->null_ptr -
(uchar*) table->record[0]);
- key_part_info->offset= (*reg_field)->offset(table->record[0]);
- key_part_info->length= (uint16) (*reg_field)->pack_length();
- key_part_info->fieldnr= (*reg_field)->field_index + 1;
+ m_key_part_info->offset= (*reg_field)->offset(table->record[0]);
+ m_key_part_info->length= (uint16) (*reg_field)->pack_length();
+ m_key_part_info->fieldnr= (*reg_field)->field_index + 1;
/* TODO:
The below method of computing the key format length of the
key part is a copy/paste from opt_range.cc, and table.cc.
@@ -18924,34 +19051,25 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
methods is supposed to compute the same length. If so, it
might be reused.
*/
- key_part_info->store_length= key_part_info->length;
+ m_key_part_info->store_length= m_key_part_info->length;
if ((*reg_field)->real_maybe_null())
{
- key_part_info->store_length+= HA_KEY_NULL_LENGTH;
- key_part_info->key_part_flag |= HA_NULL_PART;
- }
- if ((*reg_field)->type() == MYSQL_TYPE_BLOB ||
- (*reg_field)->real_type() == MYSQL_TYPE_VARCHAR ||
- (*reg_field)->type() == MYSQL_TYPE_GEOMETRY)
- {
- if ((*reg_field)->type() == MYSQL_TYPE_BLOB ||
- (*reg_field)->type() == MYSQL_TYPE_GEOMETRY)
- key_part_info->key_part_flag|= HA_BLOB_PART;
- else
- key_part_info->key_part_flag|= HA_VAR_LENGTH_PART;
-
- key_part_info->store_length+=HA_KEY_BLOB_LENGTH;
+ m_key_part_info->store_length+= HA_KEY_NULL_LENGTH;
+ m_key_part_info->key_part_flag |= HA_NULL_PART;
}
+ m_key_part_info->key_part_flag|= (*reg_field)->key_part_flag();
+ m_key_part_info->store_length+= (*reg_field)->key_part_length_bytes();
+ keyinfo->key_length+= m_key_part_info->store_length;
- keyinfo->key_length+= key_part_info->store_length;
-
- key_part_info->type= (uint8) (*reg_field)->key_type();
- key_part_info->key_type =
- ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
- (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
+ m_key_part_info->type= (uint8) (*reg_field)->key_type();
+ m_key_part_info->key_type =
+ ((ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_TEXT ||
+ (ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
+ (ha_base_keytype) m_key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
0 : FIELDFLAG_BINARY;
+
+ m_key_part_info++;
}
}
@@ -18965,7 +19083,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (!do_not_open)
{
if (instantiate_tmp_table(table, param->keyinfo, param->start_recinfo,
- &param->recinfo, select_options))
+ &param->recinfo, m_select_options))
goto err;
}
@@ -18975,14 +19093,115 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
thd->mem_root= mem_root_save;
- DBUG_RETURN(table);
+ DBUG_RETURN(false);
err:
thd->mem_root= mem_root_save;
- free_tmp_table(thd,table); /* purecov: inspected */
- if (temp_pool_slot != MY_BIT_NONE)
- bitmap_lock_clear_bit(&temp_pool, temp_pool_slot);
- DBUG_RETURN(NULL); /* purecov: inspected */
+ DBUG_RETURN(true); /* purecov: inspected */
+}
+
+
+bool Create_tmp_table::add_schema_fields(THD *thd, TABLE *table,
+ TMP_TABLE_PARAM *param,
+ const ST_SCHEMA_TABLE &schema_table)
+{
+ DBUG_ENTER("Create_tmp_table::add_schema_fields");
+ DBUG_ASSERT(table);
+ DBUG_ASSERT(table->field);
+ DBUG_ASSERT(table->s->blob_field);
+ DBUG_ASSERT(table->s->reclength == 0);
+ DBUG_ASSERT(table->s->fields == 0);
+ DBUG_ASSERT(table->s->blob_fields == 0);
+
+ TABLE_SHARE *share= table->s;
+ ST_FIELD_INFO *defs= schema_table.fields_info;
+ uint fieldnr;
+ MEM_ROOT *mem_root_save= thd->mem_root;
+ thd->mem_root= &table->mem_root;
+
+ for (fieldnr= 0; !defs[fieldnr].end_marker(); fieldnr++)
+ {
+ const ST_FIELD_INFO &def= defs[fieldnr];
+ Record_addr addr(def.nullable());
+ const Type_handler *h= def.type_handler();
+ Field *field= h->make_schema_field(&table->mem_root, table, addr, def);
+ if (!field)
+ {
+ thd->mem_root= mem_root_save;
+ DBUG_RETURN(true); // EOM
+ }
+ field->init(table);
+ switch (def.def()) {
+ case DEFAULT_NONE:
+ field->flags|= NO_DEFAULT_VALUE_FLAG;
+ break;
+ case DEFAULT_TYPE_IMPLICIT:
+ break;
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+ add_field(table, field, fieldnr, param->force_not_null_cols);
+ }
+
+ share->fields= fieldnr;
+ share->blob_fields= m_blob_count;
+ table->field[fieldnr]= 0; // End marker
+ share->blob_field[m_blob_count]= 0; // End marker
+ param->func_count= 0;
+ share->column_bitmap_size= bitmap_buffer_size(share->fields);
+
+ thd->mem_root= mem_root_save;
+ DBUG_RETURN(false);
+}
+
+
+void Create_tmp_table::cleanup_on_failure(THD *thd, TABLE *table)
+{
+ if (table)
+ free_tmp_table(thd, table);
+ if (m_temp_pool_slot != MY_BIT_NONE)
+ bitmap_lock_clear_bit(&temp_pool, m_temp_pool_slot);
+}
+
+
+TABLE *create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
+ ORDER *group, bool distinct, bool save_sum_fields,
+ ulonglong select_options, ha_rows rows_limit,
+ const LEX_CSTRING *table_alias, bool do_not_open,
+ bool keep_row_order)
+{
+ TABLE *table;
+ Create_tmp_table maker(param, group,
+ distinct, save_sum_fields, select_options, rows_limit);
+ if (!(table= maker.start(thd, param, table_alias)) ||
+ maker.add_fields(thd, table, param, fields) ||
+ maker.finalize(thd, table, param, do_not_open, keep_row_order))
+ {
+ maker.cleanup_on_failure(thd, table);
+ return NULL;
+ }
+ return table;
+}
+
+
+TABLE *create_tmp_table_for_schema(THD *thd, TMP_TABLE_PARAM *param,
+ const ST_SCHEMA_TABLE &schema_table,
+ longlong select_options,
+ const LEX_CSTRING &table_alias,
+ bool do_not_open, bool keep_row_order)
+{
+ TABLE *table;
+ Create_tmp_table maker(param, (ORDER *) NULL, false, false,
+ select_options, HA_POS_ERROR);
+ if (!(table= maker.start(thd, param, &table_alias)) ||
+ maker.add_schema_fields(thd, table, param, schema_table) ||
+ maker.finalize(thd, table, param, do_not_open, keep_row_order))
+ {
+ maker.cleanup_on_failure(thd, table);
+ return NULL;
+ }
+ return table;
}
@@ -19100,10 +19319,8 @@ bool Virtual_tmp_table::sp_find_field_by_name(uint *idx,
for (uint i= 0; (f= field[i]); i++)
{
// Use the same comparison style with sp_context::find_variable()
- if (!my_strnncoll(system_charset_info,
- (const uchar *) f->field_name.str,
- f->field_name.length,
- (const uchar *) name.str, name.length))
+ if (!system_charset_info->strnncoll(f->field_name.str, f->field_name.length,
+ name.str, name.length))
{
*idx= i;
return false;
@@ -19578,7 +19795,7 @@ create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
new_table.s= &share;
new_table.s->db_plugin= ha_lock_engine(thd, TMP_ENGINE_HTON);
if (unlikely(!(new_table.file= get_new_handler(&share, &new_table.mem_root,
- new_table.s->db_type()))))
+ TMP_ENGINE_HTON))))
DBUG_RETURN(1); // End of memory
if (unlikely(new_table.file->set_ha_share_ref(&share.ha_share)))
@@ -19669,7 +19886,7 @@ err_killed:
(void) table->file->ha_rnd_end();
(void) new_table.file->ha_close();
err1:
- new_table.file->ha_delete_table(new_table.s->path.str);
+ TMP_ENGINE_HTON->drop_table(TMP_ENGINE_HTON, new_table.s->path.str);
err2:
delete new_table.file;
thd_proc_info(thd, save_proc_info);
@@ -19692,16 +19909,12 @@ free_tmp_table(THD *thd, TABLE *entry)
if (entry->file && entry->is_created())
{
+ DBUG_ASSERT(entry->db_stat);
entry->file->ha_index_or_rnd_end();
- if (entry->db_stat)
- {
- entry->file->info(HA_STATUS_VARIABLE);
- thd->tmp_tables_size+= (entry->file->stats.data_file_length +
- entry->file->stats.index_file_length);
- entry->file->ha_drop_table(entry->s->path.str);
- }
- else
- entry->file->ha_delete_table(entry->s->path.str);
+ entry->file->info(HA_STATUS_VARIABLE);
+ thd->tmp_tables_size+= (entry->file->stats.data_file_length +
+ entry->file->stats.index_file_length);
+ entry->file->ha_drop_table(entry->s->path.str);
delete entry->file;
}
@@ -19860,8 +20073,7 @@ do_select(JOIN *join, Procedure *procedure)
if (join->pushdown_query->store_data_in_temp_table)
{
- JOIN_TAB *last_tab= join->join_tab + join->table_count -
- join->exec_join_tab_cnt();
+ JOIN_TAB *last_tab= join->join_tab + join->exec_join_tab_cnt();
last_tab->next_select= end_send;
enum_nested_loop_state state= last_tab->aggr->end_send();
@@ -19896,7 +20108,7 @@ do_select(JOIN *join, Procedure *procedure)
// HAVING will be checked by end_select
error= (*end_select)(join, 0, 0);
if (error >= NESTED_LOOP_OK)
- error= (*end_select)(join, 0, 1);
+ error= (*end_select)(join, 0, 1);
/*
If we don't go through evaluate_join_record(), do the counting
@@ -19912,7 +20124,8 @@ do_select(JOIN *join, Procedure *procedure)
{
List<Item> *columns_list= (procedure ? &join->procedure_fields_list :
join->fields);
- rc= join->result->send_data(*columns_list) > 0;
+ rc= join->result->send_data_with_check(*columns_list,
+ join->unit, 0) > 0;
}
}
/*
@@ -20387,13 +20600,17 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
}
join->thd->get_stmt_da()->reset_current_row_for_warning();
- if (rc != NESTED_LOOP_NO_MORE_ROWS &&
+ if (rc != NESTED_LOOP_NO_MORE_ROWS &&
(rc= join_tab_execution_startup(join_tab)) < 0)
DBUG_RETURN(rc);
if (join_tab->loosescan_match_tab)
join_tab->loosescan_match_tab->found_match= FALSE;
+ const bool pfs_batch_update= join_tab->pfs_batch_update(join);
+ if (pfs_batch_update)
+ join_tab->table->file->start_psi_batch_mode();
+
if (rc != NESTED_LOOP_NO_MORE_ROWS)
{
error= (*join_tab->read_first_record)(join_tab);
@@ -20445,6 +20662,9 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
join_tab->last_inner && !join_tab->found)
rc= evaluate_null_complemented_join_record(join, join_tab);
+ if (pfs_batch_update)
+ join_tab->table->file->end_psi_batch_mode();
+
if (rc == NESTED_LOOP_NO_MORE_ROWS)
rc= NESTED_LOOP_OK;
DBUG_RETURN(rc);
@@ -20476,8 +20696,8 @@ 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 alias %s",
+ ("evaluate_join_record join: %p join_tab: %p "
+ "cond: %p abort: %d alias %s",
join, join_tab, select_cond, error,
join_tab->table->alias.ptr()));
@@ -21049,7 +21269,7 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
if (tab && tab->bush_children)
{
TABLE_LIST *emb_sj_nest= tab->bush_children->start->emb_sj_nest;
- emb_sj_nest->sj_subq_pred->left_expr->bring_value();
+ emb_sj_nest->sj_subq_pred->left_exp()->bring_value();
}
/* TODO: Why don't we do "Late NULLs Filtering" here? */
@@ -21588,7 +21808,9 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
int error;
/* result < 0 if row was not accepted and should not be counted */
- if (unlikely((error= join->result->send_data(*fields))))
+ if (unlikely((error= join->result->send_data_with_check(*fields,
+ join->unit,
+ join->send_records))))
{
if (error > 0)
DBUG_RETURN(NESTED_LOOP_ERROR);
@@ -21598,7 +21820,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
++join->send_records;
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
!join->do_send_rows)
{
/*
@@ -21616,7 +21838,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
}
}
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
@@ -21736,7 +21958,9 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (join->do_send_rows)
{
- error=join->result->send_data(*fields);
+ error= join->result->send_data_with_check(*fields,
+ join->unit,
+ join->send_records);
if (unlikely(error < 0))
{
/* Duplicate row, don't count */
@@ -21757,13 +21981,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
}
else if (join->send_records >= join->fetch_limit)
{
@@ -21848,7 +22072,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
}
}
}
@@ -22714,13 +22938,12 @@ static int test_if_order_by_key(JOIN *join,
if (have_pk_suffix && reverse == -1)
{
uint pk_parts= table->key_info[pk].user_defined_key_parts;
- if (!(table->file->index_flags(pk, pk_parts, 1) & HA_READ_PREV))
+ if (!(table->file->index_flags(pk, pk_parts-1, 1) & HA_READ_PREV))
reverse= 0; // Index can't be used
}
ok:
- if (used_key_parts != NULL)
- *used_key_parts= key_parts;
+ *used_key_parts= key_parts;
DBUG_RETURN(reverse);
}
@@ -22814,12 +23037,13 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
*/
for (nr= 0 ; nr < table->s->keys ; nr++)
{
+ uint not_used;
if (usable_keys->is_set(nr) &&
table->key_info[nr].key_length < min_length &&
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(NULL, order, table, nr))
+ test_if_order_by_key(NULL, order, table, nr, &not_used))
{
min_length= table->key_info[nr].key_length;
best= nr;
@@ -23183,9 +23407,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
Otherwise, construct a ref access (todo: it's not clear what is the
win in using ref access when we could use quick select also?)
*/
- if ((table->quick_keys.is_set(new_ref_key) &&
- table->quick_key_parts[new_ref_key] > ref_key_parts) ||
- !(tab->ref.key >= 0))
+ if ((table->opt_range_keys.is_set(new_ref_key) &&
+ table->opt_range[new_ref_key].key_parts > ref_key_parts) ||
+ !(tab->ref.key >= 0))
{
/*
The range optimizer constructed QUICK_RANGE for ref_key, and
@@ -23210,8 +23434,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
(tab->join->select_options &
OPTION_FOUND_ROWS) ?
HA_POS_ERROR :
- tab->join->unit->select_limit_cnt,TRUE,
- TRUE, FALSE, FALSE) <= 0;
+ tab->join->unit->
+ lim.get_select_limit(),
+ TRUE, TRUE, FALSE, FALSE) <= 0;
if (res)
{
select->cond= save_cond;
@@ -23287,7 +23512,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
goto use_filesort;
if (select && // psergey: why doesn't this use a quick?
- table->quick_keys.is_set(best_key) && best_key != ref_key)
+ table->opt_range_keys.is_set(best_key) && best_key != ref_key)
{
key_map tmp_map;
tmp_map.clear_all(); // Force the creation of quick select
@@ -23312,7 +23537,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
select->test_quick_select(join->thd, tmp_map, 0,
join->select_options & OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt,
+ join->unit->lim.get_select_limit(),
TRUE, FALSE, FALSE, FALSE);
if (cond_saved)
@@ -23369,15 +23594,6 @@ check_reverse_order:
bool quick_created=
(select && select->quick && select->quick!=save_quick);
- /*
- If ref_key used index tree reading only ('Using index' in EXPLAIN),
- and best_key doesn't, then revert the decision.
- */
- if (table->covering_keys.is_set(best_key))
- table->file->ha_start_keyread(best_key);
- else
- table->file->ha_end_keyread();
-
if (!quick_created)
{
if (select) // Throw any existing quick select
@@ -23393,8 +23609,7 @@ check_reverse_order:
if the table is accessed by the primary key
*/
if (tab->rowid_filter &&
- tab->index == table->s->primary_key &&
- table->file->primary_key_is_clustered())
+ table->file->is_clustering_key(tab->index))
{
tab->range_rowid_filter_info= 0;
delete tab->rowid_filter;
@@ -23419,7 +23634,6 @@ check_reverse_order:
tab->ref.key_parts= 0;
if (select_limit < table->stat_records())
tab->limit= select_limit;
- table->file->ha_end_keyread();
}
}
else if (tab->type != JT_ALL || tab->select->quick)
@@ -23761,7 +23975,7 @@ JOIN_TAB::remove_duplicates()
if (!field_count && !(join->select_options & OPTION_FOUND_ROWS) && !having)
{ // only const items with no OPTION_FOUND_ROWS
- join->unit->select_limit_cnt= 1; // Only send first row
+ join->unit->lim.set_single_row(); // Only send first row
DBUG_RETURN(false);
}
@@ -23898,21 +24112,21 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
Field **ptr;
DBUG_ENTER("remove_dup_with_hash_index");
- if (unlikely(!my_multi_malloc(MYF(MY_WME),
- &key_buffer,
- (uint) ((key_length + extra_length) *
- (long) file->stats.records),
- &field_lengths,
- (uint) (field_count*sizeof(*field_lengths)),
- NullS)))
+ if (!my_multi_malloc(key_memory_hash_index_key_buffer, MYF(MY_WME),
+ &key_buffer,
+ (uint) ((key_length + extra_length) *
+ (long) file->stats.records),
+ &field_lengths,
+ (uint) (field_count*sizeof(*field_lengths)),
+ NullS))
DBUG_RETURN(1);
for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
(*field_length++)= (*ptr)->sort_length();
- if (unlikely(my_hash_init(&hash, &my_charset_bin,
- (uint) file->stats.records, 0,
- key_length, (my_hash_get_key) 0, 0, 0)))
+ if (my_hash_init(key_memory_hash_index_key_buffer, &hash, &my_charset_bin,
+ (uint) file->stats.records, 0, key_length,
+ (my_hash_get_key) 0, 0, 0))
{
my_free(key_buffer);
DBUG_RETURN(1);
@@ -23948,7 +24162,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
field_length=field_lengths;
for (ptr= first_field ; *ptr ; ptr++)
{
- (*ptr)->make_sort_key(key_pos, *field_length);
+ (*ptr)->make_sort_key_part(key_pos, *field_length);
key_pos+= (*ptr)->maybe_null() + *field_length++;
}
/* Check if it exists before */
@@ -25590,7 +25804,7 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select)
****************************************************************************/
/**
- Replace occurences of group by fields in an expression by ref items.
+ Replace occurrences of group by fields in an expression by ref items.
The function replaces occurrences of group by fields in expr
by ref objects for these fields unless they are under aggregate
@@ -25651,8 +25865,9 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list,
{
Item *new_item;
if (!(new_item= new (thd->mem_root) Item_ref(thd, context,
- group_tmp->item, 0,
- &item->name)))
+ group_tmp->item,
+ null_clex_str,
+ item->name)))
return 1; // fatal_error is set
thd->change_item_tree(arg, new_item);
arg_changed= TRUE;
@@ -25977,8 +26192,9 @@ int JOIN::rollup_send_data(uint idx)
copy_ref_ptr_array(ref_ptrs, rollup.ref_pointer_arrays[i]);
if ((!having || having->val_int()))
{
- if (send_records < unit->select_limit_cnt && do_send_rows &&
- (res= result->send_data(rollup.fields[i])) > 0)
+ if (send_records < unit->lim.get_select_limit() && do_send_rows &&
+ (res= result->send_data_with_check(rollup.fields[i],
+ unit, send_records)) > 0)
return 1;
if (!res)
send_records++;
@@ -26210,8 +26426,10 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
/* Enable the table access time tracker only for "ANALYZE stmt" */
if (thd->lex->analyze_stmt)
+ {
table->file->set_time_tracker(&eta->op_tracker);
-
+ eta->op_tracker.my_gap_tracker = &eta->extra_time_tracker;
+ }
/* No need to save id and select_type here, they are kept in Explain_select */
/* table */
@@ -26391,16 +26609,14 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
{
IS_table_read_plan *is_table_read_plan= table_list->is_table_read_plan;
- const char *tmp_buff;
- int f_idx;
StringBuffer<64> key_name_buf;
if (is_table_read_plan->trivial_show_command ||
is_table_read_plan->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;
- key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
+ int f_idx= table_list->schema_table->idx_field1;
+ LEX_CSTRING tmp= table_list->schema_table->fields_info[f_idx].name();
+ key_name_buf.append(tmp, cs);
}
if (is_table_read_plan->trivial_show_command ||
is_table_read_plan->has_table_lookup_value())
@@ -26409,9 +26625,9 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
is_table_read_plan->has_db_lookup_value())
key_name_buf.append(',');
- f_idx= table_list->schema_table->idx_field2;
- tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
- key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
+ int f_idx= table_list->schema_table->idx_field2;
+ LEX_CSTRING tmp= table_list->schema_table->fields_info[f_idx].name();
+ key_name_buf.append(tmp, cs);
}
if (key_name_buf.length())
@@ -26889,28 +27105,11 @@ int JOIN::save_explain_data_intern(Explain_query *output,
- this derived table was pushed down ("PUSHED DERIVED")
*/
if (!select_lex->pushdown_select && select_lex->type != pushed_derived_text)
- {
for (SELECT_LEX_UNIT *tmp_unit= join->select_lex->first_inner_unit();
tmp_unit;
tmp_unit= tmp_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
- (3) they are not hanging CTEs (they are needed for execution)
- */
- if (!(tmp_unit->item && tmp_unit->item->eliminated) && // (1)
- (!tmp_unit->derived ||
- tmp_unit->derived->is_materialized_derived()) && // (2)
- !(tmp_unit->with_element &&
- (!tmp_unit->derived || !tmp_unit->derived->derived_result))) // (3)
- {
+ if (tmp_unit->explainable())
explain->add_child(tmp_unit->first_select()->select_number);
- }
- }
- }
if (select_lex->is_top_level_node())
output->query_plan_ready();
@@ -26972,16 +27171,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
DBUG_ASSERT(ref == unit->item);
}
- /*
- Save plans for child subqueries, when
- (1) they are not parts of eliminated WHERE/ON clauses.
- (2) they are not VIEWs that were "merged for INSERT".
- (3) they are not hanging CTEs (they are needed for execution)
- */
- if (!(unit->item && unit->item->eliminated) && // (1)
- !(unit->derived && unit->derived->merged_for_insert) && // (2)
- !(unit->with_element &&
- (!unit->derived || !unit->derived->derived_result))) // (3)
+ if (unit->explainable())
{
if (mysql_explain_union(thd, unit, result))
DBUG_VOID_RETURN;
@@ -27023,15 +27213,11 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
{
thd->lex->current_select= first;
unit->set_limit(unit->global_parameters());
- res= mysql_select(thd,
- first->table_list.first,
- first->with_wild, first->item_list,
+ res= mysql_select(thd, first->table_list.first, first->item_list,
first->where,
first->order_list.elements + first->group_list.elements,
- first->order_list.first,
- first->group_list.first,
- first->having,
- thd->lex->proc_list.first,
+ first->order_list.first, first->group_list.first,
+ first->having, thd->lex->proc_list.first,
first->options | thd->variables.option_bits | SELECT_DESCRIBE,
result, unit, first);
}
@@ -27238,7 +27424,7 @@ Index_hint::print(THD *thd, String *str)
str->append (STRING_WITH_LEN(" ("));
if (key_name.length)
{
- if (thd && !my_strnncoll(system_charset_info,
+ if (thd && !system_charset_info->strnncoll(
(const uchar *)key_name.str, key_name.length,
(const uchar *)primary_key_name,
strlen(primary_key_name)))
@@ -27488,7 +27674,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
else
str->append(',');
- if (is_subquery_function() && item->is_autogenerated_name)
+ if (is_subquery_function() && item->is_autogenerated_name())
{
/*
Do not print auto-generated aliases in subqueries. It has no purpose
@@ -27784,8 +27970,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,
- MYF(MY_THREAD_SPECIFIC)))
+ my_init_dynamic_array(thd->mem_root->m_psi_key, &keyuse, sizeof(KEYUSE),
+ 20, 64, MYF(MY_THREAD_SPECIFIC)))
{
delete_dynamic(&added_keyuse);
return REOPT_ERROR;
@@ -27902,14 +28088,14 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
We need to adjust the estimates if we had a quick select (or ref(const)) on
index keynr.
*/
- if (table->quick_keys.is_set(keynr))
+ if (table->opt_range_keys.is_set(keynr))
{
/*
Start from quick select's rows and cost. These are always cheaper than
full index scan/cost.
*/
- double best_rows= (double)table->quick_rows[keynr];
- double best_cost= (double)table->quick_costs[keynr];
+ double best_rows= (double) table->opt_range[keynr].rows;
+ double best_cost= (double) table->opt_range[keynr].cost;
/*
Check if ref(const) access was possible on this index.
@@ -27936,21 +28122,16 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
2. ref(const) uses fewer key parts, becasue there is a
range_cond(key_part+1).
*/
- if (kp == table->quick_key_parts[keynr])
- ref_rows= table->quick_rows[keynr];
+ if (kp == table->opt_range[keynr].key_parts)
+ ref_rows= table->opt_range[keynr].rows;
else
ref_rows= (ha_rows) table->key_info[keynr].actual_rec_per_key(kp-1);
if (ref_rows > 0)
{
- double tmp= (double)ref_rows;
- /* Reuse the cost formula from best_access_path: */
- set_if_smaller(tmp, (double) tab->join->thd->variables.max_seeks_for_key);
- if (table->covering_keys.is_set(keynr))
- tmp= table->file->keyread_time(keynr, 1, (ha_rows) tmp);
- else
- tmp= table->file->read_time(keynr, 1,
- (ha_rows) MY_MIN(tmp,tab->worst_seeks));
+ double tmp= cost_for_index_read(tab->join->thd, table, keynr,
+ ref_rows,
+ (ha_rows) tab->worst_seeks);
if (tmp < best_cost)
{
best_cost= tmp;
@@ -28028,7 +28209,7 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab,
value for further use in QUICK_SELECT_DESC
@note
- This function takes into account table->quick_condition_rows statistic
+ This function takes into account table->opt_range_condition_rows statistic
(that is calculated by the make_join_statistics function).
However, single table procedures such as mysql_update() and mysql_delete()
never call make_join_statistics, so they have to update it manually
@@ -28063,7 +28244,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
double fanout= 1;
ha_rows table_records= table->stat_records();
bool group= join && join->group && order == join->group_list;
- ha_rows refkey_rows_estimate= table->quick_condition_rows;
+ ha_rows refkey_rows_estimate= table->opt_range_condition_rows;
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
THD* thd= join ? join->thd : table->in_use;
@@ -28117,7 +28298,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
trace_cheaper_ordering.add("read_time", read_time);
/*
Calculate the selectivity of the ref_key for REF_ACCESS. For
- RANGE_ACCESS we use table->quick_condition_rows.
+ RANGE_ACCESS we use table->opt_range_condition_rows.
*/
if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
{
@@ -28128,9 +28309,9 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
if (tab->ref.const_ref_part_map ==
make_prev_keypart_map(tab->ref.key_parts) &&
- table->quick_keys.is_set(ref_key) &&
- table->quick_key_parts[ref_key] == tab->ref.key_parts)
- refkey_rows_estimate= table->quick_rows[ref_key];
+ table->opt_range_keys.is_set(ref_key) &&
+ table->opt_range[ref_key].key_parts == tab->ref.key_parts)
+ refkey_rows_estimate= table->opt_range[ref_key].rows;
else
{
const KEY *ref_keyinfo= table->key_info + ref_key;
@@ -28344,8 +28525,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
possible_key.add("cause", "ref estimates better");
continue;
}
- if (table->quick_keys.is_set(nr))
- quick_records= table->quick_rows[nr];
+ if (table->opt_range_keys.is_set(nr))
+ quick_records= table->opt_range[nr].rows;
possible_key.add("records", quick_records);
if (best_key < 0 ||
(select_limit <= MY_MIN(quick_records,best_records) ?
@@ -28415,7 +28596,6 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*new_select_limit= has_limit ? best_select_limit : table_records;
if (new_used_key_parts != NULL)
*new_used_key_parts= best_key_parts;
-
DBUG_RETURN(TRUE);
}
@@ -28441,7 +28621,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
@note
Side effects:
- may deallocate or deallocate and replace select->quick;
- - may set table->quick_condition_rows and table->quick_rows[...]
+ - may set table->opt_range_condition_rows and table->quick_rows[...]
to table->file->stats.records.
*/
@@ -28506,10 +28686,10 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
{ // check if some index scan & LIMIT is more efficient than filesort
/*
- Update quick_condition_rows since single table UPDATE/DELETE procedures
+ Update opt_range_condition_rows since single table UPDATE/DELETE procedures
don't call make_join_statistics() and leave this variable uninitialized.
*/
- table->quick_condition_rows= table->stat_records();
+ table->opt_range_condition_rows= table->stat_records();
int key, direction;
if (test_if_cheaper_ordering(NULL, order, table,
@@ -28910,5 +29090,375 @@ select_handler *SELECT_LEX::find_select_handler(THD *thd)
/**
+ @brief
+ Construct not null conditions for provingly not nullable fields
+
+ @details
+ For each non-constant joined table the function creates a conjunction
+ of IS NOT NULL predicates containing a predicate for each field used
+ in the WHERE clause or an OR expression such that
+ - is declared as nullable
+ - for which it can proved be that it is null-rejected
+ - is a part of some index.
+ This conjunction could be anded with either the WHERE condition or with
+ an ON expression and the modified join query would produce the same
+ result set as the original one.
+ If a conjunction of IS NOT NULL predicates is constructed for an inner
+ table of an outer join OJ that is not an inner table of embedded outer
+ joins then it is to be anded with the ON expression of OJ.
+ The constructed conjunctions of IS NOT NULL predicates are attached
+ to the corresponding tables. They used for range analysis complementary
+ to other sargable range conditions.
+
+ @note
+ Let f be a field of the joined table t. In the context of the upper
+ paragraph field f is called null-rejected if any the following holds:
+
+ - t is a table of a top inner join and a conjunctive formula that rejects
+ rows with null values for f can be extracted from the WHERE condition
+
+ - t is an outer table of a top outer join operation and a conjunctive
+ formula over the outer tables of the outer join that rejects rows with
+ null values for can be extracted from the WHERE condition
+
+ - t is an outer table of a non-top outer join operation and a conjunctive
+ formula over the outer tables of the outer join that rejects rows with
+ null values for f can be extracted from the ON expression of the
+ embedding outer join
+
+ - the joined table is an inner table of a outer join operation and
+ a conjunctive formula over inner tables of the outer join that rejects
+ rows with null values for f can be extracted from the ON expression of
+ the outer join operation.
+
+ It is assumed above that all inner join nests have been eliminated and
+ that all possible conversions of outer joins into inner joins have been
+ already done.
+*/
+
+void JOIN::make_notnull_conds_for_range_scans()
+{
+ DBUG_ENTER("JOIN::make_notnull_conds_for_range_scans");
+
+
+ if (impossible_where ||
+ !optimizer_flag(thd, OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN))
+ {
+ /* Complementary range analysis is not needed */
+ DBUG_VOID_RETURN;
+ }
+
+ if (conds && build_notnull_conds_for_range_scans(this, conds,
+ conds->used_tables()))
+ {
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ {
+ /*
+ Found a IS NULL conjunctive predicate for a null-rejected field
+ in the WHERE clause
+ */
+ conds= false_cond;
+ cond_equal= 0;
+ impossible_where= true;
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ List_iterator<TABLE_LIST> li(*join_list);
+ TABLE_LIST *tbl;
+ while ((tbl= li++))
+ {
+ if (tbl->on_expr)
+ {
+ if (tbl->nested_join)
+ {
+ build_notnull_conds_for_inner_nest_of_outer_join(this, tbl);
+ }
+ else if (build_notnull_conds_for_range_scans(this, tbl->on_expr,
+ tbl->table->map))
+ {
+ /*
+ Found a IS NULL conjunctive predicate for a null-rejected field
+ of the inner table of an outer join with ON expression tbl->on_expr
+ */
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ tbl->on_expr= false_cond;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ @brief
+ Build not null conditions for range scans of given join tables
+
+ @param join the join for whose tables not null conditions are to be built
+ @param cond the condition from which not null predicates are to be inferred
+ @param allowed the bit map of join tables to be taken into account
+
+ @details
+ For each join table t from the 'allowed' set of tables the function finds
+ all fields whose null-rejectedness can be inferred from null-rejectedness
+ of the condition cond. For each found field f from table t such that it
+ participates at least in one index on table t a NOT NULL predicate is
+ constructed and a conjunction of all such predicates is attached to t.
+ If when looking for null-rejecting fields of t it is discovered one of its
+ fields has to be null-rejected and there is IS NULL conjunctive top level
+ predicate for this field then the function immediately returns true.
+ The function uses the bitmap TABLE::tmp_set to mark found null-rejected
+ fields of table t.
+
+ @note
+ Currently only top level conjuncts without disjunctive sub-formulas are
+ are taken into account when looking for null-rejected fields.
+
+ @retval
+ true if a contradiction is inferred
+ false otherwise
+*/
+
+static
+bool build_notnull_conds_for_range_scans(JOIN *join, Item *cond,
+ table_map allowed)
+{
+ THD *thd= join->thd;
+
+ DBUG_ENTER("build_notnull_conds_for_range_scans");
+
+ for (JOIN_TAB *s= join->join_tab + join->const_tables ;
+ s < join->join_tab + join->table_count ; s++)
+ {
+ /* Clear all needed bitmaps to mark found fields */
+ if (allowed & s->table->map)
+ bitmap_clear_all(&s->table->tmp_set);
+ }
+
+ /*
+ Find all null-rejected fields assuming that cond is null-rejected and
+ only formulas over tables from 'allowed' are to be taken into account
+ */
+ if (cond->find_not_null_fields(allowed))
+ DBUG_RETURN(true);
+
+ /*
+ For each table t from 'allowed' build a conjunction of NOT NULL predicates
+ constructed for all found fields if they are included in some indexes.
+ If the construction of the conjunction succeeds attach the formula to
+ t->table->notnull_cond. The condition will be used to look for complementary
+ range scans.
+ */
+ for (JOIN_TAB *s= join->join_tab + join->const_tables ;
+ s < join->join_tab + join->table_count ; s++)
+ {
+ TABLE *tab= s->table;
+ List<Item> notnull_list;
+ Item *notnull_cond= 0;
+
+ if (!(allowed & tab->map))
+ continue;
+
+ for (Field** field_ptr= tab->field; *field_ptr; field_ptr++)
+ {
+ Field *field= *field_ptr;
+ if (field->part_of_key.is_clear_all())
+ continue;
+ if (!bitmap_is_set(&tab->tmp_set, field->field_index))
+ continue;
+ Item_field *field_item= new (thd->mem_root) Item_field(thd, field);
+ if (!field_item)
+ continue;
+ Item *isnotnull_item=
+ new (thd->mem_root) Item_func_isnotnull(thd, field_item);
+ if (!isnotnull_item)
+ continue;
+ if (notnull_list.push_back(isnotnull_item, thd->mem_root))
+ continue;
+ s->const_keys.merge(field->part_of_key);
+ }
+
+ switch (notnull_list.elements) {
+ case 0:
+ break;
+ case 1:
+ notnull_cond= notnull_list.head();
+ break;
+ default:
+ notnull_cond=
+ new (thd->mem_root) Item_cond_and(thd, notnull_list);
+ }
+ if (notnull_cond && !notnull_cond->fix_fields(thd, 0))
+ {
+ tab->notnull_cond= notnull_cond;
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ @brief
+ Build not null conditions for inner nest tables of an outer join
+
+ @param join the join for whose table nest not null conditions are to be built
+ @param nest_tbl the nest of the inner tables of an outer join
+
+ @details
+ The function assumes that nest_tbl is the nest of the inner tables of an
+ outer join and so an ON expression for this outer join is attached to
+ nest_tbl.
+ The function selects the tables of the nest_tbl that are not inner tables of
+ embedded outer joins and then it calls build_notnull_conds_for_range_scans()
+ for nest_tbl->on_expr and the bitmap for the selected tables. This call
+ finds all fields belonging to the selected tables whose null-rejectedness
+ can be inferred from the null-rejectedness of nest_tbl->on_expr. After this
+ the function recursively finds all null_rejected fields for the remaining
+ tables from the nest of nest_tbl.
+*/
+
+static
+void build_notnull_conds_for_inner_nest_of_outer_join(JOIN *join,
+ TABLE_LIST *nest_tbl)
+{
+ TABLE_LIST *tbl;
+ table_map used_tables= 0;
+ THD *thd= join->thd;
+ List_iterator<TABLE_LIST> li(nest_tbl->nested_join->join_list);
+
+ while ((tbl= li++))
+ {
+ if (!tbl->on_expr)
+ used_tables|= tbl->table->map;
+ }
+ if (used_tables &&
+ build_notnull_conds_for_range_scans(join, nest_tbl->on_expr, used_tables))
+ {
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ nest_tbl->on_expr= false_cond;
+ }
+
+ li.rewind();
+ while ((tbl= li++))
+ {
+ if (tbl->on_expr)
+ {
+ if (tbl->nested_join)
+ {
+ build_notnull_conds_for_inner_nest_of_outer_join(join, tbl);
+ }
+ else if (build_notnull_conds_for_range_scans(join, tbl->on_expr,
+ tbl->table->map))
+ {
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ tbl->on_expr= false_cond;
+ }
+ }
+ }
+}
+
+
+/*
+ @brief
+ Initialize join cache and enable keyread
+*/
+void JOIN::init_join_cache_and_keyread()
+{
+ JOIN_TAB *tab;
+ for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES);
+ tab;
+ tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
+ {
+ TABLE *table= tab->table;
+ switch (tab->type) {
+ case JT_SYSTEM:
+ case JT_CONST:
+ case JT_FT:
+ case JT_UNKNOWN:
+ case JT_MAYBE_REF:
+ break;
+ case JT_EQ_REF:
+ case JT_REF_OR_NULL:
+ case JT_REF:
+ if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread)
+ table->file->ha_start_keyread(tab->ref.key);
+ break;
+ case JT_HASH:
+ case JT_ALL:
+ SQL_SELECT *select;
+ select= tab->select ? tab->select :
+ (tab->filesort ? tab->filesort->select : NULL);
+ if (select && select->quick && select->quick->index != MAX_KEY &&
+ table->covering_keys.is_set(select->quick->index) &&
+ !table->no_keyread)
+ table->file->ha_start_keyread(select->quick->index);
+ break;
+ case JT_HASH_NEXT:
+ case JT_NEXT:
+ if ((tab->read_first_record == join_read_first ||
+ tab->read_first_record == join_read_last) &&
+ table->covering_keys.is_set(tab->index) &&
+ !table->no_keyread)
+ {
+ DBUG_ASSERT(!tab->filesort);
+ table->file->ha_start_keyread(tab->index);
+ }
+ break;
+ default:
+ break;
+ /* purecov: end */
+ }
+
+ if (table->file->keyread_enabled())
+ {
+ /*
+ Here we set the read_set bitmap for all covering keys
+ except CLUSTERED indexes, with all the key-parts inside the key.
+ This is needed specifically for an index that contains virtual column.
+
+ Example:
+ Lets say we have this query
+ SELECT b FROM t1;
+
+ and the table definition is like
+ CREATE TABLE t1(
+ a varchar(10) DEFAULT NULL,
+ b varchar(255) GENERATED ALWAYS AS (a) VIRTUAL,
+ KEY key1 (b));
+
+ So we a virtual column b and an index key1 defined on the virtual
+ column. So if a query uses a vcol, base columns that it
+ depends on are automatically added to the read_set - because they're
+ needed to calculate the vcol.
+ But if we're doing keyread, vcol is taken
+ from the index, not calculated, and base columns do not need to be
+ in the read set. To ensure this we try to set the read_set to only
+ the key-parts of the indexes.
+
+ Another side effect of this is
+ Lets say you have a query
+ select a, b from t1
+ and there is an index key1 (a,b,c)
+ then as key1 is covering and we would have the keyread enable for
+ this key, so the below call will also set the read_set for column
+ c, which is not a problem as we read all the columns from the index
+ tuple.
+ */
+ if (!(table->file->index_flags(table->file->keyread, 0, 1) & HA_CLUSTERED_INDEX))
+ table->mark_columns_used_by_index(table->file->keyread, table->read_set);
+ }
+ if (tab->cache && tab->cache->init(select_options & SELECT_DESCRIBE))
+ revise_cache_usage(tab);
+ else
+ tab->remove_redundant_bnl_scan_conds();
+ }
+}
+
+
+/**
@} (end of group Query_Optimizer)
*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 3ac8c152ffc..bb9ecf88df0 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -247,13 +247,13 @@ class SplM_opt_info;
typedef struct st_join_table {
TABLE *table;
TABLE_LIST *tab_list;
- KEYUSE *keyuse; /**< pointer to first used key */
+ KEYUSE *keyuse; /**< pointer to first used key */
KEY *hj_key; /**< descriptor of the used best hash join key
- not supported by any index */
+ not supported by any index */
SQL_SELECT *select;
COND *select_cond;
COND *on_precond; /**< part of on condition to check before
- accessing the first inner table */
+ accessing the first inner table */
QUICK_SELECT_I *quick;
/*
The value of select_cond before we've attempted to do Index Condition
@@ -635,6 +635,8 @@ typedef struct st_join_table {
ha_rows get_examined_rows();
bool preread_init();
+ bool pfs_batch_update(JOIN *join);
+
bool is_sjm_nest() { return MY_TEST(bush_children); }
/*
@@ -1097,7 +1099,7 @@ protected:
keyuse.buffer= NULL;
keyuse.malloc_flags= 0;
best_positions= 0; /* To detect errors */
- error= my_multi_malloc(MYF(MY_WME),
+ error= my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME),
&best_positions,
sizeof(*best_positions) * (tables + 1),
&join_tab_keyuse,
@@ -1622,10 +1624,9 @@ public:
return exec_join_tab_cnt() + aggr_tables - 1;
}
- int prepare(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);
+ int prepare(TABLE_LIST *tables, 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();
@@ -1643,7 +1644,6 @@ public:
bool flatten_subqueries();
bool optimize_unflattened_subqueries();
bool optimize_constant_subqueries();
- int init_join_caches();
bool make_range_rowid_filters();
bool init_range_rowid_filters();
bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields,
@@ -1790,6 +1790,7 @@ public:
void add_keyuses_for_splitting();
bool inject_best_splitting_cond(table_map remaining_tables);
bool fix_all_splittings_in_plan();
+ void make_notnull_conds_for_range_scans();
bool transform_in_predicates_into_in_subq(THD *thd);
private:
@@ -1826,6 +1827,7 @@ private:
bool add_having_as_table_cond(JOIN_TAB *tab);
bool make_aggr_tables_info();
bool add_fields_for_current_rowid(JOIN_TAB *cur, List<Item> *fields);
+ void init_join_cache_and_keyread();
};
enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS};
@@ -2103,8 +2105,7 @@ int join_read_key2(THD *thd, struct st_join_table *tab, TABLE *table,
bool handle_select(THD *thd, LEX *lex, select_result *result,
ulong setup_tables_done_option);
-bool mysql_select(THD *thd,
- TABLE_LIST *tables, uint wild_num, List<Item> &list,
+bool mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &list,
COND *conds, uint og_num, ORDER *order, ORDER *group,
Item *having, ORDER *proc_param, ulonglong select_type,
select_result *result, SELECT_LEX_UNIT *unit,
@@ -2433,6 +2434,12 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ulonglong select_options, ha_rows rows_limit,
const LEX_CSTRING *alias, bool do_not_open=FALSE,
bool keep_row_order= FALSE);
+TABLE *create_tmp_table_for_schema(THD *thd, TMP_TABLE_PARAM *param,
+ const ST_SCHEMA_TABLE &schema_table,
+ longlong select_options,
+ const LEX_CSTRING &alias,
+ bool do_not_open, bool keep_row_order);
+
void free_tmp_table(THD *thd, TABLE *entry);
bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
TMP_ENGINE_COLUMNDEF *start_recinfo,
@@ -2509,29 +2516,6 @@ public:
class select_handler;
-class Pushdown_select: public Sql_alloc
-{
-private:
- bool is_analyze;
- List<Item> result_columns;
- bool send_result_set_metadata();
- bool send_data();
- bool send_eof();
-
-public:
- SELECT_LEX *select;
- select_handler *handler;
-
- Pushdown_select(SELECT_LEX *sel, select_handler *h);
-
- ~Pushdown_select();
-
- bool init();
-
- int execute();
-};
-
-
bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
int test_if_group_changed(List<Cached_item> &list);
int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort);
diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc
index a2bcfd5a4ff..b9d500d463c 100644
--- a/sql/sql_sequence.cc
+++ b/sql/sql_sequence.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2017, MariaDB Corporation, Alibaba Corporation
+ Copyrgiht (c) 2020, MariaDB Corporation.
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,20 +49,20 @@ struct Field_definition
static Field_definition sequence_structure[]=
{
- {"next_not_cached_value", 21, &type_handler_longlong,
+ {"next_not_cached_value", 21, &type_handler_slonglong,
{STRING_WITH_LEN("")}, FL},
- {"minimum_value", 21, &type_handler_longlong, {STRING_WITH_LEN("")}, FL},
- {"maximum_value", 21, &type_handler_longlong, {STRING_WITH_LEN("")}, FL},
- {"start_value", 21, &type_handler_longlong, {STRING_WITH_LEN("start value when sequences is created or value if RESTART is used")}, FL},
- {"increment", 21, &type_handler_longlong,
+ {"minimum_value", 21, &type_handler_slonglong, {STRING_WITH_LEN("")}, FL},
+ {"maximum_value", 21, &type_handler_slonglong, {STRING_WITH_LEN("")}, FL},
+ {"start_value", 21, &type_handler_slonglong, {STRING_WITH_LEN("start value when sequences is created or value if RESTART is used")}, FL},
+ {"increment", 21, &type_handler_slonglong,
{STRING_WITH_LEN("increment value")}, FL},
- {"cache_size", 21, &type_handler_longlong, {STRING_WITH_LEN("")},
+ {"cache_size", 21, &type_handler_ulonglong, {STRING_WITH_LEN("")},
FL | UNSIGNED_FLAG},
- {"cycle_option", 1, &type_handler_tiny, {STRING_WITH_LEN("0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed")},
+ {"cycle_option", 1, &type_handler_utiny, {STRING_WITH_LEN("0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed")},
FL | UNSIGNED_FLAG },
- {"cycle_count", 21, &type_handler_longlong,
+ {"cycle_count", 21, &type_handler_slonglong,
{STRING_WITH_LEN("How many cycles have been done")}, FL},
- {NULL, 0, &type_handler_longlong, {STRING_WITH_LEN("")}, 0}
+ {NULL, 0, &type_handler_slonglong, {STRING_WITH_LEN("")}, 0}
};
#undef FL
@@ -312,8 +313,7 @@ bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *org_table_list)
if (!temporary_table)
{
/*
- The following code works like open_system_tables_for_read() and
- close_system_tables()
+ The following code works like open_system_tables_for_read()
The idea is:
- Copy the table_list object for the sequence that was created
- Backup the current state of open tables and create a new
@@ -450,11 +450,11 @@ int SEQUENCE::read_initial_values(TABLE *table)
MYSQL_LOCK *lock;
bool mdl_lock_used= 0;
THD *thd= table->in_use;
- bool has_active_transaction= !thd->transaction.stmt.is_empty();
+ bool has_active_transaction= !thd->transaction->stmt.is_empty();
/*
There is already a mdl_ticket for this table. However, for list_fields
the MDL lock is of type MDL_SHARED_HIGH_PRIO which is not usable
- for doing a able lock. Get a proper read lock to solve this.
+ for doing a table lock. Get a proper read lock to solve this.
*/
if (table->mdl_ticket == 0)
{
@@ -465,10 +465,9 @@ int SEQUENCE::read_initial_values(TABLE *table)
where we don't have a mdl lock on the table
*/
- mdl_request.init(MDL_key::TABLE,
- table->s->db.str,
- table->s->table_name.str,
- MDL_SHARED_READ, MDL_EXPLICIT);
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE, table->s->db.str,
+ table->s->table_name.str, MDL_SHARED_READ,
+ MDL_EXPLICIT);
mdl_requests.push_front(&mdl_request);
if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
@@ -486,7 +485,7 @@ int SEQUENCE::read_initial_values(TABLE *table)
thd->mdl_context.release_lock(mdl_request.ticket);
write_unlock(table);
- if (!has_active_transaction && !thd->transaction.stmt.is_empty() &&
+ if (!has_active_transaction && !thd->transaction->stmt.is_empty() &&
!thd->in_sub_stmt)
trans_commit_stmt(thd);
DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT);
@@ -508,7 +507,7 @@ int SEQUENCE::read_initial_values(TABLE *table)
But we also don't want to commit the stmt transaction while in a
substatement, see MDEV-15977.
*/
- if (!has_active_transaction && !thd->transaction.stmt.is_empty() &&
+ if (!has_active_transaction && !thd->transaction->stmt.is_empty() &&
!thd->in_sub_stmt)
trans_commit_stmt(thd);
}
@@ -598,7 +597,6 @@ void sequence_definition::adjust_values(longlong next_value)
int sequence_definition::write_initial_sequence(TABLE *table)
{
int error;
- THD *thd= table->in_use;
MY_BITMAP *save_write_set;
store_fields(table);
@@ -606,15 +604,14 @@ int sequence_definition::write_initial_sequence(TABLE *table)
table->s->sequence->copy(this);
/*
Sequence values will be replicated as a statement
- like 'create sequence'. So disable binary log temporarily
+ like 'create sequence'. So disable row logging for this table & statement
*/
- tmp_disable_binlog(thd);
+ table->file->row_logging= table->file->row_logging_init= 0;
save_write_set= table->write_set;
table->write_set= &table->s->all_set;
table->s->sequence->initialized= SEQUENCE::SEQ_IN_PREPARE;
error= table->file->ha_write_row(table->record[0]);
table->s->sequence->initialized= SEQUENCE::SEQ_UNINTIALIZED;
- reenable_binlog(thd);
table->write_set= save_write_set;
if (unlikely(error))
table->file->print_error(error, MYF(0));
@@ -937,6 +934,8 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
table= first_table->table;
seq= table->s->sequence;
+
+ seq->write_lock(table);
new_seq->reserved_until= seq->reserved_until;
/* Copy from old sequence those fields that the user didn't specified */
@@ -969,18 +968,18 @@ bool Sql_cmd_alter_sequence::execute(THD *thd)
first_table->db.str,
first_table->table_name.str);
error= 1;
+ seq->write_unlock(table);
goto end;
}
- table->s->sequence->write_lock(table);
if (likely(!(error= new_seq->write(table, 1))))
{
/* Store the sequence values in table share */
- table->s->sequence->copy(new_seq);
+ seq->copy(new_seq);
}
else
table->file->print_error(error, MYF(0));
- table->s->sequence->write_unlock(table);
+ seq->write_unlock(table);
if (trans_commit_stmt(thd))
error= 1;
if (trans_commit_implicit(thd))
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index 7913a7d2b9f..d52d6071e89 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -93,6 +93,8 @@ static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
DBUG_RETURN((uchar*) server->server_name);
}
+static PSI_memory_key key_memory_servers;
+
#ifdef HAVE_PSI_INTERFACE
static PSI_rwlock_key key_rwlock_THR_LOCK_servers;
@@ -101,6 +103,11 @@ static PSI_rwlock_info all_servers_cache_rwlocks[]=
{ &key_rwlock_THR_LOCK_servers, "THR_LOCK_servers", PSI_FLAG_GLOBAL}
};
+static PSI_memory_info all_servers_cache_memory[]=
+{
+ { &key_memory_servers, "servers_cache", PSI_FLAG_GLOBAL}
+};
+
static void init_servers_cache_psi_keys(void)
{
const char* category= "sql";
@@ -111,9 +118,87 @@ static void init_servers_cache_psi_keys(void)
count= array_elements(all_servers_cache_rwlocks);
PSI_server->register_rwlock(category, all_servers_cache_rwlocks, count);
+
+ count= array_elements(all_servers_cache_memory);
+ mysql_memory_register(category, all_servers_cache_memory, count);
}
#endif /* HAVE_PSI_INTERFACE */
+
+struct close_cached_connection_tables_arg
+{
+ THD *thd;
+ LEX_CSTRING *connection;
+ TABLE_LIST *tables;
+};
+
+
+static my_bool close_cached_connection_tables_callback(
+ TDC_element *element, close_cached_connection_tables_arg *arg)
+{
+ TABLE_LIST *tmp;
+
+ mysql_mutex_lock(&element->LOCK_table_share);
+ /* Ignore if table is not open or does not have a connect_string */
+ if (!element->share || !element->share->connect_string.length ||
+ !element->ref_count)
+ goto end;
+
+ /* Compare the connection string */
+ if (arg->connection &&
+ (arg->connection->length > element->share->connect_string.length ||
+ (arg->connection->length < element->share->connect_string.length &&
+ (element->share->connect_string.str[arg->connection->length] != '/' &&
+ element->share->connect_string.str[arg->connection->length] != '\\')) ||
+ strncasecmp(arg->connection->str, element->share->connect_string.str,
+ arg->connection->length)))
+ goto end;
+
+ /* close_cached_tables() only uses these elements */
+ if (!(tmp= (TABLE_LIST*) alloc_root(arg->thd->mem_root, sizeof(TABLE_LIST))) ||
+ !(arg->thd->make_lex_string(&tmp->db, element->share->db.str, element->share->db.length)) ||
+ !(arg->thd->make_lex_string(&tmp->table_name, element->share->table_name.str,
+ element->share->table_name.length)))
+ {
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return TRUE;
+ }
+
+ tmp->next_global= tmp->next_local= arg->tables;
+ MDL_REQUEST_INIT(&tmp->mdl_request, MDL_key::TABLE, tmp->db.str,
+ tmp->table_name.str, MDL_EXCLUSIVE, MDL_TRANSACTION);
+ arg->tables= tmp;
+
+end:
+ mysql_mutex_unlock(&element->LOCK_table_share);
+ return FALSE;
+}
+
+
+/**
+ Close all tables which match specified connection string or
+ if specified string is NULL, then any table with a connection string.
+
+ @return false ok
+ @return true error, some tables may keep using old server info
+*/
+
+static bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection)
+{
+ close_cached_connection_tables_arg argument= { thd, connection, 0 };
+ DBUG_ENTER("close_cached_connections");
+
+ if (tdc_iterate(thd,
+ (my_hash_walk_action) close_cached_connection_tables_callback,
+ &argument))
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(argument.tables ?
+ close_cached_tables(thd, argument.tables, true,
+ thd->variables.lock_wait_timeout) : false);
+}
+
+
/*
Initialize structures responsible for servers used in federated
server scheme information for them from the server
@@ -148,7 +233,7 @@ bool servers_init(bool dont_read_servers_table)
DBUG_RETURN(TRUE);
/* initialise our servers cache */
- if (my_hash_init(&servers_cache, system_charset_info, 32, 0, 0,
+ if (my_hash_init(key_memory_servers, &servers_cache, system_charset_info, 32, 0, 0,
(my_hash_get_key) servers_cache_get_key, 0, 0))
{
return_val= TRUE; /* we failed, out of memory? */
@@ -156,7 +241,7 @@ bool servers_init(bool dont_read_servers_table)
}
/* Initialize the mem root for data */
- init_sql_alloc(&mem, "servers", ACL_ALLOC_BLOCK_SIZE, 0,
+ init_sql_alloc(key_memory_servers, &mem, ACL_ALLOC_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
if (dont_read_servers_table)
@@ -206,7 +291,7 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
my_hash_reset(&servers_cache);
free_root(&mem, MYF(0));
- init_sql_alloc(&mem, "servers_load", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(key_memory_servers, &mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (init_read_record(&read_record_info,thd,table=tables[0].table, NULL, NULL,
1,0, FALSE))
@@ -394,6 +479,7 @@ insert_server(THD *thd, FOREIGN_SERVER *server)
/* need to open before acquiring THR_LOCK_plugin or it will deadlock */
if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
goto end;
+ table->file->row_logging= 0; // Don't log to binary log
/* insert the server into the table */
if (unlikely(error= insert_server_record(table, server)))
@@ -532,9 +618,9 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
{
int error;
DBUG_ENTER("insert_server_record");
- tmp_disable_binlog(table->in_use);
- table->use_all_columns();
+ DBUG_ASSERT(!table->file->row_logging);
+ table->use_all_columns();
empty_record(table);
/* set the field that's the PK to the value we're looking for */
@@ -567,8 +653,6 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
}
else
error= ER_FOREIGN_SERVER_EXISTS;
-
- reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -885,7 +969,8 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
{
int error=0;
DBUG_ENTER("update_server_record");
- tmp_disable_binlog(table->in_use);
+ DBUG_ASSERT(!table->file->row_logging);
+
table->use_all_columns();
/* set the field that's the PK to the value we're looking for */
table->field[0]->store(server->server_name,
@@ -921,7 +1006,6 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server)
}
end:
- reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
@@ -946,7 +1030,8 @@ delete_server_record(TABLE *table, LEX_CSTRING *name)
{
int error;
DBUG_ENTER("delete_server_record");
- tmp_disable_binlog(table->in_use);
+ DBUG_ASSERT(!table->file->row_logging);
+
table->use_all_columns();
/* set the field that's the PK to the value we're looking for */
@@ -970,7 +1055,6 @@ delete_server_record(TABLE *table, LEX_CSTRING *name)
table->file->print_error(error, MYF(0));
}
- reenable_binlog(table->in_use);
DBUG_RETURN(error);
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 29ee8ef2fb1..c00476871e4 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -94,7 +94,6 @@ enum enum_i_s_events_fields
ISE_DB_CL
};
-#define USERNAME_WITH_HOST_CHAR_LENGTH (USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2)
static const LEX_CSTRING trg_action_time_type_names[]=
{
@@ -124,14 +123,6 @@ static const char *ha_choice_values[] = {"", "0", "1"};
static void store_key_options(THD *, String *, TABLE *, KEY *);
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-static void get_cs_converted_string_value(THD *thd,
- String *input_str,
- String *output_str,
- CHARSET_INFO *cs,
- bool use_hex);
-#endif
-
static int show_create_view(THD *thd, TABLE_LIST *table, String *buff);
static int show_create_sequence(THD *thd, TABLE_LIST *table_list,
String *packet);
@@ -356,15 +347,14 @@ int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
{
if (lookup.wild_db_value)
{
- if (my_wildcmp(files_charset_info, dl.str, dlend, wstr, wend,
- wild_prefix, wild_one, wild_many))
+ if (files_charset_info->wildcmp(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))
+ if (files_charset_info->strnncoll(dl.str, dl.length,
+ lookup.db_value.str, lookup.db_value.length))
continue;
}
}
@@ -378,124 +368,6 @@ int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
}
-#ifdef HAVE_SPATIAL
-static int fill_spatial_ref_sys(THD *thd, TABLE_LIST *tables, COND *cond)
-{
- DBUG_ENTER("fill_spatial_ref_sys");
- TABLE *table= tables->table;
- CHARSET_INFO *cs= system_charset_info;
- int result= 1;
-
- restore_record(table, s->default_values);
-
- table->field[0]->store(-1, FALSE); /*SRID*/
- table->field[1]->store(STRING_WITH_LEN("Not defined"), cs); /*AUTH_NAME*/
- table->field[2]->store(-1, FALSE); /*AUTH_SRID*/
- table->field[3]->store(STRING_WITH_LEN(
- "LOCAL_CS[\"Spatial reference wasn't specified\","
- "LOCAL_DATUM[\"Unknown\",0]," "UNIT[\"m\",1.0]," "AXIS[\"x\",EAST],"
- "AXIS[\"y\",NORTH]]"), cs);/*SRTEXT*/
- if (schema_table_store_record(thd, table))
- goto exit;
-
- table->field[0]->store(0, TRUE); /*SRID*/
- table->field[1]->store(STRING_WITH_LEN("EPSG"), cs); /*AUTH_NAME*/
- table->field[2]->store(404000, TRUE); /*AUTH_SRID*/
- table->field[3]->store(STRING_WITH_LEN(
- "LOCAL_CS[\"Wildcard 2D cartesian plane in metric unit\","
- "LOCAL_DATUM[\"Unknown\",0]," "UNIT[\"m\",1.0],"
- "AXIS[\"x\",EAST]," "AXIS[\"y\",NORTH],"
- "AUTHORITY[\"EPSG\",\"404000\"]]"), cs);/*SRTEXT*/
- if (schema_table_store_record(thd, table))
- goto exit;
-
- result= 0;
-
-exit:
- DBUG_RETURN(result);
-}
-
-
-static int get_geometry_column_record(THD *thd, TABLE_LIST *tables,
- TABLE *table, bool res,
- const LEX_CSTRING *db_name,
- const LEX_CSTRING *table_name)
-{
- CHARSET_INFO *cs= system_charset_info;
- TABLE *show_table;
- Field **ptr, *field;
- DBUG_ENTER("get_geometry_column_record");
-
- if (res)
- {
- if (thd->lex->sql_command != SQLCOM_SHOW_FIELDS)
- {
- /*
- I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS
- rather than in SHOW COLUMNS
- */
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- thd->get_stmt_da()->sql_errno(),
- thd->get_stmt_da()->message());
- thd->clear_error();
- res= 0;
- }
- DBUG_RETURN(res);
- }
-
- if (tables->schema_table)
- goto exit;
- show_table= tables->table;
- ptr= show_table->field;
- show_table->use_all_columns(); // Required for default
- restore_record(show_table, s->default_values);
-
- for (; (field= *ptr) ; ptr++)
- if (field->type() == MYSQL_TYPE_GEOMETRY)
- {
- Field_geom *fg= (Field_geom *) field;
-
- DEBUG_SYNC(thd, "get_schema_column");
-
- /* Get default row, with all NULL fields set to NULL */
- restore_record(table, s->default_values);
-
- /*F_TABLE_CATALOG*/
- table->field[0]->store(STRING_WITH_LEN("def"), cs);
- /*F_TABLE_SCHEMA*/
- table->field[1]->store(db_name->str, db_name->length, cs);
- /*F_TABLE_NAME*/
- table->field[2]->store(table_name->str, table_name->length, cs);
- /*G_TABLE_CATALOG*/
- table->field[4]->store(STRING_WITH_LEN("def"), cs);
- /*G_TABLE_SCHEMA*/
- table->field[5]->store(db_name->str, db_name->length, cs);
- /*G_TABLE_NAME*/
- table->field[6]->store(table_name->str, table_name->length, cs);
- /*G_GEOMETRY_COLUMN*/
- table->field[7]->store(field->field_name.str, field->field_name.length,
- cs);
- /*STORAGE_TYPE*/
- table->field[8]->store(1LL, TRUE); /*Always 1 (binary implementation)*/
- /*GEOMETRY_TYPE*/
- table->field[9]->store((longlong) (fg->get_geometry_type()), TRUE);
- /*COORD_DIMENSION*/
- table->field[10]->store(2LL, TRUE);
- /*MAX_PPR*/
- table->field[11]->set_null();
- /*SRID*/
- table->field[12]->store((longlong) (fg->get_srid()), TRUE);
-
- if (schema_table_store_record(thd, table))
- DBUG_RETURN(1);
- }
-
-exit:
- DBUG_RETURN(0);
-}
-#endif /*HAVE_SPATIAL*/
-
-
/***************************************************************************
** List all Authors.
** If you can update it, you get to be in it :)
@@ -609,7 +481,10 @@ static struct show_privileges_st sys_privileges[]=
{"Proxy", "Server Admin", "To make proxy user possible"},
{"References", "Databases,Tables", "To have references on tables"},
{"Reload", "Server Admin", "To reload or refresh tables, logs and privileges"},
- {"Replication client","Server Admin","To ask where the slave or master servers are"},
+ {"Binlog admin", "Server", "To purge binary logs"},
+ {"Binlog monitor", "Server", "To use SHOW BINLOG STATUS and SHOW BINARY LOG"},
+ {"Replication master admin", "Server", "To monitor connected slaves"},
+ {"Replication slave admin", "Server", "To start/monitor/stop slave and apply binlog events"},
{"Replication slave","Server Admin","To read binary log events from the master"},
{"Select", "Tables", "To retrieve rows from table"},
{"Show databases","Server Admin","To see all databases with SHOW DATABASES"},
@@ -619,6 +494,10 @@ static struct show_privileges_st sys_privileges[]=
{"Trigger","Tables", "To use triggers"},
{"Create tablespace", "Server Admin", "To create/alter/drop tablespaces"},
{"Update", "Tables", "To update existing rows"},
+ {"Set user","Server", "To create views and stored routines with a different definer"},
+ {"Federated admin", "Server", "To execute the CREATE SERVER, ALTER SERVER, DROP SERVER statements"},
+ {"Connection admin", "Server", "To bypass connection limits and kill other users' connections"},
+ {"Read_only admin", "Server", "To perform write operations even if @@read_only=ON"},
{"Usage","Server Admin","No privileges - allow connect only"},
{NullS, NullS, NullS}
};
@@ -700,8 +579,8 @@ static bool skip_ignored_dir_check= TRUE;
bool
ignore_db_dirs_init()
{
- return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_CSTRING *),
- 0, 0, MYF(0));
+ return my_init_dynamic_array(key_memory_ignored_db, &ignore_db_dirs_array,
+ sizeof(LEX_STRING *), 0, 0, MYF(0));
}
@@ -749,8 +628,8 @@ push_ignored_db_dir(char *path)
return true;
// No need to normalize, it's only a directory name, not a path.
- if (!my_multi_malloc(0,
- &new_elt, sizeof(LEX_CSTRING),
+ if (!my_multi_malloc(key_memory_ignored_db, MYF(0),
+ &new_elt, sizeof(LEX_STRING),
&new_elt_buffer, path_len + 1,
NullS))
return true;
@@ -830,7 +709,7 @@ void ignore_db_dirs_append(const char *dirname_arg)
LEX_STRING *new_entry;
size_t len= strlen(dirname_arg);
- if (!my_multi_malloc(0,
+ if (!my_multi_malloc(PSI_INSTRUMENT_ME, MYF(0),
&new_entry, sizeof(LEX_STRING),
&new_entry_buf, len + 1,
NullS))
@@ -852,7 +731,7 @@ void ignore_db_dirs_append(const char *dirname_arg)
// Add one for comma and one for \0.
size_t newlen= curlen + len + 1 + 1;
char *new_db_dirs;
- if (!(new_db_dirs= (char*)my_malloc(newlen ,MYF(0))))
+ if (!(new_db_dirs= (char*)my_malloc(PSI_INSTRUMENT_ME, newlen, MYF(0))))
{
// This is not a critical condition
return;
@@ -878,12 +757,10 @@ ignore_db_dirs_process_additions()
skip_ignored_dir_check= TRUE;
- if (my_hash_init(&ignore_db_dirs_hash,
- lower_case_table_names ?
- character_set_filesystem : &my_charset_bin,
- 0, 0, 0, db_dirs_hash_get_key,
- dispose_db_dir,
- HASH_UNIQUE))
+ if (my_hash_init(key_memory_ignored_db, &ignore_db_dirs_hash,
+ lower_case_table_names ? character_set_filesystem :
+ &my_charset_bin, 0, 0, 0, db_dirs_hash_get_key,
+ dispose_db_dir, HASH_UNIQUE))
return true;
/* len starts from 1 because of the terminating zero. */
@@ -905,7 +782,8 @@ ignore_db_dirs_process_additions()
len--;
/* +1 the terminating zero */
- ptr= opt_ignore_db_dirs= (char *) my_malloc(len + 1, MYF(0));
+ ptr= opt_ignore_db_dirs= (char *) my_malloc(key_memory_ignored_db, len + 1,
+ MYF(0));
if (!ptr)
return true;
@@ -1275,10 +1153,10 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list,
access is granted. We need to check if table_list->grant.privilege
contains any table-specific privilege.
*/
- DBUG_PRINT("debug", ("table_list->grant.privilege: %lx",
- table_list->grant.privilege));
+ DBUG_PRINT("debug", ("table_list->grant.privilege: %llx",
+ (longlong) (table_list->grant.privilege)));
if (check_some_access(thd, SHOW_CREATE_TABLE_ACLS, table_list) ||
- (table_list->grant.privilege & SHOW_CREATE_TABLE_ACLS) == 0)
+ (table_list->grant.privilege & SHOW_CREATE_TABLE_ACLS) == NO_ACL)
{
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"SHOW", thd->security_ctx->priv_user,
@@ -1472,11 +1350,11 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname,
LEX_CSTRING *orig_dbname,
const DDL_options_st &options)
{
- char buff[2048];
+ char buff[2048+DATABASE_COMMENT_MAXLEN];
String buffer(buff, sizeof(buff), system_charset_info);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx;
- uint db_access;
+ privilege_t db_access(NO_ACL);
#endif
Schema_specification_st create;
Protocol *protocol=thd->protocol;
@@ -1508,6 +1386,7 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname,
{
*dbname= INFORMATION_SCHEMA_NAME;
create.default_table_charset= system_charset_info;
+ create.schema_comment= NULL;
}
else
{
@@ -1547,6 +1426,13 @@ bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname,
}
buffer.append(STRING_WITH_LEN(" */"));
}
+
+ if (create.schema_comment)
+ {
+ buffer.append(STRING_WITH_LEN(" COMMENT "));
+ append_unescaped(&buffer, create.schema_comment->str,
+ create.schema_comment->length);
+ }
protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
if (protocol->write())
@@ -1614,7 +1500,7 @@ static const char *require_quotes(const char *name, uint name_length)
for (; name < end ; name++)
{
uchar chr= (uchar) *name;
- int length= my_charlen(system_charset_info, name, end);
+ int length= system_charset_info->charlen(name, end);
if (length == 1 && !system_charset_info->ident_map[chr])
return name;
if (length == 1 && (chr < '0' || chr > '9'))
@@ -1675,7 +1561,7 @@ append_identifier(THD *thd, String *packet, const char *name, size_t length)
for (name_end= name+length ; name < name_end ; )
{
uchar chr= (uchar) *name;
- int char_length= my_charlen(system_charset_info, name, name_end);
+ int char_length= system_charset_info->charlen(name, name_end);
/*
charlen can return 0 and negative numbers on a wrong multibyte
sequence. It is possible when upgrading from 4.0,
@@ -1867,7 +1753,7 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value,
@param thd thread handler
@param packet string to append
@param opt list of options
- @param check_options only print known options
+ @param check_options print all used options
@param rules list of known options
*/
@@ -1926,7 +1812,9 @@ static void add_table_options(THD *thd, TABLE *table,
handlerton *hton;
HA_CREATE_INFO create_info;
bool check_options= (!(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
- !create_info_arg);
+ (!create_info_arg ||
+ create_info_arg->used_fields &
+ HA_CREATE_PRINT_ALL_OPTIONS));
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (table->part_info)
@@ -2097,6 +1985,14 @@ static void append_period(THD *thd, String *packet, const LEX_CSTRING &start,
packet->append(STRING_WITH_LEN(")"));
}
+int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
+ Table_specification_st *create_info_arg,
+ enum_with_db_name with_db_name)
+{
+ return show_create_table_ex(thd, table_list, NULL, NULL, packet,
+ create_info_arg, with_db_name);
+}
+
/*
Build a CREATE TABLE statement for a table.
@@ -2105,6 +2001,11 @@ static void append_period(THD *thd, String *packet, const LEX_CSTRING &start,
thd The thread
table_list A list containing one table to write statement
for.
+ force_db If not NULL, database name to use in the CREATE
+ TABLE statement.
+ force_name If not NULL, table name to use in the CREATE TABLE
+ statement. if NULL, the name from table_list will be
+ used.
packet Pointer to a string where statement will be
written.
create_info_arg Pointer to create information that can be used
@@ -2121,9 +2022,11 @@ static void append_period(THD *thd, String *packet, const LEX_CSTRING &start,
0 OK
*/
-int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
- Table_specification_st *create_info_arg,
- enum_with_db_name with_db_name)
+int show_create_table_ex(THD *thd, TABLE_LIST *table_list,
+ const char *force_db, const char *force_name,
+ String *packet,
+ Table_specification_st *create_info_arg,
+ enum_with_db_name with_db_name)
{
List<Item> field_list;
char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH];
@@ -2173,41 +2076,55 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN("TABLE "));
if (create_info_arg && create_info_arg->if_not_exists())
packet->append(STRING_WITH_LEN("IF NOT EXISTS "));
- if (table_list->schema_table)
+
+ if (force_name)
{
- alias.str= table_list->schema_table->table_name;
- alias.length= strlen(alias.str);
+ if (force_db)
+ {
+ append_identifier(thd, packet, force_db, strlen(force_db));
+ packet->append(STRING_WITH_LEN("."));
+ }
+ append_identifier(thd, packet, force_name, strlen(force_name));
}
else
{
- if (lower_case_table_names == 2)
+ if (table_list->schema_table)
{
- alias.str= table->alias.c_ptr();
- alias.length= table->alias.length();
+ alias.str= table_list->schema_table->table_name;
+ alias.length= strlen(alias.str);
}
else
- alias= share->table_name;
- }
+ {
+ if (lower_case_table_names == 2)
+ {
+ alias.str= table->alias.c_ptr();
+ alias.length= table->alias.length();
+ }
+ else
+ alias= share->table_name;
+ }
- /*
- Print the database before the table name if told to do that. The
- database name is only printed in the event that it is different
- from the current database. The main reason for doing this is to
- avoid having to update gazillions of tests and result files, but
- it also saves a few bytes of the binary log.
- */
- if (with_db_name == WITH_DB_NAME)
- {
- const LEX_CSTRING *const db=
- table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db;
- if (!thd->db.str || cmp(db, &thd->db))
- {
- append_identifier(thd, packet, db);
- packet->append(STRING_WITH_LEN("."));
+ /*
+ Print the database before the table name if told to do that. The
+ database name is only printed in the event that it is different
+ from the current database. The main reason for doing this is to
+ avoid having to update gazillions of tests and result files, but
+ it also saves a few bytes of the binary log.
+ */
+ if (with_db_name == WITH_DB_NAME)
+ {
+ const LEX_CSTRING *const db=
+ table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db;
+ if (!thd->db.str || cmp(db, &thd->db))
+ {
+ append_identifier(thd, packet, db);
+ packet->append(STRING_WITH_LEN("."));
+ }
}
+
+ append_identifier(thd, packet, &alias);
}
- append_identifier(thd, packet, &alias);
packet->append(STRING_WITH_LEN(" (\n"));
/*
We need this to get default values from the table
@@ -2243,12 +2160,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
field->sql_type(type);
packet->append(type.ptr(), type.length(), system_charset_info);
- DBUG_EXECUTE_IF("sql_type",
- packet->append(" /* ");
- packet->append(field->type_handler()->version().ptr());
- packet->append(" */ ");
- );
-
if (field->has_charset() && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
{
if (field->charset() != share->table_charset)
@@ -2352,6 +2263,14 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
+ if (period.name)
+ {
+ append_period(thd, packet,
+ period.start_field(share)->field_name,
+ period.end_field(share)->field_name,
+ period.name, true);
+ }
+
key_info= table->s->key_info;
primary_key= share->primary_key;
@@ -2386,7 +2305,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" ("));
- for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
+ uint key_parts= key_info->user_defined_key_parts;
+ if (key_info->without_overlaps)
+ key_parts-= 2;
+
+ for (uint j=0 ; j < key_parts ; j++,key_part++)
{
Field *field= key_part->field;
if (field->invisible > INVISIBLE_USER)
@@ -2406,6 +2329,14 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
key_part->field->charset()->mbmaxlen);
}
}
+
+ if (key_info->without_overlaps)
+ {
+ packet->append(',');
+ append_identifier(thd, packet, &share->period.name);
+ packet->append(STRING_WITH_LEN(" WITHOUT OVERLAPS"));
+ }
+
packet->append(')');
store_key_options(thd, packet, table, &table->key_info[i]);
if (key_info->parser)
@@ -2439,15 +2370,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
}
}
- if (period.name)
- {
- append_period(thd, packet,
- period.start_field(share)->field_name,
- period.end_field(share)->field_name,
- period.name, true);
- }
-
-
/*
Get possible foreign key definitions stored in InnoDB and append them
to the CREATE TABLE statement
@@ -2969,8 +2891,6 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
server_threads.iterate(list_callback, &arg);
ulonglong now= microsecond_interval_timer();
- char buff[20]; // For progress
- String store_buffer(buff, sizeof(buff), system_charset_info);
while (auto thd_info= arg.thread_infos.get())
{
@@ -2996,7 +2916,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
protocol->store_null();
if (!thd->variables.old_mode &&
!(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO))
- protocol->store(thd_info->progress, 3, &store_buffer);
+ protocol->store_double(thd_info->progress, 3);
if (protocol->write())
break; /* purecov: inspected */
}
@@ -3159,8 +3079,8 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
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;
+ calling_user= (thd->security_ctx->master_access & PRIV_STMT_SHOW_EXPLAIN) ?
+ NullS : thd->security_ctx->priv_user;
if ((tmp= find_thread_by_id(thread_id)))
{
@@ -3277,8 +3197,9 @@ static my_bool processlist_callback(THD *tmp, processlist_callback_arg *arg)
const char *val;
ulonglong max_counter;
bool got_thd_data;
- char *user= arg->thd->security_ctx->master_access & PROCESS_ACL ?
- NullS : arg->thd->security_ctx->priv_user;
+ char *user=
+ arg->thd->security_ctx->master_access & PRIV_STMT_SHOW_PROCESSLIST ?
+ NullS : arg->thd->security_ctx->priv_user;
if ((!tmp->vio_ok() && !tmp->system_thread) ||
(user && (tmp->system_thread || !tmp_sctx->user ||
@@ -3411,8 +3332,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
Status functions
*****************************************************************************/
-static DYNAMIC_ARRAY all_status_vars;
+DYNAMIC_ARRAY all_status_vars;
static bool status_vars_inited= 0;
+ulonglong status_var_array_version= 0;
C_MODE_START
static int show_var_cmp(const void *var1, const void *var2)
@@ -3440,6 +3362,7 @@ static void shrink_var_array(DYNAMIC_ARRAY *array)
}
else // array is completely empty - delete it
delete_dynamic(array);
+ status_var_array_version++;
}
/*
@@ -3467,7 +3390,8 @@ int add_status_vars(SHOW_VAR *list)
if (status_vars_inited)
mysql_rwlock_wrlock(&LOCK_all_status_vars);
if (!all_status_vars.buffer && // array is not allocated yet - do it now
- my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 250, 50, MYF(0)))
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &all_status_vars,
+ sizeof(SHOW_VAR), 250, 50, MYF(0)))
{
res= 1;
goto err;
@@ -3478,6 +3402,7 @@ int add_status_vars(SHOW_VAR *list)
all_status_vars.elements--; // but next insert_dynamic should overwite it
if (status_vars_inited)
sort_dynamic(&all_status_vars, show_var_cmp);
+ status_var_array_version++;
err:
if (status_vars_inited)
mysql_rwlock_unlock(&LOCK_all_status_vars);
@@ -3496,6 +3421,7 @@ void init_status_vars()
{
status_vars_inited=1;
sort_dynamic(&all_status_vars, show_var_cmp);
+ status_var_array_version++;
}
void reset_status_vars()
@@ -3522,6 +3448,7 @@ void reset_status_vars()
void free_status_vars()
{
delete_dynamic(&all_status_vars);
+ status_var_array_version++;
}
/*
@@ -3583,6 +3510,11 @@ void remove_status_vars(SHOW_VAR *list)
}
}
+/* Current version of the all_status_vars. */
+ulonglong get_status_vars_version(void)
+{
+ return status_var_array_version;
+}
/**
@brief Returns the value of a system or a status variable.
@@ -3637,12 +3569,18 @@ const char* get_one_variable(THD *thd,
/* fall through */
case SHOW_ULONG:
case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
+#ifndef _WIN64
+ case SHOW_SIZE_T:
+#endif
end= int10_to_str(*(long*) value, buff, 10);
break;
case SHOW_LONGLONG_STATUS:
value= ((char *) status_var + (intptr) value);
/* fall through */
case SHOW_ULONGLONG:
+#ifdef _WIN64
+ case SHOW_SIZE_T:
+#endif
end= longlong10_to_str(*(longlong*) value, buff, 10);
break;
case SHOW_HA_ROWS:
@@ -3976,9 +3914,9 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
ST_SCHEMA_TABLE *schema_table= table->schema_table;
ST_FIELD_INFO *field_info= schema_table->fields_info;
const char *field_name1= schema_table->idx_field1 >= 0 ?
- field_info[schema_table->idx_field1].field_name : "";
+ field_info[schema_table->idx_field1].name().str : "";
const char *field_name2= schema_table->idx_field2 >= 0 ?
- field_info[schema_table->idx_field2].field_name : "";
+ field_info[schema_table->idx_field2].name().str : "";
if (item_func->functype() == Item_func::EQ_FUNC ||
item_func->functype() == Item_func::EQUAL_FUNC)
@@ -4014,18 +3952,18 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
return 1;
/* Lookup value is database name */
- if (!cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
- (uchar *) item_field->field_name.str,
- item_field->field_name.length))
+ if (!cs->strnncollsp(field_name1, strlen(field_name1),
+ item_field->field_name.str,
+ item_field->field_name.length))
{
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,
- strlen(field_name2),
- (uchar *) item_field->field_name.str,
- item_field->field_name.length))
+ else if (!cs->strnncollsp(field_name2,
+ strlen(field_name2),
+ item_field->field_name.str,
+ item_field->field_name.length))
{
thd->make_lex_string(&lookup_field_vals->table_value,
tmp_str->ptr(), tmp_str->length());
@@ -4114,16 +4052,16 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table)
ST_SCHEMA_TABLE *schema_table= table->schema_table;
ST_FIELD_INFO *field_info= schema_table->fields_info;
const char *field_name1= schema_table->idx_field1 >= 0 ?
- field_info[schema_table->idx_field1].field_name : "";
+ field_info[schema_table->idx_field1].name().str : "";
const char *field_name2= schema_table->idx_field2 >= 0 ?
- field_info[schema_table->idx_field2].field_name : "";
+ field_info[schema_table->idx_field2].name().str : "";
if (table->table != item_field->field->table ||
- (cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1),
- (uchar *) item_field->field_name.str,
- item_field->field_name.length) &&
- cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2),
- (uchar *) item_field->field_name.str,
- item_field->field_name.length)))
+ (cs->strnncollsp(field_name1, strlen(field_name1),
+ item_field->field_name.str,
+ item_field->field_name.length) &&
+ cs->strnncollsp(field_name2, strlen(field_name2),
+ item_field->field_name.str,
+ item_field->field_name.length)))
return 0;
}
else if (item->type() == Item::EXPR_CACHE_ITEM)
@@ -4546,8 +4484,7 @@ static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl,
char path[FN_REFLEN];
build_table_filename(path, sizeof(path) - 1,
db->str, table->str, reg_ext, 0);
- bool is_sequence;
- if (dd_frm_type(thd, path, &engine_name, &is_sequence) == TABLE_TYPE_NORMAL)
+ if (dd_frm_type(thd, path, &engine_name) == TABLE_TYPE_NORMAL)
tl->option= engine_name.str;
}
}
@@ -4819,18 +4756,18 @@ uint get_table_open_method(TABLE_LIST *tables,
if (schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
{
Field **ptr, *field;
- int table_open_method= 0, field_indx= 0;
+ uint table_open_method= 0, field_indx= 0;
uint star_table_open_method= OPEN_FULL_TABLE;
bool used_star= true; // true if '*' is used in select
for (ptr=tables->table->field; (field= *ptr) ; ptr++)
{
+ const ST_FIELD_INFO &def= schema_table->fields_info[field_indx];
star_table_open_method=
- MY_MIN(star_table_open_method,
- schema_table->fields_info[field_indx].open_method);
+ MY_MIN(star_table_open_method, (uint) def.open_method());
if (bitmap_is_set(tables->table->read_set, field->field_index))
{
used_star= false;
- table_open_method|= schema_table->fields_info[field_indx].open_method;
+ table_open_method|= (uint) def.open_method();
}
field_indx++;
}
@@ -4871,8 +4808,9 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
bool can_deadlock)
{
bool error;
- table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str,
- MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table->mdl_request, MDL_key::TABLE, table->db.str,
+ table->table_name.str, MDL_SHARED_HIGH_PRIO,
+ MDL_TRANSACTION);
if (can_deadlock)
{
@@ -4994,8 +4932,8 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table,
if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY)
{
- init_sql_alloc(&tbl.mem_root, "fill_schema_table_from_frm",
- TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(key_memory_table_triggers_list,
+ &tbl.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (!Table_triggers_list::check_n_load(thd, db_name,
table_name, &tbl, 1))
{
@@ -5149,7 +5087,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
IS_table_read_plan *plan= tables->is_table_read_plan;
enum enum_schema_tables schema_table_idx;
- Dynamic_array<LEX_CSTRING*> db_names;
+ Dynamic_array<LEX_CSTRING*> db_names(PSI_INSTRUMENT_MEM);
Item *partial_cond= plan->partial_cond;
int error= 1;
Open_tables_backup open_tables_state_backup;
@@ -5218,7 +5156,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
goto err;
/* Use tmp_mem_root to allocate data for opened tables */
- init_alloc_root(&tmp_mem_root, "get_all_tables", SHOW_ALLOC_BLOCK_SIZE,
+ init_alloc_root(PSI_INSTRUMENT_ME, &tmp_mem_root, SHOW_ALLOC_BLOCK_SIZE,
SHOW_ALLOC_BLOCK_SIZE, MY_THREAD_SPECIFIC);
for (size_t i=0; i < db_names.elements(); i++)
@@ -5233,7 +5171,7 @@ 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
{
- Dynamic_array<LEX_CSTRING*> table_names;
+ Dynamic_array<LEX_CSTRING*> table_names(PSI_INSTRUMENT_MEM);
int res= make_table_name_list(thd, &table_names, lex,
&plan->lookup_field_vals, db_name);
if (unlikely(res == 2)) /* Not fatal error, continue */
@@ -5331,14 +5269,17 @@ err:
}
-bool store_schema_shemata(THD* thd, TABLE *table, LEX_CSTRING *db_name,
- CHARSET_INFO *cs)
+bool store_schema_schemata(THD* thd, TABLE *table, LEX_CSTRING *db_name,
+ CHARSET_INFO *cs, LEX_CSTRING *schema_comment= NULL)
{
restore_record(table, s->default_values);
table->field[0]->store(STRING_WITH_LEN("def"), system_charset_info);
table->field[1]->store(db_name->str, db_name->length, system_charset_info);
table->field[2]->store(cs->csname, strlen(cs->csname), system_charset_info);
table->field[3]->store(cs->name, strlen(cs->name), system_charset_info);
+ if (schema_comment)
+ table->field[5]->store(schema_comment->str, schema_comment->length,
+ system_charset_info);
return schema_table_store_record(thd, table);
}
@@ -5374,7 +5315,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
*/
LOOKUP_FIELD_VALUES lookup_field_vals;
- Dynamic_array<LEX_CSTRING*> db_names;
+ Dynamic_array<LEX_CSTRING*> db_names(PSI_INSTRUMENT_MEM);
Schema_specification_st create;
TABLE *table= tables->table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -5405,8 +5346,8 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
DBUG_ASSERT(db_name->length <= NAME_LEN);
if (db_name == &INFORMATION_SCHEMA_NAME)
{
- if (store_schema_shemata(thd, table, db_name,
- system_charset_info))
+ if (store_schema_schemata(thd, table, db_name,
+ system_charset_info))
DBUG_RETURN(1);
continue;
}
@@ -5414,13 +5355,14 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) ||
acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, false) ||
(sctx->priv_role[0] ?
- acl_get("", "", sctx->priv_role, db_name->str, false) : 0) ||
+ acl_get("", "", sctx->priv_role, db_name->str, false) : NO_ACL) ||
!check_grant_db(thd, db_name->str))
#endif
{
load_db_opt_by_name(thd, db_name->str, &create);
- if (store_schema_shemata(thd, table, db_name,
- create.default_table_charset))
+ if (store_schema_schemata(thd, table, db_name,
+ create.default_table_charset,
+ create.schema_comment))
DBUG_RETURN(1);
}
}
@@ -5587,10 +5529,25 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
str.qs_append(STRING_WITH_LEN(" partitioned"));
#endif
- if (share->transactional != HA_CHOICE_UNDEF)
- {
+ /*
+ Write transactional=0|1 for tables where the user has specified the
+ option or for tables that supports both transactional and non
+ transactional tables
+ */
+ if (share->transactional != HA_CHOICE_UNDEF ||
+ (share->db_type() &&
+ share->db_type()->flags & HTON_TRANSACTIONAL_AND_NON_TRANSACTIONAL &&
+ file))
+ {
+ uint choice= share->transactional;
+ if (choice == HA_CHOICE_UNDEF)
+ choice= ((file->ha_table_flags() &
+ (HA_NO_TRANSACTIONS | HA_CRASH_SAFE)) ==
+ HA_NO_TRANSACTIONS ?
+ HA_CHOICE_NO : HA_CHOICE_YES);
+
str.qs_append(STRING_WITH_LEN(" transactional="));
- str.qs_append(ha_choice_values[(uint) share->transactional]);
+ str.qs_append(ha_choice_values[choice]);
}
append_create_options(thd, &str, share->option_list, false, 0);
@@ -5979,16 +5936,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
rather than in SHOW COLUMNS
*/
if (thd->is_error())
- {
- /*
- The the query was aborted because examined rows exceeded limit.
- Don't send the warning here. It is done later, in handle_select().
- */
- push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- thd->get_stmt_da()->sql_errno(),
- thd->get_stmt_da()->message());
- thd->clear_error();
- }
+ convert_error_to_warning(thd);
res= 0;
}
DBUG_RETURN(res);
@@ -6018,7 +5966,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
restore_record(table, s->default_values);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- uint col_access;
+ ulonglong col_access;
check_access(thd,SELECT_ACL, db_name->str,
&tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table));
col_access= get_column_grant(thd, &tables->grant,
@@ -6189,16 +6137,15 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
LEX_CSTRING yesno[2]= {{ STRING_WITH_LEN("NO") },
{ STRING_WITH_LEN("YES") }};
LEX_CSTRING *tmp;
- const char *option_name= show_comp_option_name[(int) hton->state];
+ const char *option_name= default_type != hton ? yesno[1].str
+ : "DEFAULT";
restore_record(table, s->default_values);
table->field[0]->store(name->str, name->length, scs);
- if (hton->state == SHOW_OPTION_YES && default_type == hton)
- option_name= "DEFAULT";
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[MY_TEST(hton->commit)];
+ tmp= &yesno[MY_TEST(hton->commit && !(hton->flags & HTON_NO_ROLLBACK))];
table->field[3]->store(tmp->str, tmp->length, scs);
table->field[3]->set_notnull();
tmp= &yesno[MY_TEST(hton->prepare)];
@@ -6358,11 +6305,11 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name);
proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer);
sql_mode= (sql_mode_t) proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
- sph= Sp_handler::handler_mysql_proc((stored_procedure_type)
+ sph= Sp_handler::handler_mysql_proc((enum_sp_type)
proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
val_int());
- if (!sph || sph->type() == TYPE_ENUM_PACKAGE ||
- sph->type() == TYPE_ENUM_PACKAGE_BODY)
+ if (!sph || sph->type() == SP_TYPE_PACKAGE ||
+ sph->type() == SP_TYPE_PACKAGE_BODY)
DBUG_RETURN(0);
if (!full_access)
@@ -6373,7 +6320,7 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root,
&params);
- if (sph->type() == TYPE_ENUM_FUNCTION)
+ if (sph->type() == SP_TYPE_FUNCTION)
proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root,
&returns);
sp= sph->sp_load_for_information_schema(thd, proc_table, db, name,
@@ -6386,7 +6333,7 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
Sql_mode_save sql_mode_backup(thd);
thd->variables.sql_mode= sql_mode;
- if (sph->type() == TYPE_ENUM_FUNCTION)
+ if (sph->type() == SP_TYPE_FUNCTION)
{
restore_record(table, s->default_values);
table->field[0]->store(STRING_WITH_LEN("def"), cs);
@@ -6470,7 +6417,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db);
proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name);
proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer);
- sph= Sp_handler::handler_mysql_proc((stored_procedure_type)
+ sph= Sp_handler::handler_mysql_proc((enum_sp_type)
proc_table->field[MYSQL_PROC_MYSQL_TYPE]->
val_int());
if (!sph)
@@ -6499,7 +6446,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
copy_field_as_string(table->field[4],
proc_table->field[MYSQL_PROC_MYSQL_TYPE]);
- if (sph->type() == TYPE_ENUM_FUNCTION)
+ if (sph->type() == SP_TYPE_FUNCTION)
{
sp_head *sp;
bool free_sp_head;
@@ -6585,7 +6532,6 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool full_access;
char definer[USER_HOST_BUFF_SIZE];
- Open_tables_backup open_tables_state_backup;
enum enum_schema_tables schema_table_idx=
get_schema_table_idx(tables->schema_table);
DBUG_ENTER("fill_schema_proc");
@@ -6600,8 +6546,12 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.lock_type= TL_READ;
full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE,
1, TRUE);
- if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
+
+ start_new_trans new_trans(thd);
+
+ if (!(proc_table= open_proc_table_for_read(thd)))
{
+ new_trans.restore_old_transaction();
DBUG_RETURN(1);
}
@@ -6643,7 +6593,9 @@ err:
if (proc_table->file->inited)
(void) proc_table->file->ha_index_end();
- close_system_tables(thd, &open_tables_state_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
+
thd->variables.sql_mode = sql_mode_was;
DBUG_RETURN(res);
}
@@ -6799,12 +6751,11 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
else
{
TABLE_LIST table_list;
- uint view_access;
table_list.reset();
table_list.db= tables->db;
table_list.table_name= tables->table_name;
table_list.grant.privilege= thd->col_access;
- view_access= get_table_grant(thd, &table_list);
+ privilege_t view_access(get_table_grant(thd, &table_list));
if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) ==
(SHOW_VIEW_ACL|SELECT_ACL))
tables->allowed_show= TRUE;
@@ -7284,56 +7235,6 @@ static void collect_partition_expr(THD *thd, List<const char> &field_list,
return;
}
-
-/*
- Convert a string in a given character set to a string which can be
- used for FRM file storage in which case use_hex is TRUE and we store
- the character constants as hex strings in the character set encoding
- their field have. In the case of SHOW CREATE TABLE and the
- PARTITIONS information schema table we instead provide utf8 strings
- to the user and convert to the utf8 character set.
-
- SYNOPSIS
- get_cs_converted_part_value_from_string()
- item Item from which constant comes
- input_str String as provided by val_str after
- conversion to character set
- output_str Out value: The string created
- cs Character set string is encoded in
- NULL for INT_RESULT's here
- use_hex TRUE => hex string created
- FALSE => utf8 constant string created
-
- RETURN VALUES
- TRUE Error
- FALSE Ok
-*/
-
-int get_cs_converted_part_value_from_string(THD *thd,
- Item *item,
- String *input_str,
- String *output_str,
- CHARSET_INFO *cs,
- bool use_hex)
-{
- if (item->result_type() == INT_RESULT)
- {
- longlong value= item->val_int();
- output_str->set(value, system_charset_info);
- return FALSE;
- }
- if (!input_str)
- {
- my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0));
- return TRUE;
- }
- get_cs_converted_string_value(thd,
- input_str,
- output_str,
- cs,
- use_hex);
- return FALSE;
-}
#endif
@@ -7425,24 +7326,14 @@ static int get_partition_column_description(THD *thd, partition_info *part_info,
tmp_str.append("NULL");
else
{
- char buffer[MAX_KEY_LENGTH];
- String str(buffer, sizeof(buffer), &my_charset_bin);
- String val_conv;
Item *item= col_val->item_expression;
-
- if (!(item= part_info->get_column_item(item,
- part_info->part_field_array[i])))
- {
- DBUG_RETURN(1);
- }
- String *res= item->val_str(&str);
- if (get_cs_converted_part_value_from_string(thd, item, res, &val_conv,
- part_info->part_field_array[i]->charset(),
- FALSE))
- {
- DBUG_RETURN(1);
- }
- tmp_str.append(val_conv);
+ StringBuffer<MAX_KEY_LENGTH> val;
+ const Field *field= part_info->part_field_array[i];
+ const Type_handler *th= field->type_handler();
+ th->partition_field_append_value(&val, item,
+ field->charset(),
+ PARTITION_VALUE_PRINT_MODE_SHOW);
+ tmp_str.append(val);
}
if (i != num_elements - 1)
tmp_str.append(",");
@@ -8179,122 +8070,26 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
{
uint field_count= 0;
- Item *item;
TABLE *table;
- List<Item> field_list;
ST_SCHEMA_TABLE *schema_table= table_list->schema_table;
- ST_FIELD_INFO *fields_info= schema_table->fields_info;
- CHARSET_INFO *cs= system_charset_info;
- MEM_ROOT *mem_root= thd->mem_root;
+ ST_FIELD_INFO *fields= schema_table->fields_info;
bool need_all_fieds= table_list->schema_table_reformed || // SHOW command
thd->lex->only_view_structure(); // need table structure
DBUG_ENTER("create_schema_table");
- for (; fields_info->field_name; fields_info++)
- {
- size_t field_name_length= strlen(fields_info->field_name);
- switch (fields_info->field_type) {
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_INT24:
- if (!(item= new (mem_root)
- Item_return_int(thd, fields_info->field_name,
- fields_info->field_length,
- fields_info->field_type,
- fields_info->value)))
- {
- DBUG_RETURN(0);
- }
- item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED);
- break;
- case MYSQL_TYPE_DATE:
- if (!(item=new (mem_root)
- Item_return_date_time(thd, fields_info->field_name,
- (uint)field_name_length,
- fields_info->field_type)))
- DBUG_RETURN(0);
- break;
- case MYSQL_TYPE_TIME:
- if (!(item=new (mem_root)
- Item_return_date_time(thd, fields_info->field_name,
- (uint)field_name_length,
- fields_info->field_type)))
- DBUG_RETURN(0);
- break;
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_DATETIME:
- if (!(item=new (mem_root)
- Item_return_date_time(thd, fields_info->field_name,
- (uint)field_name_length,
- fields_info->field_type,
- fields_info->field_length)))
- DBUG_RETURN(0);
- item->decimals= fields_info->field_length;
- break;
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- if ((item= new (mem_root)
- Item_float(thd, fields_info->field_name, 0.0,
- NOT_FIXED_DEC,
- fields_info->field_length)) == NULL)
- DBUG_RETURN(NULL);
- break;
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_NEWDECIMAL:
- if (!(item= new (mem_root)
- Item_decimal(thd, (longlong) fields_info->value, false)))
- {
- DBUG_RETURN(0);
- }
- /*
- Create a type holder, as we want the type of the item to defined
- the type of the object, not the value
- */
- if (!(item= new (mem_root) Item_type_holder(thd, item)))
- DBUG_RETURN(0);
- item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED);
- item->decimals= fields_info->field_length%10;
- item->max_length= (fields_info->field_length/100)%100;
- if (item->unsigned_flag == 0)
- item->max_length+= 1;
- if (item->decimals > 0)
- item->max_length+= 1;
- item->set_name(thd, fields_info->field_name, field_name_length, cs);
- break;
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- if (!(item= new (mem_root) Item_blob(thd, fields_info->field_name,
- fields_info->field_length)))
- DBUG_RETURN(0);
- break;
- default:
- /* Don't let unimplemented types pass through. Could be a grave error. */
- DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING);
- if (!(item= new (mem_root) Item_empty_string(thd, "",
- fields_info->field_length, cs)))
- DBUG_RETURN(0);
- item->set_name(thd, fields_info->field_name, field_name_length, cs);
- break;
- }
- field_list.push_back(item, thd->mem_root);
- item->maybe_null= (fields_info->field_flags & MY_I_S_MAYBE_NULL);
+ for (; !fields->end_marker(); fields++)
field_count++;
- }
+
TMP_TABLE_PARAM *tmp_table_param = new (thd->mem_root) TMP_TABLE_PARAM;
tmp_table_param->init();
- tmp_table_param->table_charset= cs;
+ tmp_table_param->table_charset= system_charset_info;
tmp_table_param->field_count= field_count;
tmp_table_param->schema_table= 1;
SELECT_LEX *select_lex= table_list->select_lex;
bool keep_row_order= is_show_command(thd);
- if (!(table= create_tmp_table(thd, tmp_table_param, field_list, (ORDER*) 0, 0,
- 0, (select_lex->options | thd->variables.option_bits |
- TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR,
- &table_list->alias, !need_all_fieds, keep_row_order)))
+ if (!(table= create_tmp_table_for_schema(thd, tmp_table_param, *schema_table,
+ (select_lex->options | thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS),
+ table_list->alias, !need_all_fieds, keep_row_order)))
DBUG_RETURN(0);
my_bitmap_map* bitmaps=
(my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count));
@@ -8326,19 +8121,16 @@ static int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
ST_FIELD_INFO *field_info= schema_table->fields_info;
Name_resolution_context *context= &thd->lex->first_select_lex()->context;
- for (; field_info->field_name; field_info++)
+ for (; !field_info->end_marker(); field_info++)
{
- if (field_info->old_name)
+ if (field_info->old_name().str)
{
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name)};
+ LEX_CSTRING field_name= field_info->name();
Item_field *field= new (thd->mem_root)
- Item_field(thd, context, NullS, NullS, &field_name);
+ Item_field(thd, context, field_name);
if (field)
{
- field->set_name(thd, field_info->old_name,
- strlen(field_info->old_name),
- system_charset_info);
+ field->set_name(thd, field_info->old_name());
if (add_item_to_list(thd, field))
return 1;
}
@@ -8359,22 +8151,19 @@ int make_schemata_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
ST_FIELD_INFO *field_info= &schema_table->fields_info[1];
String buffer(tmp,sizeof(tmp), system_charset_info);
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name) };
-
Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, &field_name);
+ field_info->name());
if (!field || add_item_to_list(thd, field))
return 1;
buffer.length(0);
- buffer.append(field_info->old_name);
+ buffer.append(field_info->old_name());
if (lex->wild && lex->wild->ptr())
{
buffer.append(STRING_WITH_LEN(" ("));
buffer.append(lex->wild->ptr());
buffer.append(')');
}
- field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info);
+ field->set_name(thd, &buffer);
}
return 0;
}
@@ -8383,15 +8172,14 @@ int make_schemata_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
char tmp[128];
- String buffer(tmp,sizeof(tmp), thd->charset());
+ String buffer(tmp, sizeof(tmp), system_charset_info);
LEX *lex= thd->lex;
Name_resolution_context *context= &lex->first_select_lex()->context;
ST_FIELD_INFO *field_info= &schema_table->fields_info[2];
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name) };
+ LEX_CSTRING field_name= field_info->name();
buffer.length(0);
- buffer.append(field_info->old_name);
+ buffer.append(field_info->old_name());
buffer.append(&lex->first_select_lex()->db);
if (lex->wild && lex->wild->ptr())
{
@@ -8399,22 +8187,17 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
buffer.append(lex->wild->ptr());
buffer.append(')');
}
- Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, &field_name);
+ Item_field *field= new (thd->mem_root) Item_field(thd, context, field_name);
if (add_item_to_list(thd, field))
return 1;
- field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info);
+ field->set_name(thd, &buffer);
if (thd->lex->verbose)
{
field_info= &schema_table->fields_info[3];
- LEX_CSTRING field_name2= {field_info->field_name,
- strlen(field_info->field_name) };
- field= new (thd->mem_root) Item_field(thd, context, NullS, NullS,
- &field_name2);
+ field= new (thd->mem_root) Item_field(thd, context, field_info->name());
if (add_item_to_list(thd, field))
return 1;
- field->set_name(thd, field_info->old_name, strlen(field_info->old_name),
- system_charset_info);
+ field->set_name(thd, field_info->old_name());
}
return 0;
}
@@ -8430,19 +8213,15 @@ int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
for (; *field_num >= 0; field_num++)
{
field_info= &schema_table->fields_info[*field_num];
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name)};
if (!thd->lex->verbose && (*field_num == 14 ||
*field_num == 18 ||
*field_num == 19))
continue;
Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, &field_name);
+ field_info->name());
if (field)
{
- field->set_name(thd, field_info->old_name,
- strlen(field_info->old_name),
- system_charset_info);
+ field->set_name(thd, field_info->old_name());
if (add_item_to_list(thd, field))
return 1;
}
@@ -8461,15 +8240,11 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
for (; *field_num >= 0; field_num++)
{
field_info= &schema_table->fields_info[*field_num];
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name)};
Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, &field_name);
+ field_info->name());
if (field)
{
- field->set_name(thd, field_info->old_name,
- strlen(field_info->old_name),
- system_charset_info);
+ field->set_name(thd, field_info->old_name());
if (add_item_to_list(thd, field))
return 1;
}
@@ -8488,15 +8263,11 @@ int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
for (; *field_num >= 0; field_num++)
{
field_info= &schema_table->fields_info[*field_num];
- LEX_CSTRING field_name= {field_info->field_name,
- strlen(field_info->field_name)};
Item_field *field= new (thd->mem_root) Item_field(thd, context,
- NullS, NullS, &field_name);
+ field_info->name());
if (field)
{
- field->set_name(thd, field_info->old_name,
- strlen(field_info->old_name),
- system_charset_info);
+ field->set_name(thd, field_info->old_name());
if (add_item_to_list(thd, field))
return 1;
}
@@ -8973,7 +8744,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_hton(plugin);
- if (hton->fill_is_table && hton->state == SHOW_OPTION_YES)
+ if (hton->fill_is_table)
hton->fill_is_table(hton, thd, args->tables, args->cond,
get_schema_table_idx(args->tables->schema_table));
return false;
@@ -9079,809 +8850,654 @@ int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond)
}
+namespace Show {
+
ST_FIELD_INFO schema_fields_info[]=
{
- {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"SCHEMA_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database",
- SKIP_OPEN_TABLE},
- {"DEFAULT_CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- SKIP_OPEN_TABLE},
- {"DEFAULT_COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- SKIP_OPEN_TABLE},
- {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("CATALOG_NAME", Catalog(), NOT_NULL),
+ Column("SCHEMA_NAME", Name(), NOT_NULL, "Database"),
+ Column("DEFAULT_CHARACTER_SET_NAME", CSName(), NOT_NULL),
+ Column("DEFAULT_COLLATION_NAME", CSName(), NOT_NULL),
+ Column("SQL_PATH", Varchar(FN_REFLEN), NULLABLE),
+ Column("SCHEMA_COMMENT", Varchar(DATABASE_COMMENT_MAXLEN), NOT_NULL),
+ CEnd()
};
ST_FIELD_INFO tables_fields_info[]=
{
- {"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, "Name",
- SKIP_OPEN_TABLE},
- {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine", OPEN_FRM_ONLY},
- {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version", OPEN_FRM_ONLY},
- {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format", OPEN_FULL_TABLE},
- {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows", OPEN_FULL_TABLE},
- {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length", OPEN_FULL_TABLE},
- {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length", OPEN_FULL_TABLE},
- {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length", OPEN_FULL_TABLE},
- {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length", OPEN_FULL_TABLE},
- {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free", OPEN_FULL_TABLE},
- {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment", OPEN_FULL_TABLE},
- {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time", OPEN_FULL_TABLE},
- {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time", OPEN_FULL_TABLE},
- {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time", OPEN_FULL_TABLE},
- {"TABLE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation",
- OPEN_FRM_ONLY},
- {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", OPEN_FULL_TABLE},
- {"CREATE_OPTIONS", 2048, MYSQL_TYPE_STRING, 0, 1, "Create_options",
- OPEN_FULL_TABLE},
- {"TABLE_COMMENT", TABLE_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
- "Comment", OPEN_FRM_ONLY},
- {"MAX_INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_index_length", OPEN_FULL_TABLE},
- {"TEMPORARY", 1, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "Temporary", OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL),
+ Column("TABLE_NAME", Name(), NOT_NULL, "Name"),
+ Column("TABLE_TYPE", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("ENGINE", Name(), NULLABLE, "Engine", OPEN_FRM_ONLY),
+ Column("VERSION", ULonglong(), NULLABLE, "Version", OPEN_FRM_ONLY),
+ Column("ROW_FORMAT", Varchar(10), NULLABLE, "Row_format", OPEN_FULL_TABLE),
+ Column("TABLE_ROWS", ULonglong(), NULLABLE, "Rows", OPEN_FULL_TABLE),
+ Column("AVG_ROW_LENGTH", ULonglong(), NULLABLE, "Avg_row_length",
+ OPEN_FULL_TABLE),
+ Column("DATA_LENGTH", ULonglong(), NULLABLE, "Data_length",OPEN_FULL_TABLE),
+ Column("MAX_DATA_LENGTH", ULonglong(), NULLABLE, "Max_data_length",
+ OPEN_FULL_TABLE),
+ Column("INDEX_LENGTH", ULonglong(), NULLABLE, "Index_length",OPEN_FULL_TABLE),
+ Column("DATA_FREE", ULonglong(), NULLABLE, "Data_free", OPEN_FULL_TABLE),
+ Column("AUTO_INCREMENT", ULonglong(), NULLABLE, "Auto_increment",
+ OPEN_FULL_TABLE),
+ Column("CREATE_TIME", Datetime(0), NULLABLE, "Create_time",OPEN_FULL_TABLE),
+ Column("UPDATE_TIME", Datetime(0), NULLABLE, "Update_time",OPEN_FULL_TABLE),
+ Column("CHECK_TIME", Datetime(0), NULLABLE, "Check_time", OPEN_FULL_TABLE),
+ Column("TABLE_COLLATION", CSName(), NULLABLE, "Collation", OPEN_FRM_ONLY),
+ Column("CHECKSUM", ULonglong(), NULLABLE, "Checksum", OPEN_FULL_TABLE),
+ Column("CREATE_OPTIONS", Varchar(2048),NULLABLE, "Create_options",
+ OPEN_FULL_TABLE),
+ Column("TABLE_COMMENT", Varchar(TABLE_COMMENT_MAXLEN),
+ NOT_NULL, "Comment", OPEN_FRM_ONLY),
+ Column("MAX_INDEX_LENGTH",ULonglong(), NULLABLE, "Max_index_length",
+ OPEN_FULL_TABLE),
+ Column("TEMPORARY", Varchar(1), NULLABLE, "Temporary", OPEN_FRM_ONLY),
+ CEnd()
};
ST_FIELD_INFO columns_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field",
- OPEN_FRM_ONLY},
- {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- MY_I_S_UNSIGNED, 0, OPEN_FRM_ONLY},
- {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0,
- 1, "Default", OPEN_FRM_ONLY},
- {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY},
- {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"DATETIME_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FRM_ONLY},
- {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation",
- OPEN_FRM_ONLY},
- {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type", OPEN_FRM_ONLY},
- {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key", OPEN_FRM_ONLY},
- {"EXTRA", 30, MYSQL_TYPE_STRING, 0, 0, "Extra", OPEN_FRM_ONLY},
- {"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges", OPEN_FRM_ONLY},
- {"COLUMN_COMMENT", COLUMN_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
- "Comment", OPEN_FRM_ONLY},
- {"IS_GENERATED", 6, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"GENERATION_EXPRESSION", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1,
- 0, OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("COLUMN_NAME", Name(), NOT_NULL, "Field", OPEN_FRM_ONLY),
+ Column("ORDINAL_POSITION", ULonglong(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("COLUMN_DEFAULT", Longtext(MAX_FIELD_VARCHARLENGTH),
+ NULLABLE, "Default",OPEN_FRM_ONLY),
+ Column("IS_NULLABLE", Yesno(), NOT_NULL, "Null", OPEN_FRM_ONLY),
+ Column("DATA_TYPE", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("CHARACTER_MAXIMUM_LENGTH",ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("CHARACTER_OCTET_LENGTH", ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("NUMERIC_PRECISION", ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("NUMERIC_SCALE", ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("DATETIME_PRECISION", ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("CHARACTER_SET_NAME", CSName(), NULLABLE, OPEN_FRM_ONLY),
+ Column("COLLATION_NAME", CSName(), NULLABLE, "Collation", OPEN_FRM_ONLY),
+ Column("COLUMN_TYPE", Longtext(65535), NOT_NULL, "Type", OPEN_FRM_ONLY),
+ Column("COLUMN_KEY", Varchar(3), NOT_NULL, "Key", OPEN_FRM_ONLY),
+ Column("EXTRA", Varchar(30), NOT_NULL, "Extra", OPEN_FRM_ONLY),
+ Column("PRIVILEGES", Varchar(80), NOT_NULL, "Privileges", OPEN_FRM_ONLY),
+ Column("COLUMN_COMMENT", Varchar(COLUMN_COMMENT_MAXLEN), NOT_NULL, "Comment",
+ OPEN_FRM_ONLY),
+ Column("IS_GENERATED", Varchar(6), NOT_NULL, OPEN_FRM_ONLY),
+ Column("GENERATION_EXPRESSION", Longtext(MAX_FIELD_VARCHARLENGTH),
+ NULLABLE, OPEN_FRM_ONLY),
+ CEnd()
};
ST_FIELD_INFO charsets_fields_info[]=
{
- {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Charset",
- SKIP_OPEN_TABLE},
- {"DEFAULT_COLLATE_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "Default collation", SKIP_OPEN_TABLE},
- {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description",
- SKIP_OPEN_TABLE},
- {"MAXLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("CHARACTER_SET_NAME", CSName(), NOT_NULL, "Charset"),
+ Column("DEFAULT_COLLATE_NAME", CSName(), NOT_NULL, "Default collation"),
+ Column("DESCRIPTION", Varchar(60), NOT_NULL, "Description"),
+ Column("MAXLEN", SLonglong(3), NOT_NULL, "Maxlen"),
+ CEnd()
};
ST_FIELD_INFO collation_fields_info[]=
{
- {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Collation",
- SKIP_OPEN_TABLE},
- {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Charset",
- SKIP_OPEN_TABLE},
- {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id",
- SKIP_OPEN_TABLE},
- {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default", SKIP_OPEN_TABLE},
- {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled", SKIP_OPEN_TABLE},
- {"SORTLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("COLLATION_NAME", CSName(), NOT_NULL, "Collation"),
+ Column("CHARACTER_SET_NAME", CSName(), NOT_NULL, "Charset"),
+ Column("ID", SLonglong(MY_INT32_NUM_DECIMAL_DIGITS), NOT_NULL, "Id"),
+ Column("IS_DEFAULT", Yesno(), NOT_NULL, "Default"),
+ Column("IS_COMPILED", Yesno(), NOT_NULL, "Compiled"),
+ Column("SORTLEN", SLonglong(3), NOT_NULL, "Sortlen"),
+ CEnd()
};
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},
- {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("GRANTEE", Userhost(), NOT_NULL),
+ Column("ROLE_NAME", Varchar(USERNAME_CHAR_LENGTH), NOT_NULL),
+ Column("IS_GRANTABLE", Yesno(), NOT_NULL),
+ Column("IS_DEFAULT", Yesno(), NULLABLE),
+ CEnd()
};
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}
+ Column("ROLE_NAME", Varchar(USERNAME_CHAR_LENGTH), NULLABLE),
+ CEnd()
};
ST_FIELD_INFO engines_fields_info[]=
{
- {"ENGINE", 64, MYSQL_TYPE_STRING, 0, 0, "Engine", SKIP_OPEN_TABLE},
- {"SUPPORT", 8, MYSQL_TYPE_STRING, 0, 0, "Support", SKIP_OPEN_TABLE},
- {"COMMENT", 160, MYSQL_TYPE_STRING, 0, 0, "Comment", SKIP_OPEN_TABLE},
- {"TRANSACTIONS", 3, MYSQL_TYPE_STRING, 0, 1, "Transactions", SKIP_OPEN_TABLE},
- {"XA", 3, MYSQL_TYPE_STRING, 0, 1, "XA", SKIP_OPEN_TABLE},
- {"SAVEPOINTS", 3 ,MYSQL_TYPE_STRING, 0, 1, "Savepoints", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("ENGINE", Varchar(64), NOT_NULL, "Engine"),
+ Column("SUPPORT", Varchar(8), NOT_NULL, "Support"),
+ Column("COMMENT", Varchar(160), NOT_NULL, "Comment"),
+ Column("TRANSACTIONS", Varchar(3), NULLABLE, "Transactions"),
+ Column("XA", Varchar(3), NULLABLE, "XA"),
+ Column("SAVEPOINTS", Varchar(3), NULLABLE, "Savepoints"),
+ CEnd()
};
ST_FIELD_INFO events_fields_info[]=
{
- {"EVENT_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"EVENT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db",
- SKIP_OPEN_TABLE},
- {"EVENT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
- SKIP_OPEN_TABLE},
- {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
- {"TIME_ZONE", 64, MYSQL_TYPE_STRING, 0, 0, "Time zone", SKIP_OPEN_TABLE},
- {"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
- {"EXECUTE_AT", 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at", SKIP_OPEN_TABLE},
- {"INTERVAL_VALUE", 256, MYSQL_TYPE_STRING, 0, 1, "Interval value",
- SKIP_OPEN_TABLE},
- {"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field",
- SKIP_OPEN_TABLE},
- {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts", SKIP_OPEN_TABLE},
- {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends", SKIP_OPEN_TABLE},
- {"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE},
- {"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE},
- {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE},
- {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
- {"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator", SKIP_OPEN_TABLE},
- {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "character_set_client", SKIP_OPEN_TABLE},
- {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "collation_connection", SKIP_OPEN_TABLE},
- {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "Database Collation", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ // QQ: shouldn't EVENT_CATALOG be Catalog() like in all other places?
+ Column("EVENT_CATALOG", Name(), NOT_NULL),
+ Column("EVENT_SCHEMA", Name(), NOT_NULL, "Db"),
+ Column("EVENT_NAME", Name(), NOT_NULL, "Name"),
+ Column("DEFINER", Definer(), NOT_NULL, "Definer"),
+ Column("TIME_ZONE", Varchar(64), NOT_NULL, "Time zone"),
+ Column("EVENT_BODY", Varchar(8), NOT_NULL),
+ Column("EVENT_DEFINITION", Longtext(65535), NOT_NULL),
+ Column("EVENT_TYPE", Varchar(9), NOT_NULL, "Type"),
+ Column("EXECUTE_AT", Datetime(0), NULLABLE, "Execute at"),
+ Column("INTERVAL_VALUE", Varchar(256),NULLABLE, "Interval value"),
+ Column("INTERVAL_FIELD", Varchar(18), NULLABLE, "Interval field"),
+ Column("SQL_MODE", SQLMode(), NOT_NULL),
+ Column("STARTS", Datetime(0), NULLABLE, "Starts"),
+ Column("ENDS", Datetime(0), NULLABLE, "Ends"),
+ Column("STATUS", Varchar(18), NOT_NULL, "Status"),
+ Column("ON_COMPLETION", Varchar(12), NOT_NULL),
+ Column("CREATED", Datetime(0), NOT_NULL),
+ Column("LAST_ALTERED", Datetime(0), NOT_NULL),
+ Column("LAST_EXECUTED", Datetime(0), NULLABLE),
+ Column("EVENT_COMMENT", Name(), NOT_NULL),
+ Column("ORIGINATOR", SLonglong(10),NOT_NULL,"Originator"),
+ Column("CHARACTER_SET_CLIENT", CSName(), NOT_NULL, "character_set_client"),
+ Column("COLLATION_CONNECTION", CSName(), NOT_NULL, "collation_connection"),
+ Column("DATABASE_COLLATION", CSName(), NOT_NULL, "Database Collation"),
+ CEnd()
};
ST_FIELD_INFO coll_charset_app_fields_info[]=
{
- {"COLLATION_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- SKIP_OPEN_TABLE},
- {"CHARACTER_SET_NAME", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("COLLATION_NAME", CSName(), NOT_NULL),
+ Column("CHARACTER_SET_NAME", CSName(), NOT_NULL),
+ CEnd()
};
ST_FIELD_INFO proc_fields_info[]=
{
- {"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db",
- SKIP_OPEN_TABLE},
- {"ROUTINE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
- SKIP_OPEN_TABLE},
- {"ROUTINE_TYPE", 13, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
- {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"DATETIME_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"DTD_IDENTIFIER", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"EXTERNAL_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"EXTERNAL_LANGUAGE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- SKIP_OPEN_TABLE},
- {"PARAMETER_STYLE", 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"IS_DETERMINISTIC", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"SQL_DATA_ACCESS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- SKIP_OPEN_TABLE},
- {"SQL_PATH", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type",
- SKIP_OPEN_TABLE},
- {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created", SKIP_OPEN_TABLE},
- {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified", SKIP_OPEN_TABLE},
- {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"ROUTINE_COMMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Comment",
- SKIP_OPEN_TABLE},
- {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer", SKIP_OPEN_TABLE},
- {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "character_set_client", SKIP_OPEN_TABLE},
- {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "collation_connection", SKIP_OPEN_TABLE},
- {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "Database Collation", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("SPECIFIC_NAME", Name(), NOT_NULL),
+ Column("ROUTINE_CATALOG", Catalog(), NOT_NULL),
+ Column("ROUTINE_SCHEMA", Name(), NOT_NULL, "Db"),
+ Column("ROUTINE_NAME", Name(), NOT_NULL, "Name"),
+ Column("ROUTINE_TYPE", Varchar(13),NOT_NULL, "Type"),
+ Column("DATA_TYPE", Name(), NOT_NULL),
+ Column("CHARACTER_MAXIMUM_LENGTH",SLong(21), NULLABLE),
+ Column("CHARACTER_OCTET_LENGTH", SLong(21), NULLABLE),
+ Column("NUMERIC_PRECISION", SLong(21), NULLABLE),
+ Column("NUMERIC_SCALE", SLong(21), NULLABLE),
+ Column("DATETIME_PRECISION", ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("CHARACTER_SET_NAME", Varchar(64),NULLABLE),
+ Column("COLLATION_NAME", Varchar(64),NULLABLE),
+ Column("DTD_IDENTIFIER", Longtext(65535), NULLABLE),
+ Column("ROUTINE_BODY", Varchar(8), NOT_NULL),
+ Column("ROUTINE_DEFINITION", Longtext(65535), NULLABLE),
+ Column("EXTERNAL_NAME", Name(), NULLABLE),
+ Column("EXTERNAL_LANGUAGE", Name(), NULLABLE),
+ Column("PARAMETER_STYLE", Varchar(8), NOT_NULL),
+ Column("IS_DETERMINISTIC", Varchar(3), NOT_NULL),
+ Column("SQL_DATA_ACCESS", Name(), NOT_NULL),
+ Column("SQL_PATH", Name(), NULLABLE),
+ Column("SECURITY_TYPE", Varchar(7), NOT_NULL, "Security_type"),
+ Column("CREATED", Datetime(0), NOT_NULL, "Created"),
+ Column("LAST_ALTERED", Datetime(0), NOT_NULL, "Modified"),
+ Column("SQL_MODE", SQLMode(), NOT_NULL),
+ Column("ROUTINE_COMMENT", Longtext(65535), NOT_NULL, "Comment"),
+ Column("DEFINER", Definer(), NOT_NULL, "Definer"),
+ Column("CHARACTER_SET_CLIENT", CSName(), NOT_NULL, "character_set_client"),
+ Column("COLLATION_CONNECTION", CSName(), NOT_NULL, "collation_connection"),
+ Column("DATABASE_COLLATION", CSName(), NOT_NULL, "Database Collation"),
+ CEnd()
};
ST_FIELD_INFO stat_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", OPEN_FRM_ONLY},
- {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique", OPEN_FRM_ONLY},
- {"INDEX_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name",
- OPEN_FRM_ONLY},
- {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index", OPEN_FRM_ONLY},
- {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name",
- OPEN_FRM_ONLY},
- {"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation", OPEN_FRM_ONLY},
- {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 1,
- "Cardinality", OPEN_FULL_TABLE},
- {"SUB_PART", 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part", OPEN_FRM_ONLY},
- {"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed", OPEN_FRM_ONLY},
- {"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null", OPEN_FRM_ONLY},
- {"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type", OPEN_FULL_TABLE},
- {"COMMENT", 16, MYSQL_TYPE_STRING, 0, 1, "Comment", OPEN_FRM_ONLY},
- {"INDEX_COMMENT", INDEX_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0,
- "Index_comment", OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TABLE_NAME", Name(), NOT_NULL, "Table", OPEN_FRM_ONLY),
+ Column("NON_UNIQUE", SLonglong(1),NOT_NULL, "Non_unique", OPEN_FRM_ONLY),
+ Column("INDEX_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("INDEX_NAME", Name(), NOT_NULL, "Key_name", OPEN_FRM_ONLY),
+ Column("SEQ_IN_INDEX", SLonglong(2),NOT_NULL, "Seq_in_index",OPEN_FRM_ONLY),
+ Column("COLUMN_NAME", Name(), NOT_NULL, "Column_name", OPEN_FRM_ONLY),
+ Column("COLLATION", Varchar(1), NULLABLE, "Collation", OPEN_FRM_ONLY),
+ Column("CARDINALITY", SLonglong(), NULLABLE, "Cardinality", OPEN_FULL_TABLE),
+ Column("SUB_PART", SLonglong(3),NULLABLE, "Sub_part", OPEN_FRM_ONLY),
+ Column("PACKED", Varchar(10), NULLABLE, "Packed", OPEN_FRM_ONLY),
+ Column("NULLABLE", Varchar(3), NOT_NULL, "Null", OPEN_FRM_ONLY),
+ Column("INDEX_TYPE", Varchar(16), NOT_NULL, "Index_type", OPEN_FULL_TABLE),
+ Column("COMMENT", Varchar(16), NULLABLE, "Comment", OPEN_FRM_ONLY),
+ Column("INDEX_COMMENT", Varchar(INDEX_COMMENT_MAXLEN),
+ NOT_NULL, "Index_comment",OPEN_FRM_ONLY),
+ CEnd()
};
ST_FIELD_INFO view_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"VIEW_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"CHECK_OPTION", 8, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"IS_UPDATABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FRM_ONLY},
- {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FRM_ONLY},
- {"ALGORITHM", 10, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("VIEW_DEFINITION", Longtext(65535), NOT_NULL, OPEN_FRM_ONLY),
+ Column("CHECK_OPTION", Varchar(8), NOT_NULL, OPEN_FRM_ONLY),
+ Column("IS_UPDATABLE", Yesno(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("DEFINER", Definer(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("SECURITY_TYPE", Varchar(7), NOT_NULL, OPEN_FRM_ONLY),
+ Column("CHARACTER_SET_CLIENT", CSName(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("COLLATION_CONNECTION", CSName(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("ALGORITHM", Varchar(10),NOT_NULL, OPEN_FRM_ONLY),
+ CEnd()
};
ST_FIELD_INFO user_privileges_fields_info[]=
{
- {"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},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("GRANTEE", Userhost(), NOT_NULL),
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL),
+ Column("PRIVILEGE_TYPE", Name(), NOT_NULL),
+ Column("IS_GRANTABLE", Yesno(), NOT_NULL),
+ CEnd()
};
ST_FIELD_INFO schema_privileges_fields_info[]=
{
- {"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},
- {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("GRANTEE", Userhost(), NOT_NULL),
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL),
+ Column("PRIVILEGE_TYPE", Name(), NOT_NULL),
+ Column("IS_GRANTABLE", Yesno(), NOT_NULL),
+ CEnd()
};
ST_FIELD_INFO table_privileges_fields_info[]=
{
- {"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},
- {"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},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("GRANTEE", Userhost(), NOT_NULL),
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL),
+ Column("TABLE_NAME", Name(), NOT_NULL),
+ Column("PRIVILEGE_TYPE", Name(), NOT_NULL),
+ Column("IS_GRANTABLE", Yesno(), NOT_NULL),
+ CEnd()
};
ST_FIELD_INFO column_privileges_fields_info[]=
{
- {"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},
- {"COLUMN_NAME", 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},
- {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("GRANTEE", Userhost(), NOT_NULL),
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL),
+ Column("TABLE_NAME", Name(), NOT_NULL),
+ Column("COLUMN_NAME", Name(), NOT_NULL),
+ Column("PRIVILEGE_TYPE", Name(), NOT_NULL),
+ Column("IS_GRANTABLE", Yesno(), NOT_NULL),
+ CEnd()
};
ST_FIELD_INFO table_constraints_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CONSTRAINT_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("CONSTRAINT_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_TYPE", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ CEnd()
};
ST_FIELD_INFO key_column_usage_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FULL_TABLE},
- {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0,
- OPEN_FULL_TABLE},
- {"REFERENCED_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {"REFERENCED_COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("CONSTRAINT_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("COLUMN_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("ORDINAL_POSITION", SLonglong(10), NOT_NULL, OPEN_FULL_TABLE),
+ Column("POSITION_IN_UNIQUE_CONSTRAINT", SLonglong(10), NULLABLE, OPEN_FULL_TABLE),
+ Column("REFERENCED_TABLE_SCHEMA", Name(), NULLABLE, OPEN_FULL_TABLE),
+ Column("REFERENCED_TABLE_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ Column("REFERENCED_COLUMN_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ CEnd()
};
ST_FIELD_INFO table_names_fields_info[]=
{
- {"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 + MYSQL50_TABLE_NAME_PREFIX_LENGTH,
- MYSQL_TYPE_STRING, 0, 0, "Tables_in_", SKIP_OPEN_TABLE},
- {"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type",
- OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL),
+ Column("TABLE_NAME", Varchar(NAME_CHAR_LEN + MYSQL50_TABLE_NAME_PREFIX_LENGTH),
+ NOT_NULL, "Tables_in_"),
+ Column("TABLE_TYPE", Name(), NOT_NULL, "Table_type", OPEN_FRM_ONLY),
+ CEnd()
};
ST_FIELD_INFO open_tables_fields_info[]=
{
- {"Database", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database",
- SKIP_OPEN_TABLE},
- {"Table",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table", SKIP_OPEN_TABLE},
- {"In_use", 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use", SKIP_OPEN_TABLE},
- {"Name_locked", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("Database", Name(), NOT_NULL, "Database"),
+ Column("Table", Name(), NOT_NULL, "Table"),
+ Column("In_use", SLonglong(1), NOT_NULL, "In_use"),
+ Column("Name_locked", SLonglong(4), NOT_NULL, "Name_locked"),
+ CEnd()
};
ST_FIELD_INFO triggers_fields_info[]=
{
- {"TRIGGER_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TRIGGER_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"TRIGGER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger",
- OPEN_FRM_ONLY},
- {"EVENT_MANIPULATION", 6, MYSQL_TYPE_STRING, 0, 0, "Event", OPEN_FRM_ONLY},
- {"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FRM_ONLY},
- {"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FRM_ONLY},
- {"EVENT_OBJECT_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table",
- OPEN_FRM_ONLY},
- {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FRM_ONLY},
- {"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY},
- {"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement",
- OPEN_FRM_ONLY},
- {"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"ACTION_TIMING", 6, MYSQL_TYPE_STRING, 0, 0, "Timing", OPEN_FRM_ONLY},
- {"ACTION_REFERENCE_OLD_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FRM_ONLY},
- {"ACTION_REFERENCE_NEW_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FRM_ONLY},
- {"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
+ Column("TRIGGER_CATALOG", Catalog(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TRIGGER_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("TRIGGER_NAME", Name(), NOT_NULL, "Trigger", OPEN_FRM_ONLY),
+ Column("EVENT_MANIPULATION", Varchar(6), NOT_NULL, "Event", OPEN_FRM_ONLY),
+ Column("EVENT_OBJECT_CATALOG", Catalog(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("EVENT_OBJECT_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY),
+ Column("EVENT_OBJECT_TABLE", Name(), NOT_NULL, "Table", OPEN_FRM_ONLY),
+ Column("ACTION_ORDER", SLonglong(4), NOT_NULL, OPEN_FRM_ONLY),
+ Column("ACTION_CONDITION", Longtext(65535), NULLABLE, OPEN_FRM_ONLY),
+ Column("ACTION_STATEMENT", Longtext(65535), NOT_NULL, "Statement",OPEN_FRM_ONLY),
+ Column("ACTION_ORIENTATION", Varchar(9), NOT_NULL, OPEN_FRM_ONLY),
+ Column("ACTION_TIMING", Varchar(6), NOT_NULL, "Timing", OPEN_FRM_ONLY),
+ Column("ACTION_REFERENCE_OLD_TABLE",Name(), NULLABLE, OPEN_FRM_ONLY),
+ Column("ACTION_REFERENCE_NEW_TABLE",Name(), NULLABLE, OPEN_FRM_ONLY),
+ Column("ACTION_REFERENCE_OLD_ROW",Varchar(3),NOT_NULL, OPEN_FRM_ONLY),
+ Column("ACTION_REFERENCE_NEW_ROW",Varchar(3),NOT_NULL, OPEN_FRM_ONLY),
/* 2 here indicates 2 decimals */
- {"CREATED", 2, MYSQL_TYPE_DATETIME, 0, 1, "Created", OPEN_FRM_ONLY},
- {"SQL_MODE", 32*256, MYSQL_TYPE_STRING, 0, 0, "sql_mode", OPEN_FRM_ONLY},
- {"DEFINER", DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer", OPEN_FRM_ONLY},
- {"CHARACTER_SET_CLIENT", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "character_set_client", OPEN_FRM_ONLY},
- {"COLLATION_CONNECTION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "collation_connection", OPEN_FRM_ONLY},
- {"DATABASE_COLLATION", MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0,
- "Database Collation", OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("CREATED", Datetime(2), NULLABLE, "Created", OPEN_FRM_ONLY),
+ Column("SQL_MODE", SQLMode(), NOT_NULL, "sql_mode", OPEN_FRM_ONLY),
+ Column("DEFINER", Definer(), NOT_NULL, "Definer", OPEN_FRM_ONLY),
+ Column("CHARACTER_SET_CLIENT", CSName(), NOT_NULL, "character_set_client",
+ OPEN_FRM_ONLY),
+ Column("COLLATION_CONNECTION", CSName(), NOT_NULL, "collation_connection",
+ OPEN_FRM_ONLY),
+ Column("DATABASE_COLLATION", CSName(), NOT_NULL, "Database Collation",
+ OPEN_FRM_ONLY),
+ CEnd()
};
ST_FIELD_INFO partitions_fields_info[]=
{
- {"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLE_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"SUBPARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
- {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
- {"PARTITION_METHOD", 18, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
- OPEN_FULL_TABLE},
- {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
- OPEN_FULL_TABLE},
- {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
- OPEN_FULL_TABLE},
- {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
- {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
- OPEN_FULL_TABLE},
- {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0,
- OPEN_FULL_TABLE},
- {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE},
- {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE},
- {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE},
- {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE},
- {"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- OPEN_FULL_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLE_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("PARTITION_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ Column("SUBPARTITION_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ Column("PARTITION_ORDINAL_POSITION", ULonglong(), NULLABLE, OPEN_FULL_TABLE),
+ Column("SUBPARTITION_ORDINAL_POSITION",ULonglong(),NULLABLE, OPEN_FULL_TABLE),
+ Column("PARTITION_METHOD", Varchar(18), NULLABLE, OPEN_FULL_TABLE),
+ Column("SUBPARTITION_METHOD", Varchar(12), NULLABLE, OPEN_FULL_TABLE),
+ Column("PARTITION_EXPRESSION", Longtext(65535), NULLABLE, OPEN_FULL_TABLE),
+ Column("SUBPARTITION_EXPRESSION", Longtext(65535), NULLABLE, OPEN_FULL_TABLE),
+ Column("PARTITION_DESCRIPTION", Longtext(65535), NULLABLE, OPEN_FULL_TABLE),
+ Column("TABLE_ROWS", ULonglong(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("AVG_ROW_LENGTH", ULonglong(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("DATA_LENGTH", ULonglong(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("MAX_DATA_LENGTH", ULonglong(), NULLABLE, OPEN_FULL_TABLE),
+ Column("INDEX_LENGTH", ULonglong(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("DATA_FREE", ULonglong(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CREATE_TIME", Datetime(0), NULLABLE, OPEN_FULL_TABLE),
+ Column("UPDATE_TIME", Datetime(0), NULLABLE, OPEN_FULL_TABLE),
+ Column("CHECK_TIME", Datetime(0), NULLABLE, OPEN_FULL_TABLE),
+ Column("CHECKSUM", ULonglong(), NULLABLE, OPEN_FULL_TABLE),
+ Column("PARTITION_COMMENT", Varchar(80), NOT_NULL, OPEN_FULL_TABLE),
+ Column("NODEGROUP", Varchar(12), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLESPACE_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ CEnd()
};
ST_FIELD_INFO variables_fields_info[]=
{
- {"VARIABLE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Variable_name",
- SKIP_OPEN_TABLE},
- {"VARIABLE_VALUE", 2048, MYSQL_TYPE_STRING, 0, 0, "Value", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("VARIABLE_NAME", Varchar(64), NOT_NULL, "Variable_name"),
+ Column("VARIABLE_VALUE", Varchar(2048), NOT_NULL, "Value"),
+ CEnd()
};
ST_FIELD_INFO sysvars_fields_info[]=
{
- {"VARIABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0},
- {"SESSION_VALUE", 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"GLOBAL_VALUE", 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"GLOBAL_VALUE_ORIGIN", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0},
- {"DEFAULT_VALUE", 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"VARIABLE_SCOPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0},
- {"VARIABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0},
- {"VARIABLE_COMMENT", TABLE_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0, 0, 0},
- {"NUMERIC_MIN_VALUE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"NUMERIC_MAX_VALUE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"NUMERIC_BLOCK_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"ENUM_VALUE_LIST", 65535, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {"READ_ONLY", 3, MYSQL_TYPE_STRING, 0, 0, 0, 0},
- {"COMMAND_LINE_ARGUMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+ Column("VARIABLE_NAME", Name(), NOT_NULL),
+ Column("SESSION_VALUE", Varchar(2048), NULLABLE),
+ Column("GLOBAL_VALUE", Varchar(2048), NULLABLE),
+ Column("GLOBAL_VALUE_ORIGIN", Name(), NOT_NULL),
+ Column("DEFAULT_VALUE", Varchar(2048), NULLABLE),
+ Column("VARIABLE_SCOPE", Name(), NOT_NULL),
+ Column("VARIABLE_TYPE", Name(), NOT_NULL),
+ Column("VARIABLE_COMMENT", Varchar(TABLE_COMMENT_MAXLEN), NOT_NULL),
+ Column("NUMERIC_MIN_VALUE", Varchar(MY_INT64_NUM_DECIMAL_DIGITS), NULLABLE),
+ Column("NUMERIC_MAX_VALUE", Varchar(MY_INT64_NUM_DECIMAL_DIGITS), NULLABLE),
+ Column("NUMERIC_BLOCK_SIZE", Varchar(MY_INT64_NUM_DECIMAL_DIGITS), NULLABLE),
+ Column("ENUM_VALUE_LIST", Longtext(65535), NULLABLE),
+ Column("READ_ONLY", Yesno(), NOT_NULL),
+ Column("COMMAND_LINE_ARGUMENT",Name(), NULLABLE),
+ Column("GLOBAL_VALUE_PATH", Varchar(2048), NULLABLE),
+ CEnd()
};
ST_FIELD_INFO processlist_fields_info[]=
{
- {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id", SKIP_OPEN_TABLE},
- {"USER", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User",
- SKIP_OPEN_TABLE},
- {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host",
- SKIP_OPEN_TABLE},
- {"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db", SKIP_OPEN_TABLE},
- {"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command", SKIP_OPEN_TABLE},
- {"TIME", 7, MYSQL_TYPE_LONG, 0, 0, "Time", SKIP_OPEN_TABLE},
- {"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State", SKIP_OPEN_TABLE},
- {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info",
- SKIP_OPEN_TABLE},
- {"TIME_MS", 100 * (MY_INT64_NUM_DECIMAL_DIGITS + 1) + 3, MYSQL_TYPE_DECIMAL,
- 0, 0, "Time_ms", SKIP_OPEN_TABLE},
- {"STAGE", 2, MYSQL_TYPE_TINY, 0, 0, "Stage", SKIP_OPEN_TABLE},
- {"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_LONGLONG, 0, 0, "Memory_used", SKIP_OPEN_TABLE},
- {"MAX_MEMORY_USED", 7, MYSQL_TYPE_LONGLONG, 0, 0, "Max_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},
- {"INFO_BINARY", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_BLOB, 0, 1,
- "Info_binary", SKIP_OPEN_TABLE},
- {"TID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Tid", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("ID", SLonglong(4), NOT_NULL, "Id"),
+ Column("USER", Varchar(USERNAME_CHAR_LENGTH), NOT_NULL, "User"),
+ Column("HOST", Varchar(LIST_PROCESS_HOST_LEN),NOT_NULL, "Host"),
+ Column("DB", Name(), NULLABLE, "Db"),
+ Column("COMMAND", Varchar(16), NOT_NULL, "Command"),
+ Column("TIME", SLong(7), NOT_NULL, "Time"),
+ Column("STATE", Varchar(64), NULLABLE, "State"),
+ Column("INFO", Longtext(PROCESS_LIST_INFO_WIDTH),
+ NULLABLE, "Info"),
+ Column("TIME_MS", Decimal(100 * (MY_INT64_NUM_DECIMAL_DIGITS + 1) + 3),
+ NOT_NULL, "Time_ms"),
+ Column("STAGE", STiny(2), NOT_NULL, "Stage"),
+ Column("MAX_STAGE", STiny(2), NOT_NULL, "Max_stage"),
+ Column("PROGRESS", Decimal(703), NOT_NULL, "Progress"),
+ Column("MEMORY_USED", SLonglong(7), NOT_NULL, "Memory_used"),
+ Column("MAX_MEMORY_USED",SLonglong(7), NOT_NULL, "Max_memory_used"),
+ Column("EXAMINED_ROWS", SLong(7), NOT_NULL, "Examined_rows"),
+ Column("QUERY_ID", SLonglong(4), NOT_NULL),
+ Column("INFO_BINARY",Blob(PROCESS_LIST_INFO_WIDTH),NULLABLE, "Info_binary"),
+ Column("TID", SLonglong(4), NOT_NULL, "Tid"),
+ CEnd()
};
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", 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",
- SKIP_OPEN_TABLE},
- {"PLUGIN_LIBRARY_VERSION", 20, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 0, "License", SKIP_OPEN_TABLE},
- {"LOAD_OPTION", 64, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_MATURITY", 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_AUTH_VERSION", 80, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("PLUGIN_NAME", Name(), NOT_NULL, "Name"),
+ Column("PLUGIN_VERSION", Varchar(20), NOT_NULL),
+ Column("PLUGIN_STATUS", Varchar(16), NOT_NULL, "Status"),
+ Column("PLUGIN_TYPE", Varchar(80), NOT_NULL, "Type"),
+ Column("PLUGIN_TYPE_VERSION", Varchar(20), NOT_NULL),
+ Column("PLUGIN_LIBRARY", Name(), NULLABLE, "Library"),
+ Column("PLUGIN_LIBRARY_VERSION", Varchar(20), NULLABLE),
+ Column("PLUGIN_AUTHOR", Name(), NULLABLE),
+ Column("PLUGIN_DESCRIPTION", Longtext(65535), NULLABLE),
+ Column("PLUGIN_LICENSE", Varchar(80), NOT_NULL, "License"),
+ Column("LOAD_OPTION", Varchar(64), NOT_NULL),
+ Column("PLUGIN_MATURITY", Varchar(12), NOT_NULL),
+ Column("PLUGIN_AUTH_VERSION", Varchar(80), NULLABLE),
+ CEnd()
};
ST_FIELD_INFO files_fields_info[]=
{
- {"FILE_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE},
- {"FILE_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- SKIP_OPEN_TABLE},
- {"TABLE_CATALOG", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0,
- SKIP_OPEN_TABLE},
- {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"FULLTEXT_KEYS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {"DELETED_ROWS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"UPDATE_COUNT", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"FREE_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"EXTENT_SIZE", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE},
- {"INITIAL_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
- {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
- {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
- {"CREATION_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
- {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
- {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE},
- {"RECOVER_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE},
- {"VERSION", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version", SKIP_OPEN_TABLE},
- {"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format", SKIP_OPEN_TABLE},
- {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows", SKIP_OPEN_TABLE},
- {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length", SKIP_OPEN_TABLE},
- {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length", SKIP_OPEN_TABLE},
- {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length", SKIP_OPEN_TABLE},
- {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length", SKIP_OPEN_TABLE},
- {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free", SKIP_OPEN_TABLE},
- {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time", SKIP_OPEN_TABLE},
- {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time", SKIP_OPEN_TABLE},
- {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time", SKIP_OPEN_TABLE},
- {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum", SKIP_OPEN_TABLE},
- {"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"EXTRA", 255, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("FILE_ID", SLonglong(4), NOT_NULL),
+ Column("FILE_NAME", Varchar(FN_REFLEN),NULLABLE),
+ Column("FILE_TYPE", Varchar(20), NOT_NULL),
+ Column("TABLESPACE_NAME", Name(), NULLABLE),
+ Column("TABLE_CATALOG", Name(), NOT_NULL),
+ Column("TABLE_SCHEMA", Name(), NULLABLE),
+ Column("TABLE_NAME", Name(), NULLABLE),
+ Column("LOGFILE_GROUP_NAME", Name(), NULLABLE),
+ Column("LOGFILE_GROUP_NUMBER",SLonglong(4), NULLABLE),
+ Column("ENGINE", Name(), NOT_NULL),
+ Column("FULLTEXT_KEYS", Name(), NULLABLE),
+ Column("DELETED_ROWS", SLonglong(4), NULLABLE),
+ Column("UPDATE_COUNT", SLonglong(4), NULLABLE),
+ Column("FREE_EXTENTS", SLonglong(4), NULLABLE),
+ Column("TOTAL_EXTENTS", SLonglong(4), NULLABLE),
+ Column("EXTENT_SIZE", SLonglong(4), NOT_NULL),
+ Column("INITIAL_SIZE", ULonglong(), NULLABLE),
+ Column("MAXIMUM_SIZE", ULonglong(), NULLABLE),
+ Column("AUTOEXTEND_SIZE", ULonglong(), NULLABLE),
+ Column("CREATION_TIME", Datetime(0), NULLABLE),
+ Column("LAST_UPDATE_TIME", Datetime(0), NULLABLE),
+ Column("LAST_ACCESS_TIME", Datetime(0), NULLABLE),
+ Column("RECOVER_TIME", SLonglong(4), NULLABLE),
+ Column("TRANSACTION_COUNTER", SLonglong(4), NULLABLE),
+ Column("VERSION", ULonglong(), NULLABLE, "Version"),
+ Column("ROW_FORMAT", Varchar(10), NULLABLE, "Row_format"),
+ Column("TABLE_ROWS", ULonglong(), NULLABLE, "Rows"),
+ Column("AVG_ROW_LENGTH", ULonglong(), NULLABLE, "Avg_row_length"),
+ Column("DATA_LENGTH", ULonglong(), NULLABLE, "Data_length"),
+ Column("MAX_DATA_LENGTH", ULonglong(), NULLABLE, "Max_data_length"),
+ Column("INDEX_LENGTH", ULonglong(), NULLABLE, "Index_length"),
+ Column("DATA_FREE", ULonglong(), NULLABLE, "Data_free"),
+ Column("CREATE_TIME", Datetime(0), NULLABLE, "Create_time"),
+ Column("UPDATE_TIME", Datetime(0), NULLABLE, "Update_time"),
+ Column("CHECK_TIME", Datetime(0), NULLABLE, "Check_time"),
+ Column("CHECKSUM", ULonglong(), NULLABLE, "Checksum"),
+ Column("STATUS", Varchar(20), NOT_NULL),
+ Column("EXTRA", Varchar(255), NULLABLE),
+ CEnd()
};
+}; // namespace Show
+
+
void init_fill_schema_files_row(TABLE* table)
{
int i;
- for(i=0; files_fields_info[i].field_name!=NULL; i++)
+ for(i=0; !Show::files_fields_info[i].end_marker(); i++)
table->field[i]->set_null();
table->field[IS_FILES_STATUS]->set_notnull();
table->field[IS_FILES_STATUS]->store("NORMAL", 6, system_charset_info);
}
+
+namespace Show {
+
ST_FIELD_INFO referential_constraints_fields_info[]=
{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"UNIQUE_CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"UNIQUE_CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"UNIQUE_CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0,
- MY_I_S_MAYBE_NULL, 0, OPEN_FULL_TABLE},
- {"MATCH_OPTION", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"UPDATE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"DELETE_RULE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("CONSTRAINT_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("UNIQUE_CONSTRAINT_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("UNIQUE_CONSTRAINT_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("UNIQUE_CONSTRAINT_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ Column("MATCH_OPTION", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("UPDATE_RULE", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("DELETE_RULE", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("REFERENCED_TABLE_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ CEnd()
};
ST_FIELD_INFO parameters_fields_info[]=
{
- {"SPECIFIC_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"SPECIFIC_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"SPECIFIC_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 0, 0, OPEN_FULL_TABLE},
- {"PARAMETER_MODE", 5, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"PARAMETER_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
- {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
- {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
- {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE},
- {"DATETIME_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
- 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY},
- {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE},
- {"DTD_IDENTIFIER", 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"ROUTINE_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}
+ Column("SPECIFIC_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("SPECIFIC_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("SPECIFIC_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("ORDINAL_POSITION", SLong(21), NOT_NULL, OPEN_FULL_TABLE),
+ Column("PARAMETER_MODE", Varchar(5), NULLABLE, OPEN_FULL_TABLE),
+ Column("PARAMETER_NAME", Name(), NULLABLE, OPEN_FULL_TABLE),
+ Column("DATA_TYPE", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CHARACTER_MAXIMUM_LENGTH",SLong(21), NULLABLE, OPEN_FULL_TABLE),
+ Column("CHARACTER_OCTET_LENGTH", SLong(21), NULLABLE, OPEN_FULL_TABLE),
+ Column("NUMERIC_PRECISION", SLong(21), NULLABLE, OPEN_FULL_TABLE),
+ Column("NUMERIC_SCALE", SLong(21), NULLABLE, OPEN_FULL_TABLE),
+ Column("DATETIME_PRECISION", ULonglong(), NULLABLE, OPEN_FRM_ONLY),
+ Column("CHARACTER_SET_NAME", Varchar(64), NULLABLE, OPEN_FULL_TABLE),
+ Column("COLLATION_NAME", Varchar(64), NULLABLE, OPEN_FULL_TABLE),
+ Column("DTD_IDENTIFIER", Longtext(65535), NOT_NULL, OPEN_FULL_TABLE),
+ Column("ROUTINE_TYPE", Varchar(9), NOT_NULL, OPEN_FULL_TABLE),
+ CEnd()
};
ST_FIELD_INFO tablespaces_fields_info[]=
{
- {"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- SKIP_OPEN_TABLE},
- {"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"TABLESPACE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL,
- 0, SKIP_OPEN_TABLE},
- {"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL,
- 0, SKIP_OPEN_TABLE},
- {"EXTENT_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
- MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
- {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
- MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
- {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
- MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
- {"NODEGROUP_ID", 21, MYSQL_TYPE_LONGLONG, 0,
- MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE},
- {"TABLESPACE_COMMENT", 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0,
- SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("TABLESPACE_NAME", Name(), NOT_NULL),
+ Column("ENGINE", Name(), NOT_NULL),
+ Column("TABLESPACE_TYPE", Name(), NULLABLE),
+ Column("LOGFILE_GROUP_NAME", Name(), NULLABLE),
+ Column("EXTENT_SIZE", ULonglong(), NULLABLE),
+ Column("AUTOEXTEND_SIZE", ULonglong(), NULLABLE),
+ Column("MAXIMUM_SIZE", ULonglong(), NULLABLE),
+ Column("NODEGROUP_ID", ULonglong(), NULLABLE),
+ Column("TABLESPACE_COMMENT", Varchar(2048), NULLABLE),
+ CEnd()
};
ST_FIELD_INFO keycache_fields_info[]=
{
- {"KEY_CACHE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"SEGMENTS", 3, MYSQL_TYPE_LONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED) , 0, SKIP_OPEN_TABLE},
- {"SEGMENT_NUMBER", 3, MYSQL_TYPE_LONG, 0,
- (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
- {"FULL_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE},
- {"BLOCK_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE },
- {"USED_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_blocks_used", SKIP_OPEN_TABLE},
- {"UNUSED_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_blocks_unused", SKIP_OPEN_TABLE},
- {"DIRTY_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_blocks_not_flushed", SKIP_OPEN_TABLE},
- {"READ_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_read_requests", SKIP_OPEN_TABLE},
- {"READS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_reads", SKIP_OPEN_TABLE},
- {"WRITE_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_write_requests", SKIP_OPEN_TABLE},
- {"WRITES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
- (MY_I_S_UNSIGNED), "Key_writes", SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+ Column("KEY_CACHE_NAME",Varchar(NAME_LEN),NOT_NULL),
+ Column("SEGMENTS", ULong(3), NULLABLE),
+ Column("SEGMENT_NUMBER", ULong(3), NULLABLE),
+ Column("FULL_SIZE", ULonglong(), NOT_NULL),
+ Column("BLOCK_SIZE", ULonglong(), NOT_NULL),
+ Column("USED_BLOCKS", ULonglong(), NOT_NULL, "Key_blocks_used"),
+ Column("UNUSED_BLOCKS", ULonglong(), NOT_NULL, "Key_blocks_unused"),
+ Column("DIRTY_BLOCKS", ULonglong(), NOT_NULL, "Key_blocks_not_flushed"),
+ Column("READ_REQUESTS", ULonglong(), NOT_NULL, "Key_read_requests"),
+ Column("READS", ULonglong(), NOT_NULL, "Key_reads"),
+ Column("WRITE_REQUESTS", ULonglong(), NOT_NULL, "Key_write_requests"),
+ Column("WRITES", ULonglong(), NOT_NULL, "Key_writes"),
+ CEnd()
};
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}
+ Column("id", SLonglong(3), NULLABLE, "id"),
+ Column("select_type", Varchar(19), NOT_NULL, "select_type"),
+ Column("table", Name(), NULLABLE, "table"),
+ Column("type", Varchar(15), NULLABLE, "type"),
+ Column("possible_keys",Varchar(NAME_CHAR_LEN*MAX_KEY), NULLABLE, "possible_keys"),
+ Column("key", Varchar(NAME_CHAR_LEN*MAX_KEY), NULLABLE, "key"),
+ Column("key_len", Varchar(NAME_CHAR_LEN*MAX_KEY), NULLABLE, "key_len"),
+ Column("ref", Varchar(NAME_CHAR_LEN*MAX_REF_PARTS),NULLABLE, "ref"),
+ Column("rows", SLonglong(10), NULLABLE, "rows"),
+ Column("Extra", Varchar(255), NOT_NULL, "Extra"),
+ CEnd()
};
-#ifdef HAVE_SPATIAL
-ST_FIELD_INFO geometry_columns_fields_info[]=
-{
- {"F_TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"F_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"F_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"F_GEOMETRY_COLUMN", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field",
- OPEN_FRM_ONLY},
- {"G_TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"G_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"G_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY},
- {"G_GEOMETRY_COLUMN", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field",
- OPEN_FRM_ONLY},
- {"STORAGE_TYPE", 2, MYSQL_TYPE_TINY, 0, 0, 0, OPEN_FRM_ONLY},
- {"GEOMETRY_TYPE", 7, MYSQL_TYPE_LONG, 0, 0, 0, OPEN_FRM_ONLY},
- {"COORD_DIMENSION", 2, MYSQL_TYPE_TINY, 0, 0, 0, OPEN_FRM_ONLY},
- {"MAX_PPR", 2, MYSQL_TYPE_TINY, 0, 0, 0, OPEN_FRM_ONLY},
- {"SRID", 5, MYSQL_TYPE_SHORT, 0, 0, 0, OPEN_FRM_ONLY},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
-};
-
-
-ST_FIELD_INFO spatial_ref_sys_fields_info[]=
+ST_FIELD_INFO check_constraints_fields_info[]=
{
- {"SRID", 5, MYSQL_TYPE_SHORT, 0, 0, 0, SKIP_OPEN_TABLE},
- {"AUTH_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"AUTH_SRID", 5, MYSQL_TYPE_LONG, 0, 0, 0, SKIP_OPEN_TABLE},
- {"SRTEXT", 2048, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0}
+ Column("CONSTRAINT_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_SCHEMA", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("TABLE_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CONSTRAINT_NAME", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ Column("CHECK_CLAUSE", Name(), NOT_NULL, OPEN_FULL_TABLE),
+ CEnd()
};
-#endif /*HAVE_SPATIAL*/
+}; // namespace Show
-ST_FIELD_INFO check_constraints_fields_info[]=
-{
- {"CONSTRAINT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CONSTRAINT_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE},
- {"CONSTRAINT_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {"CHECK_CLAUSE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0,
- OPEN_FULL_TABLE},
- {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
-};
+
+namespace Show {
/** For creating fields of information_schema.OPTIMIZER_TRACE */
extern ST_FIELD_INFO optimizer_trace_info[];
+} //namespace Show
+
/*
Description of ST_FIELD_INFO in table.h
@@ -9891,108 +9507,102 @@ extern ST_FIELD_INFO optimizer_trace_info[];
ST_SCHEMA_TABLE schema_tables[]=
{
- {"ALL_PLUGINS", plugin_fields_info, 0,
+ {"ALL_PLUGINS", Show::plugin_fields_info, 0,
fill_all_plugins, make_old_format, 0, 5, -1, 0, 0},
- {"APPLICABLE_ROLES", applicable_roles_fields_info, 0,
+ {"APPLICABLE_ROLES", Show::applicable_roles_fields_info, 0,
fill_schema_applicable_roles, 0, 0, -1, -1, 0, 0},
- {"CHARACTER_SETS", charsets_fields_info, 0,
+ {"CHARACTER_SETS", Show::charsets_fields_info, 0,
fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
- {"CHECK_CONSTRAINTS", check_constraints_fields_info, 0, get_all_tables, 0,
+ {"CHECK_CONSTRAINTS", Show::check_constraints_fields_info, 0,
+ get_all_tables, 0,
get_check_constraints_record, 1, 2, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
- {"COLLATIONS", collation_fields_info, 0,
+ {"COLLATIONS", Show::collation_fields_info, 0,
fill_schema_collation, make_old_format, 0, -1, -1, 0, 0},
- {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info,
+ {"COLLATION_CHARACTER_SET_APPLICABILITY", Show::coll_charset_app_fields_info,
0, fill_schema_coll_charset_app, 0, 0, -1, -1, 0, 0},
- {"COLUMNS", columns_fields_info, 0,
+ {"COLUMNS", Show::columns_fields_info, 0,
get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0,
OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
- {"COLUMN_PRIVILEGES", column_privileges_fields_info, 0,
+ {"COLUMN_PRIVILEGES", Show::column_privileges_fields_info, 0,
fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
- {"ENABLED_ROLES", enabled_roles_fields_info, 0,
+ {"ENABLED_ROLES", Show::enabled_roles_fields_info, 0,
fill_schema_enabled_roles, 0, 0, -1, -1, 0, 0},
- {"ENGINES", engines_fields_info, 0,
+ {"ENGINES", Show::engines_fields_info, 0,
fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
#ifdef HAVE_EVENT_SCHEDULER
- {"EVENTS", events_fields_info, 0,
+ {"EVENTS", Show::events_fields_info, 0,
Events::fill_schema_events, make_old_format, 0, -1, -1, 0, 0},
#else
- {"EVENTS", events_fields_info, 0,
+ {"EVENTS", Show::events_fields_info, 0,
0, make_old_format, 0, -1, -1, 0, 0},
#endif
- {"EXPLAIN", show_explain_fields_info, 0, fill_show_explain,
+ {"EXPLAIN", Show::show_explain_fields_info, 0, fill_show_explain,
make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0},
- {"FILES", files_fields_info, 0,
+ {"FILES", Show::files_fields_info, 0,
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
- {"GLOBAL_STATUS", variables_fields_info, 0,
+ {"GLOBAL_STATUS", Show::variables_fields_info, 0,
fill_status, make_old_format, 0, 0, -1, 0, 0},
- {"GLOBAL_VARIABLES", variables_fields_info, 0,
+ {"GLOBAL_VARIABLES", Show::variables_fields_info, 0,
fill_variables, make_old_format, 0, 0, -1, 0, 0},
- {"KEY_CACHES", keycache_fields_info, 0,
+ {"KEY_CACHES", Show::keycache_fields_info, 0,
fill_key_cache_tables, 0, 0, -1,-1, 0, 0},
- {"KEY_COLUMN_USAGE", key_column_usage_fields_info, 0,
+ {"KEY_COLUMN_USAGE", Show::key_column_usage_fields_info, 0,
get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0,
OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
- {"OPEN_TABLES", open_tables_fields_info, 0,
+ {"OPEN_TABLES", Show::open_tables_fields_info, 0,
fill_open_tables, make_old_format, 0, -1, -1, 1, 0},
- {"OPTIMIZER_TRACE", optimizer_trace_info, 0,
+ {"OPTIMIZER_TRACE", Show::optimizer_trace_info, 0,
fill_optimizer_trace_info, NULL, NULL, -1, -1, false, 0},
- {"PARAMETERS", parameters_fields_info, 0,
+ {"PARAMETERS", Show::parameters_fields_info, 0,
fill_schema_proc, 0, 0, -1, -1, 0, 0},
- {"PARTITIONS", partitions_fields_info, 0,
+ {"PARTITIONS", Show::partitions_fields_info, 0,
get_all_tables, 0, get_schema_partitions_record, 1, 2, 0,
OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
- {"PLUGINS", plugin_fields_info, 0,
+ {"PLUGINS", Show::plugin_fields_info, 0,
fill_plugins, make_old_format, 0, -1, -1, 0, 0},
- {"PROCESSLIST", processlist_fields_info, 0,
+ {"PROCESSLIST", Show::processlist_fields_info, 0,
fill_schema_processlist, make_old_format, 0, -1, -1, 0, 0},
- {"PROFILING", query_profile_statistics_info, 0,
+ {"PROFILING", Show::query_profile_statistics_info, 0,
fill_query_profile_statistics_info, make_profile_table_for_show,
NULL, -1, -1, false, 0},
- {"REFERENTIAL_CONSTRAINTS", referential_constraints_fields_info,
+ {"REFERENTIAL_CONSTRAINTS", Show::referential_constraints_fields_info,
0, get_all_tables, 0, get_referential_constraints_record,
1, 9, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
- {"ROUTINES", proc_fields_info, 0,
+ {"ROUTINES", Show::proc_fields_info, 0,
fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0},
- {"SCHEMATA", schema_fields_info, 0,
+ {"SCHEMATA", Show::schema_fields_info, 0,
fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0},
- {"SCHEMA_PRIVILEGES", schema_privileges_fields_info, 0,
+ {"SCHEMA_PRIVILEGES", Show::schema_privileges_fields_info, 0,
fill_schema_schema_privileges, 0, 0, -1, -1, 0, 0},
- {"SESSION_STATUS", variables_fields_info, 0,
+ {"SESSION_STATUS", Show::variables_fields_info, 0,
fill_status, make_old_format, 0, 0, -1, 0, 0},
- {"SESSION_VARIABLES", variables_fields_info, 0,
+ {"SESSION_VARIABLES", Show::variables_fields_info, 0,
fill_variables, make_old_format, 0, 0, -1, 0, 0},
- {"STATISTICS", stat_fields_info, 0,
+ {"STATISTICS", Show::stat_fields_info, 0,
get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0,
OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE},
- {"SYSTEM_VARIABLES", sysvars_fields_info, 0,
+ {"SYSTEM_VARIABLES", Show::sysvars_fields_info, 0,
fill_sysvars, make_old_format, 0, 0, -1, 0, 0},
- {"TABLES", tables_fields_info, 0,
+ {"TABLES", Show::tables_fields_info, 0,
get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0,
OPTIMIZE_I_S_TABLE},
- {"TABLESPACES", tablespaces_fields_info, 0,
+ {"TABLESPACES", Show::tablespaces_fields_info, 0,
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
- {"TABLE_CONSTRAINTS", table_constraints_fields_info, 0,
+ {"TABLE_CONSTRAINTS", Show::table_constraints_fields_info, 0,
get_all_tables, 0, get_schema_constraints_record, 3, 4, 0,
OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
- {"TABLE_NAMES", table_names_fields_info, 0,
+ {"TABLE_NAMES", Show::table_names_fields_info, 0,
get_all_tables, make_table_names_old_format, 0, 1, 2, 1, OPTIMIZE_I_S_TABLE},
- {"TABLE_PRIVILEGES", table_privileges_fields_info, 0,
+ {"TABLE_PRIVILEGES", Show::table_privileges_fields_info, 0,
fill_schema_table_privileges, 0, 0, -1, -1, 0, 0},
- {"TRIGGERS", triggers_fields_info, 0,
+ {"TRIGGERS", Show::triggers_fields_info, 0,
get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0,
OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE},
- {"USER_PRIVILEGES", user_privileges_fields_info, 0,
+ {"USER_PRIVILEGES", Show::user_privileges_fields_info, 0,
fill_schema_user_privileges, 0, 0, -1, -1, 0, 0},
- {"VIEWS", view_fields_info, 0,
+ {"VIEWS", Show::view_fields_info, 0,
get_all_tables, 0, get_schema_views_record, 1, 2, 0,
OPEN_VIEW_ONLY|OPTIMIZE_I_S_TABLE},
-#ifdef HAVE_SPATIAL
- {"GEOMETRY_COLUMNS", geometry_columns_fields_info, 0,
- get_all_tables, make_columns_old_format, get_geometry_column_record,
- 1, 2, 0, OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
- {"SPATIAL_REF_SYS", spatial_ref_sys_fields_info, 0,
- fill_spatial_ref_sys, make_old_format, 0, -1, -1, 0, 0},
-#endif /*HAVE_SPATIAL*/
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
@@ -10002,7 +9612,8 @@ 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),
+ if (!(schema_table= (ST_SCHEMA_TABLE *)my_malloc(key_memory_ST_SCHEMA_TABLE,
+ sizeof(ST_SCHEMA_TABLE),
MYF(MY_WME | MY_ZEROFILL))))
DBUG_RETURN(1);
/* Historical Requirement */
@@ -10025,8 +9636,8 @@ int initialize_schema_table(st_plugin_int *plugin)
}
if (!schema_table->old_format)
- for (ST_FIELD_INFO *f= schema_table->fields_info; f->field_name; f++)
- if (f->old_name && f->old_name[0])
+ for (ST_FIELD_INFO *f= schema_table->fields_info; !f->end_marker(); f++)
+ if (f->old_name().str && f->old_name().str[0])
{
schema_table->old_format= make_old_format;
break;
@@ -10146,7 +9757,7 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger)
static const Datetime zero_datetime(Datetime::zero());
Item_datetime_literal *tmp= (new (mem_root)
Item_datetime_literal(thd, &zero_datetime, 2));
- tmp->set_name(thd, STRING_WITH_LEN("Created"), system_charset_info);
+ tmp->set_name(thd, Lex_cstring(STRING_WITH_LEN("Created")));
fields.push_back(tmp, mem_root);
if (p->send_result_set_metadata(&fields,
@@ -10359,15 +9970,15 @@ public:
~IS_internal_schema_access()
{}
- ACL_internal_access_result check(ulong want_access,
- ulong *save_priv) const;
+ ACL_internal_access_result check(privilege_t want_access,
+ privilege_t *save_priv) const;
const ACL_internal_table_access *lookup(const char *name) const;
};
ACL_internal_access_result
-IS_internal_schema_access::check(ulong want_access,
- ulong *save_priv) const
+IS_internal_schema_access::check(privilege_t want_access,
+ privilege_t *save_priv) const
{
want_access &= ~SELECT_ACL;
@@ -10375,7 +9986,7 @@ IS_internal_schema_access::check(ulong want_access,
We don't allow any simple privileges but SELECT_ACL on
the information_schema database.
*/
- if (unlikely(want_access & DB_ACLS))
+ if (unlikely((want_access & DB_ACLS) != NO_ACL))
return ACL_INTERNAL_ACCESS_DENIED;
/* Always grant SELECT for the information schema. */
@@ -10400,99 +10011,6 @@ void initialize_information_schema_acl()
&is_internal_schema_access);
}
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-/*
- Convert a string in character set in column character set format
- to utf8 character set if possible, the utf8 character set string
- will later possibly be converted to character set used by client.
- Thus we attempt conversion from column character set to both
- utf8 and to character set client.
-
- Examples of strings that should fail conversion to utf8 are unassigned
- characters as e.g. 0x81 in cp1250 (Windows character set for for countries
- like Czech and Poland). Example of string that should fail conversion to
- character set on client (e.g. if this is latin1) is 0x2020 (daggger) in
- ucs2.
-
- If the conversion fails we will as a fall back convert the string to
- hex encoded format. The caller of the function can also ask for hex
- encoded format of output string unconditionally.
-
- SYNOPSIS
- get_cs_converted_string_value()
- thd Thread object
- input_str Input string in cs character set
- output_str Output string to be produced in utf8
- cs Character set of input string
- use_hex Use hex string unconditionally
-
-
- RETURN VALUES
- No return value
-*/
-
-static void get_cs_converted_string_value(THD *thd,
- String *input_str,
- String *output_str,
- CHARSET_INFO *cs,
- bool use_hex)
-{
-
- output_str->length(0);
- if (input_str->length() == 0)
- {
- output_str->append("''");
- return;
- }
- if (!use_hex)
- {
- String try_val;
- uint try_conv_error= 0;
-
- try_val.copy(input_str->ptr(), input_str->length(), cs,
- thd->variables.character_set_client, &try_conv_error);
- if (likely(!try_conv_error))
- {
- String val;
- uint conv_error= 0;
-
- val.copy(input_str->ptr(), input_str->length(), cs,
- system_charset_info, &conv_error);
- if (likely(!conv_error))
- {
- append_unescaped(output_str, val.ptr(), val.length());
- return;
- }
- }
- /* We had a conversion error, use hex encoded string for safety */
- }
- {
- const uchar *ptr;
- uint i, len;
- char buf[3];
-
- output_str->append("_");
- output_str->append(cs->csname);
- output_str->append(" ");
- output_str->append("0x");
- len= input_str->length();
- ptr= (uchar*)input_str->ptr();
- for (i= 0; i < len; i++)
- {
- uint high, low;
-
- high= (*ptr) >> 4;
- low= (*ptr) & 0x0F;
- buf[0]= _dig_vec_upper[high];
- buf[1]= _dig_vec_upper[low];
- buf[2]= 0;
- output_str->append((const char*)buf);
- ptr++;
- }
- }
- return;
-}
-#endif
/**
Dumps a text description of a thread, its security context
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 39cbc35230a..80588cda8b5 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -76,10 +76,19 @@ typedef struct system_status_var STATUS_VAR;
#define IS_FILES_EXTRA 37
typedef enum { WITHOUT_DB_NAME, WITH_DB_NAME } enum_with_db_name;
+
+int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond);
+
int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
Table_specification_st *create_info_arg,
enum_with_db_name with_db_name);
+int show_create_table_ex(THD *thd, TABLE_LIST *table_list,
+ const char * forced_db, const char *forced_name,
+ String *packet,
+ Table_specification_st *create_info_arg,
+ enum_with_db_name with_db_name);
+
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
bool append_identifier(THD *thd, String *packet, const char *name, size_t length);
@@ -110,6 +119,7 @@ bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user,
const LEX_CSTRING *definer_host);
int add_status_vars(SHOW_VAR *list);
void remove_status_vars(SHOW_VAR *list);
+ulonglong get_status_vars_version(void);
void init_status_vars();
void free_status_vars();
void reset_status_vars();
@@ -117,9 +127,7 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name);
void view_store_options(THD *thd, TABLE_LIST *table, String *buff);
void init_fill_schema_files_row(TABLE* table);
-bool schema_table_store_record(THD *thd, TABLE *table);
void initialize_information_schema_acl();
-COND *make_cond_for_info_schema(THD *thd, COND *cond, TABLE_LIST *table);
ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name,
bool *in_plugin);
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index 320a954711a..115f5fa4347 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -104,7 +105,7 @@ static bool assign_fixed_string(MEM_ROOT *mem_root,
src_cs= src->charset();
src_len= src->length();
src_end= src_str + src_len;
- numchars= src_cs->cset->numchars(src_cs, src_str, src_end);
+ numchars= src_cs->numchars(src_str, src_end);
if (numchars <= max_char)
{
@@ -114,7 +115,7 @@ static bool assign_fixed_string(MEM_ROOT *mem_root,
else
{
numchars= max_char;
- to_copy= dst_cs->cset->charpos(dst_cs, src_str, src_end, numchars);
+ to_copy= dst_cs->charpos(src_str, src_end, numchars);
truncated= true;
}
@@ -151,7 +152,7 @@ static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
Item *set, String *ci)
{
char str_buff[(64+1)*4]; /* Room for a null terminated UTF8 String 64 */
- String str_value(str_buff, sizeof(str_buff), & my_charset_utf8_bin);
+ String str_value(str_buff, sizeof(str_buff), & my_charset_utf8mb3_bin);
String *str;
bool truncated;
@@ -164,7 +165,7 @@ static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
}
str= set->val_str(& str_value);
- truncated= assign_fixed_string(mem_root, & my_charset_utf8_bin, 64, ci, str);
+ truncated= assign_fixed_string(mem_root, & my_charset_utf8mb3_bin, 64, ci, str);
if (truncated)
{
if (thd->is_strict_mode())
@@ -260,7 +261,7 @@ int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *con
bool truncated;
String utf8_text;
str= set->val_str(& str_value);
- truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8_bin,
+ truncated= assign_fixed_string(thd->mem_root, & my_charset_utf8mb3_bin,
MYSQL_ERRMSG_SIZE,
& utf8_text, str);
if (truncated)
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 7abbc808632..a474d7c25e9 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -2,6 +2,7 @@
#define SQL_SORT_INCLUDED
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2020, MariaDB Corporation.
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,10 +20,8 @@
#include "my_base.h" /* ha_rows */
#include <my_sys.h> /* qsort2_cmp */
#include "queues.h"
+#include "sql_class.h"
-typedef struct st_buffpek BUFFPEK;
-
-struct SORT_FIELD;
class Field;
struct TABLE;
@@ -64,50 +63,650 @@ struct BUFFPEK_COMPARE_CONTEXT
};
+/**
+ Descriptor for a merge chunk to be sort-merged.
+ A merge chunk is a sequence of pre-sorted records, written to a
+ temporary file. A Merge_chunk instance describes where this chunk is stored
+ in the file, and where it is located when it is in memory.
+
+ It is a POD because
+ - we read/write them from/to files.
+
+ We have accessors (getters/setters) for all struct members.
+ */
+
+struct Merge_chunk {
+public:
+ my_off_t file_position() const { return m_file_position; }
+ void set_file_position(my_off_t val) { m_file_position= val; }
+ void advance_file_position(my_off_t val) { m_file_position+= val; }
+
+ uchar *buffer_start() { return m_buffer_start; }
+ const uchar *buffer_end() const { return m_buffer_end; }
+
+ void set_buffer(uchar *start, uchar *end)
+ {
+ m_buffer_start= start;
+ m_buffer_end= end;
+ }
+ void set_buffer_start(uchar *start)
+ {
+ m_buffer_start= start;
+ }
+ void set_buffer_end(uchar *end)
+ {
+ DBUG_ASSERT(m_buffer_end == NULL || end <= m_buffer_end);
+ m_buffer_end= end;
+ }
+
+ void init_current_key() { m_current_key= m_buffer_start; }
+ uchar *current_key() { return m_current_key; }
+ void advance_current_key(uint val) { m_current_key+= val; }
+
+ void decrement_rowcount(ha_rows val) { m_rowcount-= val; }
+ void set_rowcount(ha_rows val) { m_rowcount= val; }
+ ha_rows rowcount() const { return m_rowcount; }
+
+ ha_rows mem_count() const { return m_mem_count; }
+ void set_mem_count(ha_rows val) { m_mem_count= val; }
+ ha_rows decrement_mem_count() { return --m_mem_count; }
+
+ ha_rows max_keys() const { return m_max_keys; }
+ void set_max_keys(ha_rows val) { m_max_keys= val; }
+
+ size_t buffer_size() const { return m_buffer_end - m_buffer_start; }
+
+ /**
+ Tries to merge *this with *mc, returns true if successful.
+ The assumption is that *this is no longer in use,
+ and the space it has been allocated can be handed over to a
+ buffer which is adjacent to it.
+ */
+ bool merge_freed_buff(Merge_chunk *mc) const
+ {
+ if (mc->m_buffer_end == m_buffer_start)
+ {
+ mc->m_buffer_end= m_buffer_end;
+ mc->m_max_keys+= m_max_keys;
+ return true;
+ }
+ else if (mc->m_buffer_start == m_buffer_end)
+ {
+ mc->m_buffer_start= m_buffer_start;
+ mc->m_max_keys+= m_max_keys;
+ return true;
+ }
+ return false;
+ }
+
+ /// The current key for this chunk
+ uchar *m_current_key= nullptr;
+ /// Current position in the file to be sorted.
+ my_off_t m_file_position= 0;
+ /// Start of main-memory buffer for this chunk.
+ uchar *m_buffer_start= nullptr;
+ /// End of main-memory buffer for this chunk.
+ uchar *m_buffer_end= nullptr;
+ /// Number of unread rows in this chunk.
+ ha_rows m_rowcount= 0;
+ /// Number of rows in the main-memory buffer.
+ ha_rows m_mem_count= 0;
+ /// If we have fixed-size rows: max number of rows in buffer.
+ ha_rows m_max_keys= 0;
+};
+
+typedef Bounds_checked_array<SORT_ADDON_FIELD> Addon_fields_array;
+typedef Bounds_checked_array<SORT_FIELD> Sort_keys_array;
+
+/**
+ This class wraps information about usage of addon fields.
+ An Addon_fields object is used both during packing of data in the filesort
+ buffer, and later during unpacking in 'Filesort_info::unpack_addon_fields'.
+
+ @see documentation for the Sort_addon_field struct.
+ @see documentation for get_addon_fields()
+ */
+class Addon_fields {
+public:
+ Addon_fields(Addon_fields_array arr)
+ : m_field_descriptors(arr),
+ m_addon_buf(),
+ m_addon_buf_length(),
+ m_using_packed_addons(false)
+ {
+ DBUG_ASSERT(!arr.is_null());
+ }
+
+ SORT_ADDON_FIELD *begin() { return m_field_descriptors.begin(); }
+ SORT_ADDON_FIELD *end() { return m_field_descriptors.end(); }
+
+ /// rr_unpack_from_tempfile needs an extra buffer when unpacking.
+ uchar *allocate_addon_buf(uint sz)
+ {
+ m_addon_buf= (uchar *)my_malloc(PSI_INSTRUMENT_ME, sz, MYF(MY_WME | MY_THREAD_SPECIFIC));
+ if (m_addon_buf)
+ m_addon_buf_length= sz;
+ return m_addon_buf;
+ }
+
+ void free_addon_buff()
+ {
+ my_free(m_addon_buf);
+ m_addon_buf= NULL;
+ m_addon_buf_length= 0;
+ }
+
+ uchar *get_addon_buf() { return m_addon_buf; }
+ uint get_addon_buf_length() const { return m_addon_buf_length; }
+
+ void set_using_packed_addons(bool val)
+ {
+ m_using_packed_addons= val;
+ }
+
+ bool using_packed_addons() const
+ {
+ return m_using_packed_addons;
+ }
+
+ static bool can_pack_addon_fields(uint record_length)
+ {
+ return (record_length <= (0xFFFF));
+ }
+
+ /**
+ @returns Total number of bytes used for packed addon fields.
+ the size of the length field + size of null bits + sum of field sizes.
+ */
+ static uint read_addon_length(uchar *p)
+ {
+ return size_of_length_field + uint2korr(p);
+ }
+
+ /**
+ Stores the number of bytes used for packed addon fields.
+ */
+ static void store_addon_length(uchar *p, uint sz)
+ {
+ // We actually store the length of everything *after* the length field.
+ int2store(p, sz - size_of_length_field);
+ }
+
+ static const uint size_of_length_field= 2;
+
+private:
+ Addon_fields_array m_field_descriptors;
+
+ uchar *m_addon_buf; ///< Buffer for unpacking addon fields.
+ uint m_addon_buf_length; ///< Length of the buffer.
+ bool m_using_packed_addons; ///< Are we packing the addon fields?
+};
+
+/**
+ This class wraps information about usage of sort keys.
+ A Sort_keys object is used both during packing of data in the filesort
+ buffer, and later during unpacking in 'Filesort_info::unpack_addon_fields'.
+
+ @see SORT_FIELD struct.
+*/
+
+class Sort_keys :public Sql_alloc,
+ public Sort_keys_array
+{
+public:
+ Sort_keys(SORT_FIELD* arr, size_t count):
+ Sort_keys_array(arr, count),
+ m_using_packed_sortkeys(false),
+ size_of_packable_fields(0),
+ sort_length_with_original_values(0),
+ sort_length_with_memcmp_values(0),
+ parameters_computed(false)
+ {
+ DBUG_ASSERT(!is_null());
+ }
+
+ bool using_packed_sortkeys() const
+ { return m_using_packed_sortkeys; }
+
+ void set_using_packed_sortkeys(bool val)
+ {
+ m_using_packed_sortkeys= val;
+ }
+ void set_size_of_packable_fields(uint len)
+ {
+ size_of_packable_fields= len;
+ }
+
+ uint get_size_of_packable_fields()
+ {
+ return size_of_packable_fields;
+ }
+
+ void set_sort_length_with_original_values(uint len)
+ {
+ sort_length_with_original_values= len;
+ }
+
+ uint get_sort_length_with_original_values()
+ {
+ return sort_length_with_original_values;
+ }
+
+ void set_sort_length_with_memcmp_values(uint len)
+ {
+ sort_length_with_memcmp_values= len;
+ }
+
+ uint get_sort_length_with_memcmp_values()
+ {
+ return sort_length_with_memcmp_values;
+ }
+
+ static void store_sortkey_length(uchar *p, uint sz)
+ {
+ int4store(p, sz - size_of_length_field);
+ }
+
+ static uint read_sortkey_length(uchar *p)
+ {
+ return size_of_length_field + uint4korr(p);
+ }
+
+ void increment_size_of_packable_fields(uint len)
+ {
+ size_of_packable_fields+= len;
+ }
+
+ void increment_original_sort_length(uint len)
+ {
+ sort_length_with_original_values+= len;
+ }
+
+ bool is_parameters_computed() { return parameters_computed; }
+ void set_parameters_computed(bool val) { parameters_computed= val; }
+
+ static const uint size_of_length_field= 4;
+
+private:
+ bool m_using_packed_sortkeys; // Are we packing sort keys
+ uint size_of_packable_fields; // Total length bytes for packable columns
+
+ /*
+ The sort length for all the keyparts storing the original values
+ */
+ uint sort_length_with_original_values;
+
+ /*
+ The sort length for all the keyparts storing the mem-comparable images
+ */
+ uint sort_length_with_memcmp_values;
+
+ /*
+ TRUE parameters(like sort_length_* , size_of_packable_field)
+ are computed
+ FALSE otherwise.
+ */
+ bool parameters_computed;
+};
+
+
+/**
+PACKED SORT KEYS
+
+Description
+
+In this optimization where we would like the pack the values of the sort key
+inside the sort buffer for each record.
+
+Contents:
+1. Background
+1.1 Implementation details
+2. Solution : Packed Sort Keys
+2.1 Packed key format
+2.2 Which format to use
+3. Special cases
+3.1 Handling very long strings
+3.2 Handling for long binary strings
+3.3 Handling very long strings with Packed sort keys
+4. Sort key columns in addon_fields
+
+1. Background
+Before this optimization of using packed sort keys, filesort() sorted the
+data using mem-comparable keys.
+
+That is, if we wanted to sort by
+
+ ORDER BY col1, col2, ... colN
+then the filesort code would for each row generate one "Sort Key"
+and then sort the rows by their Sort Keys.
+
+The Sort Keys are mem-comparable (that is, are compared by memcmp()) and
+they are of FIXED SIZE. The sort key has the same length regardless of
+what value it represents. This causes INEFFICIENT MEMORY USAGE.
+
+1.1 Implementation details
+
+make_sortkey() is the function that produces a sort key
+from a record.
+
+The function treats Field and Item objects differently.
+
+class Field has:
+
+a) void make_sort_key(uchar *buff, uint length);
+ make_sort_key is a non-virtual function which handles encoding of
+ SQL null values.
+
+b) virtual void sort_string(uchar *buff,uint length)=0;
+ sort_string produces mem-comparable image of the field value
+ for each datatype.
+
+For Items, Type_handler has a virtual function:
+
+ virtual void make_sort_key(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const= 0;
+ which various datatypes overload.
+
+
+2. SOLUTION: PACKED SORT KEYS
+
+Note that one can have mem-comparable keys are that are not fixed-size.
+MyRocks uses such encoding for example.
+
+However for this optimization it was decided to store the original
+(non-mem-comparable) values instead and use a datatype-aware
+key comparison function.
+
+2.1 Packed key format
+The keys are stored in a new variable-size data format called "packed".
+
+The format is as follows:
+
+ <sort_key_length><packed_value_1><packed_value2> ....... <packed_valueN>
+
+ format for a n-part sort key
+
+<sort_key_length> is the length of the whole key.
+Each packed value is encoded as follows:
+
+ <null_byte=0> // This is a an SQL NULL
+ [<null_byte=1>] <packed_value> // this a non-NULL value
+null_byte is present if the field/item is NULLable.
+SQL NULL is encoded as just one NULL-indicator byte. The value itself is omitted.
+
+The format of the packed_value depends on the datatype.
+For "non-packable" datatypes it is just their mem-comparable form, as before.
+
+The "packable" datatypes are currently variable-length strings and the
+packed format for them is (for binary blobs, see a note below):
+
+<length> <string>
+2.2 Which format to use
+
+The advantage of Packed Key Format is potential space savings for
+variable-length fields.
+
+The disadvantages are:
+
+a) It may actually take more space, because of sort_key_length and
+ length fields.
+b) The comparison function is more expensive.
+
+Currently the logic is: use Packed Key Format if we would save 128 or more
+bytes when constructing a sort key from values that have empty string
+for each packable component.
+
+3. SPECIAL CASES
+3.1 HANDLING VERY LONG STRINGS
+the size of sort key part was limited by @@max_sort_length variable.
+It is defined as:
+
+The number of bytes to use when sorting data values. The server uses only the
+first max_sort_length bytes of each value and ignores the rest.
+
+3.2 HANDLING VERY LONG BINARY STRINGS
+Long binary strings receive special treatment. A sort key for the long
+binary string is truncated at max_sort_length bytes like described above,
+but then a "suffix" is appended which contains the total length of the
+value before the truncation.
+
+3.3 HANDLING VERY LONG STRINGS WITH PACKED SORT KEY
+Truncating multi-byte string at N bytes is not safe because one can cut in the
+middle of a character. One is tempted to solve this by discarding the partial
+character but that's also not a good idea as in some collations multiple
+characters may produce one weight (this is called "contraction").
+
+This combination of circumstances:
+
+The string value is very long, so truncation is necessary
+The collation is "complex", so truncation is dangerous
+is deemed to be relatively rare so it was decided to just use
+the non-packed sort keys in this case.
+
+4. SORT KEY COLUMNS IN ADDON FIELDS
+Currently, each sort key column is actually stored twice
+1. as part of the sort key
+2. in the addon_fields
+This made total sense when sort key stored the mem-comparable image
+(from which one cannot restore the original value in general case).
+But since we now store the original value, we could also remove it from the
+addon_fields and further save space. This is still a limitation and needs
+to be fixed later
+
+@see Sort_keys
+
+**/
+
+/**
+ The sort record format may use one of two formats for the non-sorted part of
+ the record:
+
+ 1. Use the rowid
+
+ |<sort_key>| <rowid> |
+ / / ref_length /
+
+ 2. Use "addon fields"
+
+ |<sort_key>|<null bits>|<field a><field b>...|
+ / / addon_length /
+
+ The packed format for "addon fields"
+
+ |<sort_key>|<length>|<null bits>|<field a><field b>...|
+ / / addon_length /
+
+ <sort_key> The key may use one of the two formats:
+ A. fixed-size mem-comparable form. The record is always
+ sort_length bytes long.
+ B. "PackedKeyFormat" - the records are variable-size.
+
+ <key> Fields are fixed-size, specially encoded with
+ Field::make_sort_key() so we can do byte-by-byte compare.
+
+ <length> Contains the *actual* packed length (after packing) of
+ everything after the sort keys.
+ The size of the length field is 2 bytes,
+ which should cover most use cases: addon data <= 65535 bytes.
+ This is the same as max record size in MySQL.
+ <null bits> One bit for each nullable field, indicating whether the field
+ is null or not. May have size zero if no fields are nullable.
+ <field xx> Are stored with field->pack(), and retrieved with
+ field->unpack(). Addon fields within a record are stored
+ consecutively, with no "holes" or padding. They will have zero
+ size for NULL values.
+
+*/
+
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 addon_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; // 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.
- LEX_STRING addon_buf; // Buffer & length of added packed fields.
+ /**
+ ORDER BY list with some precalculated info for filesort.
+ Array is created and owned by a Filesort instance.
+ */
+ Bounds_checked_array<SORT_FIELD> local_sortorder;
+ Addon_fields *addon_fields; // Descriptors for companion fields.
+ Sort_keys *sort_keys;
+ bool using_pq;
uchar *unique_buff;
bool not_killable;
- char* tmp_buffer;
+ String tmp_buffer;
// The fields below are used only by Unique class.
qsort2_cmp compare;
BUFFPEK_COMPARE_CONTEXT cmp_context;
Sort_param()
{
- memset(this, 0, sizeof(*this));
+ memset(reinterpret_cast<void*>(this), 0, sizeof(*this));
+ tmp_buffer.set_thread_specific();
+ /*
+ Fix memset() clearing the charset.
+ TODO: The constructor should be eventually rewritten not to use memset().
+ */
+ tmp_buffer.set_charset(&my_charset_bin);
}
void init_for_filesort(uint sortlen, TABLE *table,
ha_rows maxrows, bool sort_positions);
+ /// Enables the packing of addons if possible.
+ void try_to_pack_addons(ulong max_length_for_sort_data);
+
+ /// Are we packing the "addon fields"?
+ bool using_packed_addons() const
+ {
+ DBUG_ASSERT(m_using_packed_addons ==
+ (addon_fields != NULL &&
+ addon_fields->using_packed_addons()));
+ return m_using_packed_addons;
+ }
+
+ bool using_packed_sortkeys() const
+ {
+ DBUG_ASSERT(m_using_packed_sortkeys ==
+ (sort_keys != NULL && sort_keys->using_packed_sortkeys()));
+ return m_using_packed_sortkeys;
+ }
+
+ /// Are we using "addon fields"?
+ bool using_addon_fields() const
+ {
+ return addon_fields != NULL;
+ }
+
+ uint32 get_result_length(uchar *plen)
+ {
+ if (!m_using_packed_addons)
+ return res_length;
+ return Addon_fields::read_addon_length(plen);
+ }
+
+ uint32 get_addon_length(uchar *plen)
+ {
+ if (using_packed_addons())
+ return Addon_fields::read_addon_length(plen);
+ else
+ return addon_length;
+ }
+
+ uint32 get_sort_length(uchar *plen)
+ {
+ if (using_packed_sortkeys())
+ return Sort_keys::read_sortkey_length(plen) +
+ /*
+ when addon fields are not present, then the sort_length also
+ includes the res_length. For packed keys here we add
+ the res_length
+ */
+ (using_addon_fields() ? 0: res_length);
+ else
+ return sort_length;
+ }
+
+ uint get_record_length(uchar *plen)
+ {
+ if (m_packed_format)
+ {
+ uint sort_len= get_sort_length(plen);
+ return sort_len + get_addon_length(plen + sort_len);
+ }
+ else
+ return rec_length;
+ }
+
+ /**
+ Getter for record length and result length.
+ @param record_start Pointer to record.
+ @param [out] recl Store record length here.
+ @param [out] resl Store result length here.
+ */
+ void get_rec_and_res_len(uchar *record_start, uint *recl, uint *resl)
+ {
+ if (m_packed_format)
+ {
+ uint sort_len= get_sort_length(record_start);
+ uint addon_len= get_addon_length(record_start + sort_len);
+ *recl= sort_len + addon_len;
+ *resl= using_addon_fields() ? addon_len : res_length;
+ }
+ else
+ {
+ *recl= rec_length;
+ *resl= res_length;
+ }
+ }
+
+ void try_to_pack_sortkeys();
+
+ qsort2_cmp get_compare_function() const
+ {
+ return using_packed_sortkeys() ?
+ get_packed_keys_compare_ptr() :
+ get_ptr_compare(sort_length);
+ }
+ void* get_compare_argument(size_t *sort_len) const
+ {
+ return using_packed_sortkeys() ?
+ (void*) this :
+ (void*) sort_len;
+ }
+
+ bool is_packed_format() const
+ {
+ return m_packed_format;
+ }
+
+private:
+ uint m_packable_length;
+ bool m_using_packed_addons; ///< caches the value of using_packed_addons()
+ /* caches the value of using_packed_sortkeys() */
+ bool m_using_packed_sortkeys;
+ bool m_packed_format;
};
+typedef Bounds_checked_array<uchar> Sort_buffer;
-int merge_many_buff(Sort_param *param, uchar *sort_buffer,
- BUFFPEK *buffpek,
- uint *maxbuffer, IO_CACHE *t_file);
-ulong read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
- uint sort_length);
+int merge_many_buff(Sort_param *param, Sort_buffer sort_buffer,
+ Merge_chunk *buffpek, uint *maxbuffer, IO_CACHE *t_file);
+ulong read_to_buffer(IO_CACHE *fromfile, Merge_chunk *buffpek,
+ Sort_param *param, bool packing_format);
bool 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);
+ IO_CACHE *to_file, Sort_buffer sort_buffer,
+ Merge_chunk *lastbuff, Merge_chunk *Fb,
+ Merge_chunk *Tb, int flag);
+int merge_index(Sort_param *param, Sort_buffer sort_buffer,
+ Merge_chunk *buffpek, uint maxbuffer,
+ IO_CACHE *tempfile, IO_CACHE *outfile);
+void reuse_freed_buff(QUEUE *queue, Merge_chunk *reuse, uint key_length);
#endif /* SQL_SORT_INCLUDED */
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index ed569d6eab8..2636299e330 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -230,17 +230,17 @@ index_stat_def= {INDEX_STAT_N_FIELDS, index_stat_fields, 4, index_stat_pk_col};
Open all statistical tables and lock them
*/
-static int open_stat_tables(THD *thd, TABLE_LIST *tables,
- Open_tables_backup *backup, bool for_write)
+static int open_stat_tables(THD *thd, TABLE_LIST *tables, bool for_write)
{
int rc;
-
Dummy_error_handler deh; // suppress errors
+ DBUG_ASSERT(thd->internal_transaction());
+
thd->push_internal_handler(&deh);
init_table_list_for_stat_tables(tables, for_write);
init_mdl_requests(tables);
thd->in_sub_stmt|= SUB_STMT_STAT_TABLES;
- rc= open_system_tables_for_read(thd, tables, backup);
+ rc= open_system_tables_for_read(thd, tables);
thd->in_sub_stmt&= ~SUB_STMT_STAT_TABLES;
thd->pop_internal_handler();
@@ -253,7 +253,7 @@ static int open_stat_tables(THD *thd, TABLE_LIST *tables,
stat_table_intact.check(tables[COLUMN_STAT].table, &column_stat_def) ||
stat_table_intact.check(tables[INDEX_STAT].table, &index_stat_def)))
{
- close_system_tables(thd, backup);
+ close_thread_tables(thd);
rc= 1;
}
@@ -270,13 +270,12 @@ static int open_stat_tables(THD *thd, TABLE_LIST *tables,
stat tables need to be adjusted accordingly.
*/
static inline int open_stat_table_for_ddl(THD *thd, TABLE_LIST *table,
- const LEX_CSTRING *stat_tab_name,
- Open_tables_backup *backup)
+ const LEX_CSTRING *stat_tab_name)
{
table->init_one_table(&MYSQL_SCHEMA_NAME, stat_tab_name, NULL, TL_WRITE);
No_such_table_error_handler nst_handler;
thd->push_internal_handler(&nst_handler);
- int res= open_system_tables_for_read(thd, table, backup);
+ int res= open_system_tables_for_read(thd, table);
thd->pop_internal_handler();
return res;
}
@@ -1037,27 +1036,31 @@ public:
stat_field->set_notnull();
switch (i) {
case COLUMN_STAT_MIN_VALUE:
+ {
+ /*
+ TODO varun: After MDEV-22583 is fixed, add a function in Field_bit
+ and move this implementation there
+ */
if (table_field->type() == MYSQL_TYPE_BIT)
stat_field->store(table_field->collected_stats->min_value->val_int(),true);
else
{
- table_field->collected_stats->min_value->val_str(&val);
- size_t length= Well_formed_prefix(val.charset(), val.ptr(),
- MY_MIN(val.length(), stat_field->field_length)).length();
- stat_field->store(val.ptr(), length, &my_charset_bin);
+ Field *field= table_field->collected_stats->min_value;
+ field->store_to_statistical_minmax_field(stat_field, &val);
}
break;
+ }
case COLUMN_STAT_MAX_VALUE:
+ {
if (table_field->type() == MYSQL_TYPE_BIT)
stat_field->store(table_field->collected_stats->max_value->val_int(),true);
else
{
- table_field->collected_stats->max_value->val_str(&val);
- size_t length= Well_formed_prefix(val.charset(), val.ptr(),
- MY_MIN(val.length(), stat_field->field_length)).length();
- stat_field->store(val.ptr(), length, &my_charset_bin);
+ Field *field= table_field->collected_stats->max_value;
+ field->store_to_statistical_minmax_field(stat_field, &val);
}
break;
+ }
case COLUMN_STAT_NULLS_RATIO:
stat_field->store(table_field->collected_stats->get_nulls_ratio());
break;
@@ -1134,17 +1137,19 @@ public:
switch (i) {
case COLUMN_STAT_MIN_VALUE:
- table_field->read_stats->min_value->set_notnull();
- stat_field->val_str(&val);
- table_field->read_stats->min_value->store(val.ptr(), val.length(),
- &my_charset_bin);
+ {
+ Field *field= table_field->read_stats->min_value;
+ field->set_notnull();
+ field->store_from_statistical_minmax_field(stat_field, &val);
break;
+ }
case COLUMN_STAT_MAX_VALUE:
- table_field->read_stats->max_value->set_notnull();
- stat_field->val_str(&val);
- table_field->read_stats->max_value->store(val.ptr(), val.length(),
- &my_charset_bin);
+ {
+ Field *field= table_field->read_stats->max_value;
+ field->set_notnull();
+ field->store_from_statistical_minmax_field(stat_field, &val);
break;
+ }
case COLUMN_STAT_NULLS_RATIO:
table_field->read_stats->set_nulls_ratio(stat_field->val_real());
break;
@@ -1432,7 +1437,7 @@ public:
*/
bool init(uint n_keyparts)
{
- if (!(rowid_buf= (uchar*)my_malloc(rowid_size, MYF(0))))
+ if (!(rowid_buf= (uchar*)my_malloc(PSI_INSTRUMENT_ME, rowid_size, MYF(0))))
return true;
if (open_cached_file(&io_cache, mysql_tmpdir, TEMP_PREFIX,
@@ -2783,18 +2788,18 @@ int collect_statistics_for_table(THD *thd, TABLE *table)
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))
+ start_new_trans new_trans(thd);
+
+ if (open_stat_tables(thd, tables, TRUE))
DBUG_RETURN(rc);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -2844,8 +2849,9 @@ int update_statistics_for_table(THD *thd, TABLE *table)
}
thd->restore_stmt_binlog_format(save_binlog_format);
-
- close_system_tables(thd, &open_tables_backup);
+ if (thd->commit_whole_transaction_and_close_tables())
+ rc= 1;
+ new_trans.restore_old_transaction();
DBUG_RETURN(rc);
}
@@ -3161,7 +3167,6 @@ static void dump_stats_from_share_to_table(TABLE *table)
int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST stat_tables[STATISTICS_TABLES];
- Open_tables_backup open_tables_backup;
DBUG_ENTER("read_statistics_for_tables");
@@ -3206,7 +3211,9 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
if (found_stat_table || !statistics_for_tables_is_needed)
DBUG_RETURN(0);
- if (open_stat_tables(thd, stat_tables, &open_tables_backup, FALSE))
+ start_new_trans new_trans(thd);
+
+ if (open_stat_tables(thd, stat_tables, FALSE))
DBUG_RETURN(1);
for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
@@ -3228,7 +3235,8 @@ int read_statistics_for_tables(THD *thd, TABLE_LIST *tables)
}
}
- close_system_tables(thd, &open_tables_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
DBUG_RETURN(0);
}
@@ -3268,8 +3276,10 @@ int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db,
Open_tables_backup open_tables_backup;
int rc= 0;
DBUG_ENTER("delete_statistics_for_table");
+
+ start_new_trans new_trans(thd);
- if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
+ if (open_stat_tables(thd, tables, TRUE))
DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3312,8 +3322,8 @@ int delete_statistics_for_table(THD *thd, const LEX_CSTRING *db,
rc= 1;
thd->restore_stmt_binlog_format(save_binlog_format);
-
- close_system_tables(thd, &open_tables_backup);
+ thd->commit_whole_transaction_and_close_tables();
+ new_trans.restore_old_transaction();
DBUG_RETURN(rc);
}
@@ -3345,12 +3355,12 @@ int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
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_stat_table_for_ddl(thd, &tables, &stat_table_name[1],
- &open_tables_backup))
+ start_new_trans new_trans(thd);
+
+ if (open_stat_table_for_ddl(thd, &tables, &stat_table_name[1]))
DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3366,8 +3376,9 @@ int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
}
thd->restore_stmt_binlog_format(save_binlog_format);
-
- close_system_tables(thd, &open_tables_backup);
+ if (thd->commit_whole_transaction_and_close_tables())
+ rc= 1;
+ new_trans.restore_old_transaction();
DBUG_RETURN(rc);
}
@@ -3402,12 +3413,12 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
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_stat_table_for_ddl(thd, &tables, &stat_table_name[2],
- &open_tables_backup))
+ start_new_trans new_trans(thd);
+
+ if (open_stat_table_for_ddl(thd, &tables, &stat_table_name[2]))
DBUG_RETURN(0);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3443,8 +3454,9 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
rc= 1;
thd->restore_stmt_binlog_format(save_binlog_format);
-
- close_system_tables(thd, &open_tables_backup);
+ if (thd->commit_whole_transaction_and_close_tables())
+ rc= 1;
+ new_trans.restore_old_transaction();
DBUG_RETURN(rc);
}
@@ -3486,14 +3498,13 @@ int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db,
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))
- {
+ start_new_trans new_trans(thd);
+
+ if (open_stat_tables(thd, tables, TRUE))
DBUG_RETURN(0); // not an error
- }
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3542,8 +3553,9 @@ int rename_table_in_stat_tables(THD *thd, const LEX_CSTRING *db,
}
thd->restore_stmt_binlog_format(save_binlog_format);
-
- close_system_tables(thd, &open_tables_backup);
+ if (thd->commit_whole_transaction_and_close_tables())
+ rc= 1;
+ new_trans.restore_old_transaction();
DBUG_RETURN(rc);
}
@@ -3576,15 +3588,15 @@ int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
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_stat_table_for_ddl(thd, &tables, &stat_table_name[1],
- &open_tables_backup))
+ start_new_trans new_trans(thd);
+
+ if (open_stat_table_for_ddl(thd, &tables, &stat_table_name[1]))
DBUG_RETURN(rc);
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
@@ -3601,8 +3613,9 @@ int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
}
thd->restore_stmt_binlog_format(save_binlog_format);
-
- close_system_tables(thd, &open_tables_backup);
+ if (thd->commit_whole_transaction_and_close_tables())
+ rc= 1;
+ new_trans.restore_old_transaction();
DBUG_RETURN(rc);
}
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index c97b8a9a830..94f2e6fc8c6 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2016, MariaDB
+ Copyright (c) 2016, 2020, 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
@@ -41,8 +41,8 @@ bool Binary_string::real_alloc(size_t length)
if (Alloced_length < arg_length)
{
free();
- if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME |
- (thread_specific ?
+ if (!(Ptr=(char*) my_malloc(PSI_INSTRUMENT_ME,
+ arg_length,MYF(MY_WME | (thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
return TRUE;
DBUG_ASSERT(length < UINT_MAX32);
@@ -92,13 +92,13 @@ bool Binary_string::realloc_raw(size_t alloc_length)
return TRUE; /* Overflow */
if (alloced)
{
- if (!(new_ptr= (char*) my_realloc(Ptr,len,
+ if (!(new_ptr= (char*) my_realloc(PSI_INSTRUMENT_ME, Ptr,len,
MYF(MY_WME |
(thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
return TRUE; // Signal error
}
- else if ((new_ptr= (char*) my_malloc(len,
+ else if ((new_ptr= (char*) my_malloc(PSI_INSTRUMENT_ME, len,
MYF(MY_WME |
(thread_specific ?
MY_THREAD_SPECIFIC : 0)))))
@@ -125,7 +125,7 @@ bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs)
if (alloc(l))
return TRUE;
- str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,base,num);
+ str_length=(uint32) (cs->longlong10_to_str)(Ptr,l,base,num);
set_charset(cs);
return FALSE;
}
@@ -767,10 +767,10 @@ void Static_binary_string::qs_append(double d)
NULL);
}
-void Static_binary_string::qs_append(double *d)
+void Static_binary_string::qs_append(const double *d)
{
double ld;
- float8get(ld, (char*) d);
+ float8get(ld, (const char*) d);
qs_append(ld);
}
@@ -788,6 +788,29 @@ void Static_binary_string::qs_append(ulonglong i)
str_length+= (int) (end-buff);
}
+
+bool Binary_string::copy_printable_hhhh(CHARSET_INFO *to_cs,
+ CHARSET_INFO *from_cs,
+ const char *from,
+ size_t from_length)
+{
+ DBUG_ASSERT(from_length < UINT_MAX32);
+ uint errors;
+ uint one_escaped_char_length= MY_CS_PRINTABLE_CHAR_LENGTH * to_cs->mbminlen;
+ uint one_char_length= MY_MAX(one_escaped_char_length, to_cs->mbmaxlen);
+ ulonglong bytes_needed= from_length * one_char_length;
+ if (bytes_needed >= UINT_MAX32 || alloc((size_t) bytes_needed))
+ return true;
+ str_length= my_convert_using_func(Ptr, Alloced_length, to_cs,
+ to_cs->cset->wc_to_printable,
+ from, from_length,
+ from_cs,
+ from_cs->cset->mb_wc,
+ &errors);
+ return false;
+}
+
+
/*
Compare strings according to collation, without end space.
@@ -809,9 +832,8 @@ void Static_binary_string::qs_append(ulonglong i)
int sortcmp(const String *s,const String *t, CHARSET_INFO *cs)
{
- return cs->coll->strnncollsp(cs,
- (uchar *) s->ptr(),s->length(),
- (uchar *) t->ptr(),t->length());
+ return cs->strnncollsp(s->ptr(), s->length(),
+ t->ptr(), t->length());
}
@@ -1087,8 +1109,8 @@ String_copier::well_formed_copy(CHARSET_INFO *to_cs,
my_charset_same(from_cs, to_cs))
{
m_cannot_convert_error_pos= NULL;
- return (uint) to_cs->cset->copy_fix(to_cs, to, to_length, from, from_length,
- nchars, this);
+ return (uint) to_cs->copy_fix(to, to_length, from, from_length,
+ nchars, this);
}
return (uint) my_convert_fix(to_cs, to, to_length, from_cs, from, from_length,
nchars, this, this);
@@ -1225,3 +1247,25 @@ bool String::append_semi_hex(const char *s, uint len, CHARSET_INFO *cs)
str_length+= nbytes;
return false;
}
+
+// Shrink the buffer, but only if it is allocated on the heap.
+void Binary_string::shrink(size_t arg_length)
+{
+ if (!is_alloced())
+ return;
+ if (ALIGN_SIZE(arg_length + 1) < Alloced_length)
+ {
+ char* new_ptr;
+ if (!(new_ptr = (char*)my_realloc(STRING_PSI_MEMORY_KEY, Ptr, arg_length,
+ MYF(thread_specific ? MY_THREAD_SPECIFIC : 0))))
+ {
+ Alloced_length = 0;
+ real_alloc(arg_length);
+ }
+ else
+ {
+ Ptr = new_ptr;
+ Alloced_length = (uint32)arg_length;
+ }
+ }
+}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 85c3bbd6044..d1378e62a97 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -30,6 +30,13 @@
#include "sql_list.h"
class String;
+#ifdef MYSQL_SERVER
+extern PSI_memory_key key_memory_String_value;
+#define STRING_PSI_MEMORY_KEY key_memory_String_value
+#else
+#define STRING_PSI_MEMORY_KEY PSI_NOT_INSTRUMENTED
+#endif
+
typedef struct st_io_cache IO_CACHE;
typedef struct st_mem_root MEM_ROOT;
@@ -60,7 +67,7 @@ class Well_formed_prefix_status: public String_copy_status
public:
Well_formed_prefix_status(CHARSET_INFO *cs,
const char *str, const char *end, size_t nchars)
- { cs->cset->well_formed_char_length(cs, str, end, nchars, this); }
+ { cs->well_formed_char_length(str, end, nchars, this); }
};
@@ -141,20 +148,26 @@ public:
Charset(CHARSET_INFO *cs) :m_charset(cs) { }
CHARSET_INFO *charset() const { return m_charset; }
+ bool use_mb() const { return m_charset->use_mb(); }
uint mbminlen() const { return m_charset->mbminlen; }
uint mbmaxlen() const { return m_charset->mbmaxlen; }
+ bool is_good_for_ft() const
+ {
+ // Binary and UCS2/UTF16/UTF32 are not supported
+ return m_charset != &my_charset_bin && m_charset->mbminlen == 1;
+ }
size_t numchars(const char *str, const char *end) const
{
- return m_charset->cset->numchars(m_charset, str, end);
+ return m_charset->numchars(str, end);
}
size_t lengthsp(const char *str, size_t length) const
{
- return m_charset->cset->lengthsp(m_charset, str, length);
+ return m_charset->lengthsp(str, length);
}
size_t charpos(const char *str, const char *end, size_t pos) const
{
- return m_charset->cset->charpos(m_charset, str, end, pos);
+ return m_charset->charpos(str, end, pos);
}
void set_charset(CHARSET_INFO *charset_arg)
{
@@ -209,18 +222,6 @@ public:
inline bool is_empty() const { return (str_length == 0); }
inline const char *ptr() const { return Ptr; }
inline const char *end() const { return Ptr + str_length; }
-
- LEX_STRING lex_string() const
- {
- LEX_STRING str = { (char*) ptr(), length() };
- return str;
- }
- LEX_CSTRING lex_cstring() const
- {
- LEX_CSTRING skr = { ptr(), length() };
- return skr;
- }
-
bool has_8bit_bytes() const
{
for (const char *c= ptr(), *c_end= end(); c < c_end; c++)
@@ -344,7 +345,7 @@ public:
void qs_append(const char *str, size_t len);
void qs_append_hex(const char *str, uint32 len);
void qs_append(double d);
- void qs_append(double *d);
+ void qs_append(const double *d);
inline void qs_append(const char c)
{
Ptr[str_length]= c;
@@ -475,6 +476,12 @@ public:
if (str.Alloced_length)
Alloced_length= (uint32) (str.Alloced_length - offset);
}
+ inline LEX_CSTRING *get_value(LEX_CSTRING *res)
+ {
+ res->str= Ptr;
+ res->length= str_length;
+ return res;
+ }
/* Take over handling of buffer from some other object */
void reset(char *ptr_arg, size_t length_arg, size_t alloced_length_arg)
@@ -524,6 +531,15 @@ public:
bool copy(const char *s, size_t arg_length); // Allocate new string
bool copy_or_move(const char *s,size_t arg_length);
+ /**
+ Convert a string to a printable format.
+ All non-convertable and control characters are replaced to 5-character
+ sequences '\hhhh'.
+ */
+ bool copy_printable_hhhh(CHARSET_INFO *to_cs,
+ CHARSET_INFO *from_cs,
+ const char *from, size_t from_length);
+
bool append_ulonglong(ulonglong val);
bool append_longlong(longlong val);
@@ -536,6 +552,10 @@ public:
q_append(s, size);
return false;
}
+ bool append(const LEX_CSTRING &s)
+ {
+ return append(s.str, s.length);
+ }
bool append(const Binary_string &s)
{
return append(s.ptr(), s.length());
@@ -645,28 +665,8 @@ public:
return realloc_with_extra(arg_length);
}
// Shrink the buffer, but only if it is allocated on the heap.
- inline void shrink(size_t arg_length)
- {
- if (!is_alloced())
- return;
- if (ALIGN_SIZE(arg_length+1) < Alloced_length)
- {
- char *new_ptr;
- if (unlikely(!(new_ptr=(char*)
- my_realloc(Ptr,
- arg_length,MYF((thread_specific ?
- MY_THREAD_SPECIFIC : 0))))))
- {
- Alloced_length= 0;
- real_alloc(arg_length);
- }
- else
- {
- Ptr= new_ptr;
- Alloced_length= (uint32) arg_length;
- }
- }
- }
+ void shrink(size_t arg_length);
+
void move(Binary_string &s)
{
set_alloced(s.Ptr, s.str_length, s.Alloced_length);
@@ -845,6 +845,15 @@ public:
bool copy_aligned(const char *s, size_t arg_length, size_t offset,
CHARSET_INFO *cs);
bool set_or_copy_aligned(const char *s, size_t arg_length, CHARSET_INFO *cs);
+ bool can_be_safely_converted_to(CHARSET_INFO *tocs) const
+ {
+ if (charset() == &my_charset_bin)
+ return Well_formed_prefix(tocs, ptr(), length()).length() == length();
+ String try_val;
+ uint try_conv_error= 0;
+ try_val.copy(ptr(), length(), charset(), tocs, &try_conv_error);
+ return try_conv_error == 0;
+ }
bool copy(const char*s, size_t arg_length, CHARSET_INFO *csfrom,
CHARSET_INFO *csto, uint *errors);
bool copy(const String *str, CHARSET_INFO *tocs, uint *errors)
@@ -879,6 +888,14 @@ public:
{
return Binary_string::append_hex((const char*)src, srclen);
}
+ bool append_introducer_and_hex(const String *str)
+ {
+ return
+ append(STRING_WITH_LEN("_")) ||
+ append(str->charset()->csname) ||
+ append(STRING_WITH_LEN(" 0x")) ||
+ append_hex(str->ptr(), (uint32) str->length());
+ }
bool append(IO_CACHE* file, uint32 arg_length)
{
return Binary_string::append(file, arg_length);
@@ -1018,6 +1035,15 @@ public:
};
+template<size_t buff_sz>
+class BinaryStringBuffer : public Binary_string
+{
+ char buff[buff_sz];
+public:
+ BinaryStringBuffer() : Binary_string(buff, buff_sz) { length(0); }
+};
+
+
class String_space: public String
{
public:
@@ -1033,7 +1059,7 @@ static inline bool check_if_only_end_space(CHARSET_INFO *cs,
const char *str,
const char *end)
{
- return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end;
+ return str + cs->scan(str, end, MY_SEQ_SPACES) == end;
}
int append_query_string(CHARSET_INFO *csinfo, String *to,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 942b838c692..67a505d78bc 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -28,8 +28,9 @@
#include "sql_base.h" // lock_table_names
#include "lock.h" // mysql_unlock_tables
#include "strfunc.h" // find_type2, find_set
-#include "sql_truncate.h" // regenerate_locked_table
-#include "sql_partition.h" // mem_alloc_error,
+#include "sql_truncate.h" // regenerate_locked_table
+#include "ha_partition.h" // PAR_EXT
+ // mem_alloc_error,
// partition_info
// NOT_A_PARTITION_ID
#include "sql_db.h" // load_db_opt_by_name
@@ -54,6 +55,7 @@
#include "sql_audit.h"
#include "sql_sequence.h"
#include "tztime.h"
+#include "sql_insert.h" // binlog_drop_table
#include <algorithm>
#ifdef __WIN__
@@ -73,6 +75,9 @@ static int copy_data_between_tables(THD *, TABLE *,TABLE *,
ha_rows *, ha_rows *,
Alter_info::enum_enable_or_disable,
Alter_table_ctx *);
+static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info,
+ Alter_info *alter_info, KEY **key_info,
+ uint key_count);
static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
uint *, handler *, KEY **, uint *, int);
static uint blob_length_by_type(enum_field_types type);
@@ -132,7 +137,7 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
*(to_p++)= (char) quote;
while (*conv_name && (end_p - to_p - 1) > 0)
{
- int length= my_charlen(system_charset_info, conv_name, conv_name_end);
+ int length= system_charset_info->charlen(conv_name, conv_name_end);
if (length <= 0)
length= 1;
if (length == 1 && *conv_name == (char) quote)
@@ -573,7 +578,7 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
/**
- Create path to a temporary table mysql_tmpdir/#sql1234_12_1
+ Create path to a temporary table mysql_tmpdir/#sql-temptable-1234-12-1
(i.e. to its .FRM file but without an extension).
@param thd The thread handle.
@@ -592,7 +597,7 @@ 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_%llx_%x",
+ my_snprintf(p, bufflen - (p - buff), "/%s-temptable-%lx-%llx-%x",
tmp_file_prefix, current_pid,
thd->thread_id, thd->tmp_table++);
@@ -1102,12 +1107,9 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
LEX_CSTRING handler_name;
handler *file= NULL;
MEM_ROOT mem_root;
- int error= TRUE;
+ int error= 1;
char to_path[FN_REFLEN];
char from_path[FN_REFLEN];
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- char *par_ext= (char*)".par";
-#endif
handlerton *hton;
DBUG_ENTER("execute_ddl_log_action");
@@ -1127,8 +1129,8 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
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, "execute_ddl_log_action", TABLE_ALLOC_BLOCK_SIZE,
- 0, MYF(MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_gdl, &mem_root, TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
frm_action= TRUE;
else
@@ -1155,28 +1157,27 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
{
strxmov(to_path, ddl_log_entry->name, reg_ext, NullS);
if (unlikely((error= mysql_file_delete(key_file_frm, to_path,
- MYF(MY_WME)))))
- {
- if (my_errno != ENOENT)
- break;
- }
+ MYF(MY_WME |
+ MY_IGNORE_ENOENT)))))
+ break;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
- (void) mysql_file_delete(key_file_partition, to_path, MYF(MY_WME));
+ strxmov(to_path, ddl_log_entry->name, PAR_EXT, NullS);
+ (void) mysql_file_delete(key_file_partition_ddl_log, to_path,
+ MYF(0));
#endif
}
else
{
- if (unlikely((error= file->ha_delete_table(ddl_log_entry->name))))
+ if (unlikely((error= hton->drop_table(hton, ddl_log_entry->name))))
{
- if (error != ENOENT && error != HA_ERR_NO_SUCH_TABLE)
+ if (!non_existing_table_error(error))
break;
}
}
if ((deactivate_ddl_log_entry_no_lock(ddl_log_entry->entry_pos)))
break;
(void) sync_ddl_log_no_lock();
- error= FALSE;
+ error= 0;
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
break;
}
@@ -1198,9 +1199,9 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
if (mysql_file_rename(key_file_frm, from_path, to_path, MYF(MY_WME)))
break;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- strxmov(to_path, ddl_log_entry->name, par_ext, NullS);
- strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS);
- (void) mysql_file_rename(key_file_partition, from_path, to_path, MYF(MY_WME));
+ strxmov(to_path, ddl_log_entry->name, PAR_EXT, NullS);
+ strxmov(from_path, ddl_log_entry->from_name, PAR_EXT, NullS);
+ (void) mysql_file_rename(key_file_partition_ddl_log, from_path, to_path, MYF(MY_WME));
#endif
}
else
@@ -1297,7 +1298,7 @@ static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
if (global_ddl_log.first_free == NULL)
{
- if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
+ if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(key_memory_DDL_LOG_MEMORY_ENTRY,
sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME))))
{
sql_print_error("Failed to allocate memory for ddl log free list");
@@ -1763,8 +1764,8 @@ uint build_table_shadow_filename(char *buff, size_t bufflen,
ALTER_PARTITION_PARAM_TYPE *lpt)
{
char tmp_name[FN_REFLEN];
- my_snprintf(tmp_name, sizeof (tmp_name), "%s-%s", tmp_file_prefix,
- lpt->table_name.str);
+ my_snprintf(tmp_name, sizeof (tmp_name), "%s-shadow-%lx-%s", tmp_file_prefix,
+ (ulong) current_thd->thread_id, lpt->table_name.str);
return build_table_filename(buff, bufflen, lpt->db.str, tmp_name, "",
FN_IS_TMP);
}
@@ -1822,6 +1823,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 (append_system_key_parts(lpt->thd, lpt->create_info, lpt->alter_info,
+ &lpt->key_info_buffer, 0))
+ DBUG_RETURN(true);
+
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,
@@ -1856,8 +1861,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
goto end;
}
- int error= writefrm(shadow_path, lpt->db.str, lpt->table_name.str,
- lpt->create_info->tmp_table(), frm.str, frm.length);
+ int error= writefile(shadow_frm_name, lpt->db.str, lpt->table_name.str,
+ lpt->create_info->tmp_table(), frm.str, frm.length);
my_free(const_cast<uchar*>(frm.str));
if (unlikely(error) ||
@@ -1894,13 +1899,13 @@ 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_partitioning_metadata(path, shadow_path,
- CHF_DELETE_FLAG) ||
+ 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_partitioning_metadata(path, shadow_path,
- CHF_RENAME_FLAG))
+ CHF_RENAME_FLAG))
#else
mysql_file_rename(key_file_frm,
shadow_frm_name, frm_name, MYF(MY_WME)))
@@ -1995,6 +2000,26 @@ int write_bin_log(THD *thd, bool clear_error,
/*
+ Write to binary log with optional adding "IF EXISTS"
+
+ The query is taken from thd->query()
+*/
+
+int write_bin_log_with_if_exists(THD *thd, bool clear_error,
+ bool is_trans, bool add_if_exists)
+{
+ int result;
+ ulonglong save_option_bits= thd->variables.option_bits;
+ if (add_if_exists)
+ thd->variables.option_bits|= OPTION_IF_EXISTS;
+ result= write_bin_log(thd, clear_error, thd->query(), thd->query_length(),
+ is_trans);
+ thd->variables.option_bits= save_option_bits;
+ return result;
+}
+
+
+/*
delete (drop) tables.
SYNOPSIS
@@ -2004,6 +2029,7 @@ int write_bin_log(THD *thd, bool clear_error,
if_exists If 1, don't give error if one table doesn't exists
drop_temporary 1 if DROP TEMPORARY
drop_sequence 1 if DROP SEQUENCE
+ dont_log_query 1 if no write to binary log and no send of ok
NOTES
Will delete all tables that can be deleted and give a compact error
@@ -2021,7 +2047,8 @@ int write_bin_log(THD *thd, bool clear_error,
*/
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool drop_sequence)
+ bool drop_temporary, bool drop_sequence,
+ bool dont_log_query)
{
bool error;
Drop_table_error_handler err_handler;
@@ -2111,20 +2138,17 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
}
}
- DBUG_EXECUTE_IF("ib_purge_virtual_mdev_16222_1",
- DBUG_ASSERT(!debug_sync_set_action(
- thd,
- STRING_WITH_LEN("now SIGNAL drop_started"))););
-
/* 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, drop_sequence, false, false);
+ false, drop_sequence, dont_log_query,
+ false);
thd->pop_internal_handler();
if (unlikely(error))
DBUG_RETURN(TRUE);
- my_ok(thd);
+ if (!dont_log_query)
+ my_ok(thd);
DBUG_RETURN(FALSE);
}
@@ -2212,23 +2236,23 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool dont_free_locks)
{
TABLE_LIST *table;
- char path[FN_REFLEN + 1], wrong_tables_buff[160];
+ char path[FN_REFLEN + 1];
LEX_CSTRING alias= null_clex_str;
- String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1,
- system_charset_info);
- uint path_length= 0, errors= 0;
+ StringBuffer<160> unknown_tables(system_charset_info);
+ uint not_found_errors= 0;
int error= 0;
int non_temp_tables_count= 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, was_table= 0, is_sequence;
- String built_query;
+ bool was_view= 0, was_table= 0, log_if_exists= if_exists;
+ const char *object_to_drop= (drop_sequence) ? "SEQUENCE" : "TABLE";
+ String normal_tables;
String built_trans_tmp_query, built_non_trans_tmp_query;
DBUG_ENTER("mysql_rm_table_no_locks");
- wrong_tables.length(0);
+ unknown_tables.length(0);
/*
Prepares the drop statements that will be written into the binary
log as follows:
@@ -2263,30 +2287,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
logging, these commands will be written with fully qualified table names
and use `db` will be suppressed.
*/
+
+ normal_tables.set_charset(thd->charset());
if (!dont_log_query)
{
- const char *object_to_drop= (drop_sequence) ? "SEQUENCE" : "TABLE";
-
- if (!drop_temporary)
- {
- const char *comment_start;
- uint32 comment_len;
-
- built_query.set_charset(thd->charset());
- built_query.append("DROP ");
- built_query.append(object_to_drop);
- built_query.append(' ');
- if (if_exists)
- built_query.append("IF EXISTS ");
-
- /* Preserve comment in original query */
- if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start)))
- {
- built_query.append(comment_start, comment_len);
- built_query.append(" ");
- }
- }
-
built_trans_tmp_query.set_charset(system_charset_info);
built_trans_tmp_query.append("DROP TEMPORARY ");
built_trans_tmp_query.append(object_to_drop);
@@ -2302,13 +2306,20 @@ 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= 0;
+ bool is_trans= 0, temporary_table_was_dropped= 0;
bool table_creation_was_logged= 0;
- LEX_CSTRING db= table->db;
- handlerton *table_type= 0;
+ bool local_non_tmp_error= 0, wrong_drop_sequence= 0;
+ bool table_dropped= 0;
+ const LEX_CSTRING db= table->db;
+ const LEX_CSTRING table_name= table->table_name;
+ handlerton *hton= 0;
+ Table_type table_type;
+ size_t path_length= 0;
+ char *path_end= 0;
+ error= 0;
DBUG_PRINT("table", ("table_l: '%s'.'%s' table: %p s: %p",
- table->db.str, table->table_name.str, table->table,
+ db.str, table_name.str, table->table,
table->table ? table->table->s : NULL));
/*
@@ -2321,40 +2332,33 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
thd->find_temporary_table(table) &&
table->mdl_request.ticket != NULL));
- if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) ||
- (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE))
- error= 1;
- else
+ /* First try to delete temporary tables and temporary sequences */
+ if ((table->open_type != OT_BASE_ONLY && is_temporary_table(table)) &&
+ (!drop_sequence || table->table->s->table_type == TABLE_TYPE_SEQUENCE))
{
table_creation_was_logged= table->table->s->table_creation_was_logged;
if (thd->drop_temporary_table(table->table, &is_trans, true))
- {
error= 1;
- goto err;
+ else
+ {
+ table->table= 0;
+ temporary_table_was_dropped= 1;
}
- error= 0;
- table->table= 0;
}
- if ((drop_temporary && if_exists) || !error)
+ if ((drop_temporary && if_exists) || temporary_table_was_dropped)
{
/*
This handles the case of temporary tables. We have the following cases:
- . "DROP TEMPORARY" was executed and a temporary table was affected
- (i.e. drop_temporary && !error) or the if_exists was specified (i.e.
- drop_temporary && if_exists).
-
- . "DROP" was executed but a temporary table was affected (.i.e
- !error).
+ - "DROP TEMPORARY" was executed and table was dropped
+ temporary_table_was_dropped == 1
+ - "DROP TEMPORARY IF EXISTS" was specified but no temporary table
+ existed
+ temporary_table_was_dropped == 0
*/
if (!dont_log_query && table_creation_was_logged)
{
- /*
- If there is an error, we don't know the type of the engine
- at this point. So, we keep it in the trx-cache.
- */
- is_trans= error ? TRUE : is_trans;
if (is_trans)
trans_tmp_table_deleted= TRUE;
else
@@ -2373,7 +2377,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
append_identifier(thd, built_ptr_query, &db);
built_ptr_query->append(".");
}
- append_identifier(thd, built_ptr_query, &table->table_name);
+ append_identifier(thd, built_ptr_query, &table_name);
built_ptr_query->append(",");
}
/*
@@ -2381,80 +2385,73 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
is no need to proceed with the code that tries to drop a regular
table.
*/
- if (!error) continue;
+ if (temporary_table_was_dropped)
+ continue;
}
else if (!drop_temporary)
{
non_temp_tables_count++;
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db.str,
- table->table_name.str,
- MDL_SHARED));
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db.str,
+ table_name.str, MDL_SHARED));
- alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
+ alias= (lower_case_table_names == 2) ? table->alias : table_name;
/* remove .frm file and engine files */
- path_length= build_table_filename(path, sizeof(path) - 1, db.str, alias.str,
- reg_ext, 0);
+ path_length= build_table_filename(path, sizeof(path) - 1, db.str,
+ alias.str, reg_ext, 0);
+ path_end= path + path_length - reg_ext_length;
}
+
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
- error= 0;
- if (drop_temporary ||
- (ha_table_exists(thd, &db, &alias, &table_type, &is_sequence) == 0 &&
- table_type == 0) ||
- (!drop_view && (was_view= (table_type == view_pseudo_hton))) ||
- (drop_sequence && !is_sequence))
+ if (drop_temporary)
+ {
+ /* "DROP TEMPORARY" but a temporary table was not found */
+ unknown_tables.append(&db);
+ unknown_tables.append('.');
+ unknown_tables.append(&table_name);
+ unknown_tables.append(',');
+ error= ENOENT;
+ not_found_errors++;
+ continue;
+ }
+
+ {
+ char engine_buf[NAME_CHAR_LEN + 1];
+ LEX_CSTRING engine= { engine_buf, 0 };
+
+ table_type= dd_frm_type(thd, path, &engine);
+ if (table_type == TABLE_TYPE_NORMAL || table_type == TABLE_TYPE_SEQUENCE)
+ {
+ plugin_ref p= plugin_lock_by_name(thd, &engine,
+ MYSQL_STORAGE_ENGINE_PLUGIN);
+ hton= p ? plugin_hton(p) : NULL;
+ }
+ // note that for TABLE_TYPE_VIEW and TABLE_TYPE_UNKNOWN hton == NULL
+ }
+
+ was_view= table_type == TABLE_TYPE_VIEW;
+ if ((table_type == TABLE_TYPE_UNKNOWN) || (was_view && !drop_view) ||
+ (table_type != TABLE_TYPE_SEQUENCE && drop_sequence))
{
/*
One of the following cases happened:
- . "DROP TEMPORARY" but a temporary table was not found.
. "DROP" but table was not found
- . "DROP TABLE" statement, but it's a view.
+ . "DROP TABLE" statement, but it's a view.
. "DROP SEQUENCE", but it's not a sequence
*/
- was_table= drop_sequence && table_type;
- if (if_exists)
- {
- char buff[FN_REFLEN];
- int err= (drop_sequence ? ER_UNKNOWN_SEQUENCES :
- ER_BAD_TABLE_ERROR);
- 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,
- err, ER_THD(thd, err),
- tbl_name.c_ptr_safe());
-
- /*
- Our job is done here. This statement was added to avoid executing
- unnecessary code farther below which in some strange corner cases
- caused the server to crash (see MDEV-17896).
- */
- goto log_query;
- }
- else
- {
- non_tmp_error = (drop_temporary ? non_tmp_error : TRUE);
- error= 1;
- }
+ wrong_drop_sequence= drop_sequence && hton;
+ was_table|= wrong_drop_sequence;
+ local_non_tmp_error= 1;
+ error= table_type == TABLE_TYPE_UNKNOWN ? ENOENT : -1;
+ tdc_remove_table(thd, db.str, table_name.str);
}
else
{
- char *end;
- int frm_delete_error= 0;
- /*
- 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 (table_type && table_type != view_pseudo_hton)
- ha_lock_engine(thd, table_type);
+ if (WSREP(thd) && hton && !wsrep_should_replicate_ddl(thd, hton->db_type))
+ {
+ error= 1;
+ goto err;
+ }
if (thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
@@ -2464,25 +2461,32 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
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;
}
else
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db.str, table->table_name.str,
- false);
+ tdc_remove_table(thd, db.str, table_name.str);
/* 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.str,
- table->table_name.str,
- MDL_EXCLUSIVE));
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db.str,
+ table_name.str, MDL_EXCLUSIVE));
// Remove extension for delete
- *(end= path + path_length - reg_ext_length)= '\0';
+ *path_end= '\0';
+
+ if (hton && hton->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE)
+ log_if_exists= 1;
- if ((error= ha_delete_table(thd, table_type, path, &db, &table->table_name,
- !dont_log_query)))
+ thd->replication_flags= 0;
+ bool enoent_warning= !dont_log_query && !(hton && hton->discover_table);
+ error= ha_delete_table(thd, hton, path, &db, &table_name, enoent_warning);
+
+ if (!error)
+ table_dropped= 1;
+ else if (error < 0)
+ error= 0; // Table didn't exists
+ else if (error)
{
if (thd->is_killed())
{
@@ -2490,79 +2494,164 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
goto err;
}
}
- else
+ /* This may be set by the storage engine in handler::delete_table() */
+ if (thd->replication_flags & OPTION_IF_EXISTS)
+ log_if_exists= 1;
+
+ /*
+ Delete the .frm file if we managed to delete the table from the
+ engine or the table didn't exists in the engine
+ */
+ strmov(path_end, reg_ext);
+ if ((likely(!error) || non_existing_table_error(error)) &&
+ !access(path, F_OK))
{
+ int frm_delete_error= 0;
/* Delete the table definition file */
- strmov(end,reg_ext);
- if (table_type && table_type != view_pseudo_hton &&
- table_type->discover_table)
+ if (hton && (hton->discover_table || error))
{
/*
- Table type is using discovery and may not need a .frm file.
+ Table type is using discovery and may not need a .frm file
+ or the .frm file existed but no table in engine.
Delete it silently if it exists
*/
- (void) mysql_file_delete(key_file_frm, path, MYF(0));
+ if (mysql_file_delete(key_file_frm, path,
+ MYF(MY_WME | MY_IGNORE_ENOENT)))
+ frm_delete_error= my_errno;
}
else if (unlikely(mysql_file_delete(key_file_frm, path,
- MYF(MY_WME))))
+ !error ? MYF(MY_WME) :
+ MYF(MY_WME | MY_IGNORE_ENOENT))))
{
frm_delete_error= my_errno;
DBUG_ASSERT(frm_delete_error);
}
+
+ if (frm_delete_error)
+ {
+ /* Remember unexpected error from dropping the .frm file */
+ error= frm_delete_error;
+ }
+ else
+ {
+ error= 0; // We succeeded to delete the frm
+ table_dropped= 1;
+ }
}
+ local_non_tmp_error|= MY_TEST(error);
+ }
- if (likely(!error))
+ /*
+ If there was no .frm file and the table is not temporary,
+ scan all engines try to drop the table from there.
+ This is to ensure we don't have any partial table files left.
+ */
+ if (non_existing_table_error(error) && !wrong_drop_sequence)
+ {
+ int ferror= 0;
+ DBUG_ASSERT(!was_view);
+
+ /* Remove extension for delete */
+ *path_end= '\0';
+ ferror= ha_delete_table_force(thd, path, &db, &table_name);
+ if (!ferror)
{
- int trigger_drop_error= 0;
+ /* Table existed and was deleted */
+ local_non_tmp_error= 0;
+ table_dropped= 1;
+ error= 0;
+ }
+ if (ferror <= 0)
+ {
+ ferror= 0; // Ignore table not found
- if (likely(!frm_delete_error))
- {
- non_tmp_table_deleted= TRUE;
- trigger_drop_error=
- Table_triggers_list::drop_all_triggers(thd, &db, &table->table_name);
- }
+ /* Delete the frm file again (just in case it was rediscovered) */
+ strmov(path_end, reg_ext);
+ if (mysql_file_delete(key_file_frm, path, MYF(MY_WME|MY_IGNORE_ENOENT)))
+ ferror= my_errno;
+ }
+ if (!error)
+ error= ferror;
+ }
+
+ if (likely(!error) || non_existing_table_error(error))
+ {
+ if (Table_triggers_list::drop_all_triggers(thd, &db, &table_name,
+ MYF(MY_WME | MY_IGNORE_ENOENT)))
+ error= error ? error : -1;
+ }
+
+ if (error)
+ {
+ StringBuffer<FN_REFLEN> tbl_name(system_charset_info);
+ uint is_note= (if_exists && (was_view || wrong_drop_sequence) ?
+ ME_NOTE : 0);
+
+ tbl_name.length(0);
+ tbl_name.append(&db);
+ tbl_name.append('.');
+ tbl_name.append(&table_name);
- if (unlikely(trigger_drop_error) ||
- (frm_delete_error && frm_delete_error != ENOENT))
+ if (!non_existing_table_error(error) || is_note)
+ {
+ /*
+ Error from engine already given. Here we only have to take
+ care about errors for trying to drop view or sequence
+ */
+ if (was_view)
+ my_error(ER_IT_IS_A_VIEW, MYF(is_note), tbl_name.c_ptr_safe());
+ else if (wrong_drop_sequence)
+ my_error(ER_NOT_SEQUENCE2, MYF(is_note), tbl_name.c_ptr_safe());
+ if (is_note)
+ error= ENOENT;
+ }
+ else
+ {
+ not_found_errors++;
+ if (unknown_tables.append(tbl_name) || unknown_tables.append(','))
+ {
error= 1;
- else if (frm_delete_error && if_exists)
- thd->clear_error();
+ goto err;
+ }
}
- non_tmp_error|= MY_TEST(error);
}
- if (error)
+ /*
+ Don't give an error if we are using IF EXISTS for a table that
+ didn't exists
+ */
+ if (if_exists && non_existing_table_error(error))
{
- if (wrong_tables.length())
- wrong_tables.append(',');
- wrong_tables.append(&db);
- wrong_tables.append('.');
- wrong_tables.append(&table->table_name);
- errors++;
+ error= 0;
+ local_non_tmp_error= 0;
}
- else
+
+ non_tmp_error|= local_non_tmp_error;
+
+ if (!error && table_dropped)
{
- PSI_CALL_drop_table_share(false, table->db.str, (uint)table->db.length,
- table->table_name.str, (uint)table->table_name.length);
+ PSI_CALL_drop_table_share(temporary_table_was_dropped,
+ db.str, (uint)db.length,
+ table_name.str, (uint)table_name.length);
mysql_audit_drop_table(thd, table);
}
-log_query:
- if (!dont_log_query && !drop_temporary)
+ if (!dont_log_query &&
+ (!error || table_dropped || non_existing_table_error(error)))
{
- non_tmp_table_deleted= (if_exists ? TRUE : non_tmp_table_deleted);
+ non_tmp_table_deleted|= (if_exists || table_dropped);
/*
Don't write the database name if it is the current one (or if
thd->db is NULL).
*/
if (thd->db.str == NULL || cmp(&db, &thd->db) != 0)
{
- append_identifier(thd, &built_query, &db);
- built_query.append(".");
+ append_identifier(thd, &normal_tables, &db);
+ normal_tables.append(".");
}
- append_identifier(thd, &built_query, &table->table_name);
- built_query.append(",");
+ append_identifier(thd, &normal_tables, &table_name);
+ normal_tables.append(",");
}
DBUG_PRINT("table", ("table: %p s: %p", table->table,
table->table ? table->table->s : NULL));
@@ -2570,20 +2659,16 @@ log_query:
DEBUG_SYNC(thd, "rm_table_no_locks_before_binlog");
thd->thread_specific_used= TRUE;
error= 0;
+
err:
- if (wrong_tables.length())
- {
- DBUG_ASSERT(errors);
- if (errors == 1 && was_view)
- my_error(ER_IT_IS_A_VIEW, MYF(0), wrong_tables.c_ptr_safe());
- else if (errors == 1 && drop_sequence && was_table)
- my_error(ER_NOT_SEQUENCE2, MYF(0), wrong_tables.c_ptr_safe());
- else if (errors > 1 || !thd->is_error())
- my_error((drop_sequence ? ER_UNKNOWN_SEQUENCES :
- ER_BAD_TABLE_ERROR),
- MYF(0), wrong_tables.c_ptr_safe());
- error= 1;
+ if (unknown_tables.length() > 1)
+ {
+ uint is_note= if_exists ? ME_NOTE : 0;
+ unknown_tables.chop();
+ my_error((drop_sequence ? ER_UNKNOWN_SEQUENCES : ER_BAD_TABLE_ERROR),
+ MYF(is_note), unknown_tables.c_ptr_safe());
}
+ error= thd->is_error();
/*
We are always logging drop of temporary tables.
@@ -2601,7 +2686,7 @@ err:
trans_tmp_table_deleted || non_tmp_table_deleted)
{
if (non_trans_tmp_table_deleted || trans_tmp_table_deleted)
- thd->transaction.stmt.mark_dropped_temp_table();
+ thd->transaction->stmt.mark_dropped_temp_table();
query_cache_invalidate3(thd, tables, 0);
if (!dont_log_query && mysql_bin_log.is_open())
@@ -2632,16 +2717,35 @@ err:
}
if (non_tmp_table_deleted)
{
- /* Chop of the last comma */
- built_query.chop();
- built_query.append(" /* generated by server */");
- 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(),
- TRUE, FALSE, FALSE,
- error_code) > 0);
+ String built_query;
+ const char *comment_start;
+ uint32 comment_len;
+
+ built_query.set_charset(thd->charset());
+ built_query.append("DROP ");
+ built_query.append(object_to_drop);
+ built_query.append(' ');
+ if (log_if_exists)
+ built_query.append("IF EXISTS ");
+
+ /* Preserve comment in original query */
+ if ((comment_len= comment_length(thd, if_exists ? 17:9,
+ &comment_start)))
+ {
+ built_query.append(comment_start, comment_len);
+ built_query.append(" ");
+ }
+
+ /* Chop of the last comma */
+ normal_tables.chop();
+ built_query.append(normal_tables.ptr(), normal_tables.length());
+ built_query.append(" /* generated by server */");
+ 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(),
+ TRUE, FALSE, FALSE,
+ error_code) > 0);
}
}
}
@@ -2661,7 +2765,8 @@ err:
if (thd->lock && thd->lock->table_count == 0 &&
non_temp_tables_count > 0 && !dont_free_locks)
{
- thd->locked_tables_list.unlock_locked_tables(thd);
+ if (thd->locked_tables_list.unlock_locked_tables(thd))
+ error= 1;
goto end;
}
for (table= tables; table; table= table->next_local)
@@ -2685,9 +2790,10 @@ err:
}
end:
- DBUG_RETURN(error);
+ DBUG_RETURN(error || thd->is_error());
}
+
/**
Log the drop of a table.
@@ -2722,6 +2828,12 @@ bool log_drop_table(THD *thd, const LEX_CSTRING *db_name,
append_identifier(thd, &query, table_name);
query.append(STRING_WITH_LEN("/* Generated to handle "
"failed CREATE OR REPLACE */"));
+
+ /*
+ In case of temporary tables we don't have to log the database name
+ in the binary log. We log this for non temporary tables, as the slave
+ may use a filter to ignore queries for a specific database.
+ */
error= thd->binlog_query(THD::STMT_QUERY_TYPE,
query.ptr(), query.length(),
FALSE, FALSE, temporary_table, 0) > 0;
@@ -2730,7 +2842,7 @@ bool log_drop_table(THD *thd, const LEX_CSTRING *db_name,
/**
- Quickly remove a table.
+ Quickly remove a table without bin logging
@param thd Thread context.
@param base The handlerton handle.
@@ -2743,7 +2855,8 @@ bool log_drop_table(THD *thd, const LEX_CSTRING *db_name,
*/
bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
- const LEX_CSTRING *table_name, uint flags, const char *table_path)
+ const LEX_CSTRING *table_name, uint flags,
+ const char *table_path)
{
char path[FN_REFLEN + 1];
int error= 0;
@@ -2751,11 +2864,13 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
size_t path_length= table_path ?
(strxnmov(path, sizeof(path) - 1, table_path, reg_ext, NullS) - path) :
- build_table_filename(path, sizeof(path)-1, db->str, table_name->str, reg_ext, flags);
- if (mysql_file_delete(key_file_frm, path, MYF(0)))
- error= 1; /* purecov: inspected */
+ build_table_filename(path, sizeof(path)-1, db->str, table_name->str,
+ reg_ext, flags);
+ if (!(flags & NO_FRM_RENAME))
+ 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 & NO_HA_TABLE)
+ if ((flags & (NO_HA_TABLE | NO_PAR_TABLE)) == NO_HA_TABLE)
{
handler *file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base);
if (!file)
@@ -2764,7 +2879,8 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
delete file;
}
if (!(flags & (FRM_ONLY|NO_HA_TABLE)))
- error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
+ if (ha_delete_table(thd, base, path, db, table_name, 0) > 0)
+ error= 1;
if (likely(error == 0))
{
@@ -2861,7 +2977,7 @@ static int sort_keys(KEY *a, KEY *b)
*/
bool check_duplicates_in_interval(const char *set_or_name,
- const char *name, TYPELIB *typelib,
+ const char *name, const TYPELIB *typelib,
CHARSET_INFO *cs, unsigned int *dup_val_count)
{
TYPELIB tmp= *typelib;
@@ -2925,12 +3041,11 @@ bool Column_definition::prepare_stage2_typelib(const char *type_name,
}
-uint Column_definition::pack_flag_numeric(uint dec) const
+uint Column_definition::pack_flag_numeric() const
{
return (FIELDFLAG_NUMBER |
(flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) |
- (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) |
- (dec << FIELDFLAG_DEC_SHIFT));
+ (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0));
}
@@ -3226,8 +3341,7 @@ bool Column_definition::prepare_stage1_check_typelib_default()
}
else /* MYSQL_TYPE_ENUM */
{
- def->length(charset->cset->lengthsp(charset,
- def->ptr(), def->length()));
+ def->length(charset->lengthsp(def->ptr(), def->length()));
not_found= !find_type2(interval, def->ptr(), def->length(), charset);
}
}
@@ -3333,7 +3447,7 @@ static Create_field * add_hash_field(THD * thd, List<Create_field> *create_list,
}
}
cf->field_name= field_name;
- cf->set_handler(&type_handler_longlong);
+ cf->set_handler(&type_handler_slonglong);
key_info->algorithm= HA_KEY_ALG_LONG_HASH;
create_list->push_back(cf,thd->mem_root);
return cf;
@@ -3343,14 +3457,70 @@ Key *
mysql_add_invisible_index(THD *thd, List<Key> *key_list,
LEX_CSTRING* field_name, enum Key::Keytype type)
{
- Key *key= NULL;
- key= new (thd->mem_root) Key(type, &null_clex_str, HA_KEY_ALG_UNDEF,
- false, DDL_options(DDL_options::OPT_NONE));
+ Key *key= new (thd->mem_root) Key(type, &null_clex_str, HA_KEY_ALG_UNDEF,
+ false, DDL_options(DDL_options::OPT_NONE));
key->columns.push_back(new(thd->mem_root) Key_part_spec(field_name, 0, true),
thd->mem_root);
key_list->push_back(key, thd->mem_root);
return key;
}
+
+
+bool Type_handler_string::Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def)
+ const
+{
+ /*
+ Set length to 0. It's set to the real column width later for CHAR.
+ It has to be the correct col width for CHAR, as its data are not
+ prefixed with length (unlike blobs).
+ */
+ part->length= 0;
+ return !Charset(def.charset).is_good_for_ft();
+}
+
+
+bool Type_handler_varchar::Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def)
+ const
+{
+ part->length= 0;
+ return !Charset(def.charset).is_good_for_ft();
+}
+
+
+bool
+Type_handler_blob_common::Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def)
+ const
+{
+ /*
+ Set keyseg length to 1 for blobs.
+ It's ignored in ft code: the data length is taken from the length prefix.
+ */
+ part->length= 1;
+ return !Charset(def.charset).is_good_for_ft();
+}
+
+
+static bool
+key_add_part_check_null(const handler *file, KEY *key_info,
+ const Column_definition *sql_field,
+ const Key_part_spec *column)
+{
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ {
+ key_info->flags|= HA_NULL_PART_KEY;
+ if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
+ {
+ my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
+ return true;
+ }
+ }
+ return false;
+}
+
+
/*
Preparation for table creation
@@ -3401,12 +3571,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_EXECUTE_IF("test_pseudo_invisible",{
mysql_add_invisible_field(thd, &alter_info->create_list,
- "invisible", &type_handler_long, INVISIBLE_SYSTEM,
+ "invisible", &type_handler_slong, INVISIBLE_SYSTEM,
new (thd->mem_root)Item_int(thd, 9));
});
DBUG_EXECUTE_IF("test_completely_invisible",{
mysql_add_invisible_field(thd, &alter_info->create_list,
- "invisible", &type_handler_long, INVISIBLE_FULL,
+ "invisible", &type_handler_slong, INVISIBLE_FULL,
new (thd->mem_root)Item_int(thd, 9));
});
DBUG_EXECUTE_IF("test_invisible_index",{
@@ -3419,11 +3589,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
LEX_CSTRING* connect_string = &create_info->connect_string;
if (connect_string->length != 0 &&
connect_string->length > CONNECT_STRING_MAXLEN &&
- (system_charset_info->cset->charpos(system_charset_info,
- connect_string->str,
- (connect_string->str +
- connect_string->length),
- CONNECT_STRING_MAXLEN)
+ (system_charset_info->charpos(connect_string->str,
+ (connect_string->str +
+ connect_string->length),
+ CONNECT_STRING_MAXLEN)
< connect_string->length))
{
my_error(ER_WRONG_STRING_LENGTH, MYF(0),
@@ -3723,6 +3892,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
for (; (key=key_iterator++) ; key_number++)
{
uint key_length=0;
+ Create_field *auto_increment_key= 0;
Key_part_spec *column;
is_hash_field_needed= false;
@@ -3894,136 +4064,93 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
cols2.rewind();
- if (key->type == Key::FULLTEXT)
- {
- if ((sql_field->real_field_type() != MYSQL_TYPE_STRING &&
- sql_field->real_field_type() != MYSQL_TYPE_VARCHAR &&
- !f_is_blob(sql_field->pack_flag)) ||
- sql_field->charset == &my_charset_bin ||
- sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
- (ft_key_charset && sql_field->charset != ft_key_charset))
- {
- my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
- DBUG_RETURN(-1);
- }
- ft_key_charset=sql_field->charset;
- /*
- for fulltext keys keyseg length is 1 for blobs (it's ignored in ft
- code anyway, and 0 (set to column width later) for char's. it has
- to be correct col width for char's, as char data are not prefixed
- with length (unlike blobs, where ft code takes data length from a
- data prefix, ignoring column->length).
- */
- column->length= MY_TEST(f_is_blob(sql_field->pack_flag));
- }
- else
- {
- column->length*= sql_field->charset->mbmaxlen;
+ switch(key->type) {
- if (key->type == Key::SPATIAL)
+ case Key::FULLTEXT:
+ if (sql_field->type_handler()->Key_part_spec_init_ft(column,
+ *sql_field) ||
+ (ft_key_charset && sql_field->charset != ft_key_charset))
{
- if (column->length)
- {
- my_error(ER_WRONG_SUB_KEY, MYF(0));
- DBUG_RETURN(TRUE);
- }
- if (!f_is_geom(sql_field->pack_flag))
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
- DBUG_RETURN(TRUE);
- }
+ my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
+ DBUG_RETURN(-1);
}
+ ft_key_charset= sql_field->charset;
+ break;
- if (f_is_blob(sql_field->pack_flag) ||
- (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL))
+ case Key::SPATIAL:
+ if (sql_field->type_handler()->Key_part_spec_init_spatial(column,
+ *sql_field) ||
+ sql_field->check_vcol_for_key(thd))
+ DBUG_RETURN(TRUE);
+ if (!(sql_field->flags & NOT_NULL_FLAG))
{
- if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
- {
- 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 ==
- Field::GEOM_POINT)
- column->length= MAX_LEN_GEOM_POINT_FIELD;
- if (!column->length)
- {
- if (key->type == Key::UNIQUE)
- is_hash_field_needed= true;
- else if (key->type == Key::MULTIPLE)
- column->length= file->max_key_length() + 1;
- else
- {
- my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
- DBUG_RETURN(TRUE);
- }
- }
+ my_message(ER_SPATIAL_CANT_HAVE_NULL,
+ ER_THD(thd, ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
+ DBUG_RETURN(TRUE);
}
-#ifdef HAVE_SPATIAL
- if (key->type == Key::SPATIAL)
- {
- if (!column->length)
- {
- /*
- 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
- Lately we'll extend this code to support more dimensions
- */
- column->length= 4*sizeof(double);
- }
- }
-#endif
+ break;
+
+ case Key::PRIMARY:
if (sql_field->vcol_info)
{
- if (key->type == Key::PRIMARY)
- {
- my_error(ER_PRIMARY_KEY_BASED_ON_GENERATED_COLUMN, MYF(0));
- DBUG_RETURN(TRUE);
- }
- if (sql_field->vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC)
- {
- /* use check_expression() to report an error */
- check_expression(sql_field->vcol_info, &sql_field->field_name,
- VCOL_GENERATED_STORED);
- DBUG_ASSERT(thd->is_error());
- DBUG_RETURN(TRUE);
- }
+ my_error(ER_PRIMARY_KEY_BASED_ON_GENERATED_COLUMN, MYF(0));
+ DBUG_RETURN(TRUE);
}
- if (!(sql_field->flags & NOT_NULL_FLAG))
- {
- if (key->type == Key::PRIMARY)
- {
- /* Implicitly set primary key fields to NOT NULL for ISO conf. */
- sql_field->flags|= NOT_NULL_FLAG;
- sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
- null_fields--;
- }
- else
- {
- key_info->flags|= HA_NULL_PART_KEY;
- if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
- {
- my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
- DBUG_RETURN(TRUE);
- }
- if (key->type == Key::SPATIAL)
- {
- my_message(ER_SPATIAL_CANT_HAVE_NULL,
- ER_THD(thd, ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
- DBUG_RETURN(TRUE);
- }
- }
- }
- if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
- {
- if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
- auto_increment--; // Field is used
- }
+ if (sql_field->type_handler()->Key_part_spec_init_primary(column,
+ *sql_field,
+ file))
+ DBUG_RETURN(TRUE);
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ {
+ /* Implicitly set primary key fields to NOT NULL for ISO conf. */
+ sql_field->flags|= NOT_NULL_FLAG;
+ sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL;
+ null_fields--;
+ }
+ break;
+
+ case Key::MULTIPLE:
+ if (sql_field->type_handler()->Key_part_spec_init_multiple(column,
+ *sql_field,
+ file) ||
+ sql_field->check_vcol_for_key(thd) ||
+ key_add_part_check_null(file, key_info, sql_field, column))
+ DBUG_RETURN(TRUE);
+ break;
+
+ case Key::FOREIGN_KEY:
+ if (sql_field->type_handler()->Key_part_spec_init_foreign(column,
+ *sql_field,
+ file) ||
+ sql_field->check_vcol_for_key(thd) ||
+ key_add_part_check_null(file, key_info, sql_field, column))
+ DBUG_RETURN(TRUE);
+ break;
+
+ case Key::UNIQUE:
+ if (sql_field->type_handler()->Key_part_spec_init_unique(column,
+ *sql_field, file,
+ &is_hash_field_needed) ||
+ sql_field->check_vcol_for_key(thd) ||
+ key_add_part_check_null(file, key_info, sql_field, column))
+ DBUG_RETURN(TRUE);
+ break;
+ }
+
+ if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
+ {
+ DBUG_ASSERT(key->type != Key::FULLTEXT);
+ DBUG_ASSERT(key->type != Key::SPATIAL);
+ if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY))
+ auto_increment--; // Field is used
+ auto_increment_key= sql_field;
}
key_part_info->fieldnr= field;
key_part_info->offset= (uint16) sql_field->offset;
key_part_info->key_type=sql_field->pack_flag;
- uint key_part_length= sql_field->key_length;
+ uint key_part_length= sql_field->type_handler()->
+ calc_key_length(*sql_field);
if (column->length)
{
@@ -4105,6 +4232,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
}
+
/* We can not store key_part_length more then 2^16 - 1 in frm */
if (is_hash_field_needed && column->length > UINT_MAX16)
{
@@ -4116,19 +4244,14 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
/* Use packed keys for long strings on the first column */
if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) &&
!((create_info->table_options & HA_OPTION_NO_PACK_KEYS)) &&
- (key_part_length >= KEY_DEFAULT_PACK_LENGTH &&
- (sql_field->real_field_type() == MYSQL_TYPE_STRING ||
- sql_field->real_field_type() == MYSQL_TYPE_VARCHAR ||
- f_is_blob(sql_field->pack_flag))) && !is_hash_field_needed)
- {
- if ((column_nr == 0 && f_is_blob(sql_field->pack_flag)) ||
- sql_field->real_field_type() == MYSQL_TYPE_VARCHAR)
- key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
- else
- key_info->flags|= HA_PACK_KEY;
+ (key_part_length >= KEY_DEFAULT_PACK_LENGTH) &&
+ !is_hash_field_needed)
+ {
+ key_info->flags|= sql_field->type_handler()->KEY_pack_flags(column_nr);
}
/* Check if the key segment is partial, set the key flag accordingly */
- if (key_part_length != sql_field->key_length &&
+ if (key_part_length != sql_field->type_handler()->
+ calc_key_length(*sql_field) &&
key_part_length != sql_field->type_handler()->max_octet_length())
key_info->flags|= HA_KEY_HAS_PART_KEY_SEG;
@@ -4178,11 +4301,22 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(TRUE);
}
- if (is_hash_field_needed && key_info->algorithm != HA_KEY_ALG_UNDEF &&
- key_info->algorithm != HA_KEY_ALG_HASH )
+ /* Check long unique keys */
+ if (is_hash_field_needed)
{
- my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length);
- DBUG_RETURN(TRUE);
+ if (auto_increment_key)
+ {
+ my_error(ER_NO_AUTOINCREMENT_WITH_UNIQUE, MYF(0),
+ sql_field->field_name.str,
+ key_info->name.str);
+ DBUG_RETURN(TRUE);
+ }
+ if (key_info->algorithm != HA_KEY_ALG_UNDEF &&
+ key_info->algorithm != HA_KEY_ALG_HASH )
+ {
+ my_error(ER_TOO_LONG_KEY, MYF(0), max_key_length);
+ DBUG_RETURN(TRUE);
+ }
}
if (is_hash_field_needed ||
(key_info->algorithm == HA_KEY_ALG_HASH &&
@@ -4221,6 +4355,42 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
// Check if a duplicate index is defined.
check_duplicate_key(thd, key, key_info, &alter_info->key_list);
+
+ key_info->without_overlaps= key->without_overlaps;
+ if (key_info->without_overlaps)
+ {
+ if (key_info->algorithm == HA_KEY_ALG_HASH ||
+ key_info->algorithm == HA_KEY_ALG_LONG_HASH)
+
+ {
+without_overlaps_err:
+ my_error(ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS, MYF(0), key_info->name.str);
+ DBUG_RETURN(true);
+ }
+ key_iterator2.rewind();
+ while ((key2 = key_iterator2++))
+ {
+ if (key2->type != Key::FOREIGN_KEY)
+ continue;
+ DBUG_ASSERT(key != key2);
+ Foreign_key *fk= (Foreign_key*) key2;
+ if (fk->update_opt != FK_OPTION_CASCADE)
+ continue;
+ for (Key_part_spec& kp: key->columns)
+ {
+ for (Key_part_spec& kp2: fk->columns)
+ {
+ if (!lex_string_cmp(system_charset_info, &kp.field_name,
+ &kp2.field_name))
+ {
+ goto without_overlaps_err;
+ }
+ }
+ }
+ }
+ create_info->period_info.unique_keys++;
+ }
+
key_info++;
}
@@ -4306,7 +4476,11 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
while ((check= c_it++))
{
if (!check->name.length || check->automatic_name)
+ {
+ if (check_expression(check, &check->name, VCOL_CHECK_TABLE, alter_info))
+ DBUG_RETURN(TRUE);
continue;
+ }
{
/* Check that there's no repeating table CHECK constraint names. */
@@ -4380,8 +4554,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
/**
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
+ If comment length is more than the standard length
+ truncate it and store the comment length upto the standard
comment length size
@param thd Thread handle
@@ -4405,7 +4579,6 @@ bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len,
Well_formed_prefix(system_charset_info, *comment, max_len).length();
if (tmp_len < comment->length)
{
-#if MARIADB_VERSION_ID < 100500
if (comment->length <= max_len)
{
if (thd->is_strict_mode())
@@ -4421,9 +4594,6 @@ bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len,
comment->length= tmp_len;
DBUG_RETURN(false);
}
-#else
-#error do it in TEXT_STRING_sys
-#endif
if (thd->is_strict_mode())
{
my_error(err_code, MYF(0), name, static_cast<ulong>(max_len));
@@ -4517,7 +4687,7 @@ bool Column_definition::prepare_blob_field(THD *thd)
set_handler(Type_handler::blob_type_handler((uint) length));
pack_length= type_handler()->calc_pack_length(0);
}
- length= key_length= 0;
+ length= 0;
}
DBUG_RETURN(0);
}
@@ -4545,43 +4715,69 @@ bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root)
}
-static bool vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info,
- Alter_info *alter_info, KEY **key_info, uint key_count)
+static bool append_system_key_parts(THD *thd, HA_CREATE_INFO *create_info,
+ Alter_info *alter_info, KEY **key_info,
+ uint key_count)
{
- DBUG_ASSERT(create_info->versioned());
-
- const char *row_start_field= create_info->vers_info.as_row.start;
- DBUG_ASSERT(row_start_field);
- const char *row_end_field= create_info->vers_info.as_row.end;
- DBUG_ASSERT(row_end_field);
+ const Lex_ident &row_start_field= create_info->vers_info.as_row.start;
+ const Lex_ident &row_end_field= create_info->vers_info.as_row.end;
+ DBUG_ASSERT(!create_info->versioned() || (row_start_field && row_end_field));
List_iterator<Key> key_it(alter_info->key_list);
Key *key= NULL;
- while ((key=key_it++))
- {
- if (key->type != Key::PRIMARY && key->type != Key::UNIQUE)
- continue;
- Key_part_spec *key_part= NULL;
- List_iterator<Key_part_spec> part_it(key->columns);
- while ((key_part=part_it++))
+ if (create_info->versioned())
+ {
+ while ((key=key_it++))
{
- if (!my_strcasecmp(system_charset_info,
- row_start_field,
- key_part->field_name.str) ||
+ if (key->type != Key::PRIMARY && key->type != Key::UNIQUE)
+ continue;
- !my_strcasecmp(system_charset_info,
- row_end_field,
- key_part->field_name.str))
- break;
+ Key_part_spec *key_part=NULL;
+ List_iterator<Key_part_spec> part_it(key->columns);
+ while ((key_part=part_it++))
+ {
+ if (row_start_field.streq(key_part->field_name) ||
+ row_end_field.streq(key_part->field_name))
+ break;
+ }
+ if (!key_part)
+ key->columns.push_back(new (thd->mem_root)
+ Key_part_spec(&row_end_field, 0, true));
}
- if (key_part)
- continue; // Key already contains Sys_start or Sys_end
+ key_it.rewind();
+ }
- Key_part_spec *row_end=
- new (thd->mem_root) Key_part_spec(&create_info->vers_info.as_row.end, 0,
- true);
- key->columns.push_back(row_end);
+ while ((key=key_it++))
+ {
+ if (key->without_overlaps)
+ {
+ DBUG_ASSERT(key->type == Key::PRIMARY || key->type == Key::UNIQUE);
+ if (!create_info->period_info.is_set()
+ || !key->period.streq(create_info->period_info.name))
+ {
+ my_error(ER_PERIOD_NOT_FOUND, MYF(0), key->period.str);
+ return true;
+ }
+
+ const auto &period_start= create_info->period_info.period.start;
+ const auto &period_end= create_info->period_info.period.end;
+ List_iterator<Key_part_spec> part_it(key->columns);
+ while (Key_part_spec *key_part= part_it++)
+ {
+ if (period_start.streq(key_part->field_name)
+ || period_end.streq(key_part->field_name))
+ {
+ my_error(ER_KEY_CONTAINS_PERIOD_FIELDS, MYF(0), key->name.str,
+ key_part->field_name);
+ return true;
+ }
+ }
+ key->columns.push_back(new (thd->mem_root)
+ Key_part_spec(&period_end, 0));
+ key->columns.push_back(new (thd->mem_root)
+ Key_part_spec(&period_start, 0));
+ }
}
return false;
@@ -4688,7 +4884,7 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db,
if (create_info->tmp_table())
{
- my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
+ my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0), "CREATE TEMPORARY TABLE");
goto err;
}
if ((part_engine_type == partition_hton) &&
@@ -4814,19 +5010,17 @@ handler *mysql_create_frm_image(THD *thd, const LEX_CSTRING &db,
{
if (key->type == Key::FOREIGN_KEY)
{
- my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
+ my_error(ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING, MYF(0),
+ "FOREIGN KEY");
goto err;
}
}
}
#endif
- if (create_info->versioned())
- {
- if(vers_prepare_keys(thd, create_info, alter_info, key_info,
- *key_count))
- goto err;
- }
+ if (append_system_key_parts(thd, create_info, alter_info, key_info,
+ *key_count))
+ goto err;
if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options,
file, key_info, key_count, create_table_mode))
@@ -4894,6 +5088,7 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db,
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;
+ handlerton *exists_hton;
DBUG_ENTER("mysql_create_table_no_lock");
DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s",
db.str, table_name.str, internal_tmp_table, path));
@@ -4935,7 +5130,8 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db,
If a table exists, it must have been pre-opened. Try looking for one
in-use in THD::all_temp_tables list of TABLE_SHAREs.
*/
- TABLE *tmp_table= thd->find_temporary_table(db.str, table_name.str);
+ TABLE *tmp_table= thd->find_temporary_table(db.str, table_name.str,
+ THD::TMP_TABLE_ANY);
if (tmp_table)
{
@@ -4970,8 +5166,27 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db,
}
else
{
- if (!internal_tmp_table && ha_table_exists(thd, &db, &table_name))
+ if (ha_check_if_updates_are_ignored(thd, create_info->db_type, "CREATE"))
{
+ /*
+ Don't create table. CREATE will still be logged in binary log
+ This can happen for shared storage engines that supports
+ ENGINE= in the create statement (Note that S3 doesn't support this.
+ */
+ error= 0;
+ goto err;
+ }
+
+ if (!internal_tmp_table && ha_table_exists(thd, &db, &table_name,
+ &exists_hton))
+ {
+ if (ha_check_if_updates_are_ignored(thd, exists_hton, "CREATE"))
+ {
+ /* Don't create table. CREATE will still be logged in binary log */
+ error= 0;
+ goto err;
+ }
+
if (options.or_replace())
{
(void) delete_statistics_for_table(thd, &db, &table_name);
@@ -4999,7 +5214,8 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db,
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->set_killed(KILL_QUERY); );
+ DBUG_EXECUTE_IF("send_kill_after_delete",
+ thd->set_killed(KILL_QUERY); );
/*
Restart statement transactions for the case of CREATE ... SELECT.
@@ -5009,7 +5225,20 @@ int create_table_impl(THD *thd, const LEX_CSTRING &orig_db,
goto err;
}
else if (options.if_not_exists())
+ {
+ /*
+ We never come here as part of normal create table as table existance
+ is checked in open_and_lock_tables(). We may come here as part of
+ ALTER TABLE when converting a table for a distributed engine to a
+ a local one.
+ */
+
+ /* Log CREATE IF NOT EXISTS on slave for distributed engines */
+ if (thd->slave_thread && (exists_hton && exists_hton->flags &
+ HTON_IGNORE_UPDATES))
+ thd->log_current_statement= 1;
goto warn;
+ }
else
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name.str);
@@ -5240,12 +5469,21 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
thd->lex->create_info.options|= create_info->options;
/* Open or obtain an exclusive metadata lock on table being created */
+ create_table->db_type= 0;
result= open_and_lock_tables(thd, *create_info, create_table, FALSE, 0);
thd->lex->create_info.options= save_thd_create_info_options;
if (result)
{
+ if (thd->slave_thread &&
+ !thd->is_error() && create_table->db_type &&
+ (create_table->db_type->flags & HTON_IGNORE_UPDATES))
+ {
+ /* Table existed in distributed engine. Log query to binary log */
+ result= 0;
+ goto err;
+ }
/* is_error() may be 0 if table existed and we generated a warning */
DBUG_RETURN(thd->is_error());
}
@@ -5313,7 +5551,7 @@ err:
}
if (create_info->tmp_table())
- thd->transaction.stmt.mark_created_temp_table();
+ thd->transaction->stmt.mark_created_temp_table();
/* Write log if no error or if we already deleted a table */
if (likely(!result) || thd->log_current_statement)
@@ -5325,7 +5563,7 @@ err:
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);
+ (void) thd->locked_tables_list.unlock_locked_table(thd, mdl_ticket);
}
else if (likely(!result) && create_info->table)
{
@@ -5433,7 +5671,7 @@ static bool make_unique_constraint_name(THD *thd, LEX_CSTRING *name,
if (!check) // Found unique name
{
name->length= (size_t) (real_end - buff);
- name->str= thd->strmake(buff, name->length);
+ name->str= strmake_root(thd->stmt_arena->mem_root, buff, name->length);
return (name->str == NULL);
}
}
@@ -5517,7 +5755,8 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
DBUG_ENTER("mysql_rename_table");
DBUG_ASSERT(base);
DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
- old_db->str, old_name->str, new_db->str, new_name->str));
+ old_db->str, old_name->str, new_db->str,
+ new_name->str));
// Temporarily disable foreign key checks
if (flags & NO_FK_CHECKS)
@@ -5527,8 +5766,8 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
build_table_filename(from, sizeof(from) - 1, old_db->str, old_name->str, "",
flags & FN_FROM_IS_TMP);
- length= build_table_filename(to, sizeof(to) - 1, new_db->str, new_name->str, "",
- flags & FN_TO_IS_TMP);
+ length= build_table_filename(to, sizeof(to) - 1, new_db->str,
+ new_name->str, "", flags & FN_TO_IS_TMP);
// Check if we hit FN_REFLEN bytes along with file extension.
if (length+reg_ext_length > FN_REFLEN)
{
@@ -5567,7 +5806,8 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
{
if (rename_file_ext(from,to,reg_ext))
error= my_errno;
- (void) file->ha_create_partitioning_metadata(to, from, CHF_RENAME_FLAG);
+ if (!(flags & NO_PAR_TABLE))
+ (void) file->ha_create_partitioning_metadata(to, from, CHF_RENAME_FLAG);
}
else if (!file || likely(!(error=file->ha_rename_table(from_base, to_base))))
{
@@ -5591,7 +5831,6 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
my_error(ER_BAD_DB_ERROR, MYF(0), new_db->str);
else if (error)
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);
@@ -5639,6 +5878,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
int res= 1;
bool is_trans= FALSE;
bool do_logging= FALSE;
+ bool force_generated_create= false;
uint not_used;
int create_res;
DBUG_ENTER("mysql_create_like_table");
@@ -5694,6 +5934,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
local_create_info.init(create_info->create_like_options());
local_create_info.db_type= src_table->table->s->db_type();
local_create_info.row_type= src_table->table->s->row_type;
+ local_create_info.alter_info= &local_alter_info;
if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info,
&local_alter_info, &local_alter_ctx))
goto err;
@@ -5791,12 +6032,24 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
if (thd->is_current_stmt_binlog_disabled())
goto err;
- if (thd->is_current_stmt_binlog_format_row())
+#ifdef ENABLE_WHEN_S3_CAN_CREATE_TABLES
+ /*
+ If we do a create based on a shared table, log the full create of the
+ resulting table. This is needed as a shared table may look different
+ when the slave executes the command.
+ */
+ force_generated_create=
+ (((src_table->table->file->partition_ht()->flags &
+ HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE) &&
+ src_table->table->s->db_type() != local_create_info.db_type));
+#endif
+
+ if (thd->is_current_stmt_binlog_format_row() || force_generated_create)
{
/*
Since temporary tables are not replicated under row-based
replication, CREATE TABLE ... LIKE ... needs special
- treatement. We have four cases to consider, according to the
+ treatement. We have some cases to consider, according to the
following decision table:
==== ========= ========= ==============================
@@ -5807,11 +6060,14 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
was created.
3 temporary normal Nothing
4 temporary temporary Nothing
+ 5 any shared Generated statement if the table
+ was created if engine changed
==== ========= ========= ==============================
*/
- if (!(create_info->tmp_table()))
+ if (!(create_info->tmp_table()) || force_generated_create)
{
- if (src_table->table->s->tmp_table) // Case 2
+ // Case 2 & 5
+ if (src_table->table->s->tmp_table || force_generated_create)
{
char buf[2048];
String query(buf, sizeof(buf), system_charset_info);
@@ -5871,8 +6127,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
goto err;
/*
- As the reference table is temporary and may not exist on slave, we must
- force the ENGINE to be present into CREATE TABLE.
+ As the reference table is temporary and may not exist on slave, we
+ must force the ENGINE to be present into CREATE TABLE.
*/
create_info->used_fields|= HA_CREATE_USED_ENGINE;
@@ -5914,7 +6170,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
res, create_info->tmp_table(), local_create_info.table));
if (create_info->tmp_table())
{
- thd->transaction.stmt.mark_created_temp_table();
+ thd->transaction->stmt.mark_created_temp_table();
if (!res && local_create_info.table)
{
/*
@@ -6162,7 +6418,7 @@ drop_create_field:
}
}
- /* Handle ALTER COLUMN IF EXISTS SET/DROP DEFAULT. */
+ /* Handle ALTER/RENAME COLUMN IF EXISTS. */
{
List_iterator<Alter_column> it(alter_info->alter_list);
Alter_column *acol;
@@ -6178,7 +6434,7 @@ drop_create_field:
for (f_ptr=table->field; *f_ptr; f_ptr++)
{
if (my_strcasecmp(system_charset_info,
- acol->name, (*f_ptr)->field_name.str) == 0)
+ acol->name.str, (*f_ptr)->field_name.str) == 0)
break;
}
if (unlikely(*f_ptr == NULL))
@@ -6186,7 +6442,7 @@ drop_create_field:
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_BAD_FIELD_ERROR,
ER_THD(thd, ER_BAD_FIELD_ERROR),
- acol->name, table->s->table_name.str);
+ acol->name.str, table->s->table_name.str);
it.remove();
if (alter_info->alter_list.is_empty())
{
@@ -6330,6 +6586,35 @@ drop_create_field:
alter_info->flags|= left_flags;
}
+ /* Handle RENAME KEY IF EXISTS. */
+ {
+ List_iterator<Alter_rename_key> rename_key_it(alter_info->alter_rename_key_list);
+ Alter_rename_key *rename_key;
+ while ((rename_key= rename_key_it++))
+ {
+ if (!rename_key->alter_if_exists)
+ continue;
+ bool exists= false;
+ for (uint n_key= 0; n_key < table->s->keys; n_key++)
+ {
+ if (my_strcasecmp(system_charset_info,
+ rename_key->old_name.str,
+ table->key_info[n_key].name.str) == 0)
+ {
+ exists= true;
+ break;
+ }
+ }
+ if (exists)
+ continue;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_KEY_DOES_NOT_EXISTS,
+ ER_THD(thd, ER_KEY_DOES_NOT_EXISTS),
+ rename_key->old_name.str, table->s->table_name.str);
+ rename_key_it.remove();
+ }
+ }
+
/* ALTER TABLE ADD KEY IF NOT EXISTS */
/* ALTER TABLE ADD FOREIGN KEY IF NOT EXISTS */
{
@@ -6434,7 +6719,7 @@ remove_key:
DBUG_ASSERT(key->or_replace());
Alter_drop::drop_type type= (key->type == Key::FOREIGN_KEY) ?
Alter_drop::FOREIGN_KEY : Alter_drop::KEY;
- Alter_drop *ad= new Alter_drop(type, key->name.str, FALSE);
+ Alter_drop *ad= new (thd->mem_root) Alter_drop(type, key->name.str, FALSE);
if (ad != NULL)
{
// Adding the index into the drop list for replacing
@@ -6515,7 +6800,7 @@ remove_key:
while ((check=it++))
{
- if (!(check->flags & Alter_info::CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
+ if (!(check->flags & VCOL_CHECK_CONSTRAINT_IF_NOT_EXISTS) &&
check->name.length)
continue;
check->flags= 0;
@@ -6928,9 +7213,8 @@ static bool fill_alter_inplace_info(THD *thd, TABLE *table, bool varchar,
ha_alter_info->handler_flags|= ALTER_STORED_COLUMN_TYPE;
}
- /* Check if field was renamed */
- if (lex_string_cmp(system_charset_info, &field->field_name,
- &new_field->field_name))
+ /* Check if field was renamed (case-sensitive for detecting case change) */
+ if (cmp(&field->field_name, &new_field->field_name))
{
field->flags|= FIELD_IS_RENAMED;
ha_alter_info->handler_flags|= ALTER_COLUMN_NAME;
@@ -7591,10 +7875,11 @@ static bool mysql_inplace_alter_table(THD *thd,
{
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;
Alter_info *alter_info= ha_alter_info->alter_info;
bool reopen_tables= false;
bool res;
+ handlerton *hton;
+
const enum_alter_inplace_result inplace_supported=
ha_alter_info->inplace_supported;
@@ -7659,9 +7944,7 @@ static bool mysql_inplace_alter_table(THD *thd,
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);
+ table->s->tdc->flush(thd, false);
}
/*
@@ -7804,6 +8087,28 @@ static bool mysql_inplace_alter_table(THD *thd,
}
}
+ /* Notify the engine that the table definition has changed */
+
+ hton= table->file->partition_ht();
+ if (hton->notify_tabledef_changed)
+ {
+ char db_buff[FN_REFLEN], table_buff[FN_REFLEN];
+ LEX_CSTRING tmp_db, tmp_table;
+ tmp_db.str= db_buff;
+ tmp_table.str= table_buff;
+ tmp_db.length= tablename_to_filename(table_list->db.str,
+ db_buff, sizeof(db_buff));
+ tmp_table.length= tablename_to_filename(table_list->table_name.str,
+ table_buff, sizeof(table_buff));
+ if ((hton->notify_tabledef_changed)(hton, &tmp_db, &tmp_table,
+ table->s->frm_image,
+ &table->s->tabledef_version))
+ {
+ my_error(HA_ERR_INCOMPATIBLE_DEFINITION, MYF(0));
+ DBUG_RETURN(true);
+ }
+ }
+
close_all_tables_for_name(thd, table->s,
alter_ctx->is_table_renamed() ?
HA_EXTRA_PREPARE_FOR_RENAME :
@@ -7828,31 +8133,11 @@ static bool mysql_inplace_alter_table(THD *thd,
DBUG_RETURN(true);
}
- table_list->mdl_request.ticket= mdl_ticket;
- if (open_table(thd, table_list, &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.str, alter_ctx->table_name.str, false);
-
+ DBUG_ASSERT(!tdc_share_is_cached(thd, alter_ctx->db.str,
+ alter_ctx->table_name.str));
if (mysql_rename_table(db_type, &alter_ctx->db, &alter_ctx->table_name,
&alter_ctx->new_db, &alter_ctx->new_alias, 0))
{
@@ -7996,6 +8281,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
List<Create_field> new_create_list;
/* New key definitions are added here */
List<Key> new_key_list;
+ List<Alter_rename_key> rename_key_list(alter_info->alter_rename_key_list);
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
List_iterator<Create_field> def_it(alter_info->create_list);
List_iterator<Alter_column> alter_it(alter_info->alter_list);
@@ -8016,6 +8302,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Field **f_ptr,*field;
MY_BITMAP *dropped_fields= NULL; // if it's NULL - no dropped fields
bool drop_period= false;
+ LEX_CSTRING period_start_name= {nullptr, 0};
+ LEX_CSTRING period_end_name= {nullptr, 0};
+ if (table->s->period.name)
+ {
+ period_start_name= table->s->period_start_field()->field_name;
+ period_end_name= table->s->period_end_field()->field_name;
+ }
DBUG_ENTER("mysql_prepare_alter_table");
/*
@@ -8136,25 +8429,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
continue;
}
- /*
- If we are doing a rename of a column, update all references in virtual
- column expressions, constraints and defaults to use the new column name
- */
- if (alter_info->flags & ALTER_RENAME_COLUMN)
- {
- if (field->vcol_info)
- field->vcol_info->expr->walk(&Item::rename_fields_processor, 1,
- &column_rename_param);
- if (field->check_constraint)
- field->check_constraint->expr->walk(&Item::rename_fields_processor, 1,
- &column_rename_param);
- if (field->default_value)
- field->default_value->expr->walk(&Item::rename_fields_processor, 1,
- &column_rename_param);
- // Force reopen because new column name is on thd->mem_root
- table->mark_table_for_reopen();
- }
-
/* Check if field is changed */
def_it.rewind();
while ((def=def_it++))
@@ -8228,19 +8502,73 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
while ((alter=alter_it++))
{
if (!my_strcasecmp(system_charset_info,field->field_name.str,
- alter->name))
+ alter->name.str))
break;
}
- if (alter)
+ if (alter && field->invisible < INVISIBLE_SYSTEM)
{
- if ((def->default_value= alter->default_value))
- def->flags&= ~NO_DEFAULT_VALUE_FLAG;
+ if (alter->is_rename())
+ {
+ def->change= alter->name;
+ def->field_name= alter->new_name;
+ column_rename_param.fields.push_back(def);
+ if (field->flags & VERS_SYS_START_FLAG)
+ create_info->vers_info.as_row.start= alter->new_name;
+ else if (field->flags & VERS_SYS_END_FLAG)
+ create_info->vers_info.as_row.end= alter->new_name;
+ if (table->s->period.name)
+ {
+ if (field == table->period_start_field())
+ period_start_name= alter->new_name;
+ else if (field == table->period_end_field())
+ period_end_name= alter->new_name;
+ }
+ }
else
- def->flags|= NO_DEFAULT_VALUE_FLAG;
+ {
+ if ((def->default_value= alter->default_value))
+ def->flags&= ~NO_DEFAULT_VALUE_FLAG;
+ else
+ def->flags|= NO_DEFAULT_VALUE_FLAG;
+ }
alter_it.remove();
}
}
}
+
+ /*
+ If we are doing a rename of a column, update all references in virtual
+ column expressions, constraints and defaults to use the new column name
+ */
+ if (alter_info->flags & ALTER_RENAME_COLUMN)
+ {
+ alter_it.rewind();
+ Alter_column *alter;
+ while ((alter=alter_it++))
+ {
+ if (alter->is_rename())
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), alter->name.str,
+ table->s->table_name.str);
+ goto err;
+ }
+ }
+ for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
+ {
+ if (field->vcol_info)
+ field->vcol_info->expr->walk(&Item::rename_fields_processor, 1,
+ &column_rename_param);
+ if (field->check_constraint)
+ field->check_constraint->expr->walk(&Item::rename_fields_processor, 1,
+ &column_rename_param);
+ if (field->default_value)
+ field->default_value->expr->walk(&Item::rename_fields_processor, 1,
+ &column_rename_param);
+ }
+ // Force reopen because new column name is on thd->mem_root
+ table->mark_table_for_reopen();
+ }
+
dropped_sys_vers_fields &= VERS_SYSTEM_FIELD;
if ((dropped_sys_vers_fields ||
alter_info->flags & ALTER_DROP_PERIOD) &&
@@ -8294,15 +8622,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
If the '0000-00-00' value isn't allowed then raise the error_if_not_empty
flag to allow ALTER TABLE only if the table to be altered is empty.
*/
- if ((def->real_field_type() == MYSQL_TYPE_DATE ||
- def->real_field_type() == MYSQL_TYPE_NEWDATE ||
- def->real_field_type() == MYSQL_TYPE_DATETIME ||
- def->real_field_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_ctx->datetime_field= def;
+ if (!alter_ctx->implicit_default_value_error_field &&
+ !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) &&
+ def->type_handler()->validate_implicit_default_value(thd, *def))
+ {
+ alter_ctx->implicit_default_value_error_field= def;
alter_ctx->error_if_not_empty= TRUE;
}
if (!def->after.str)
@@ -8360,7 +8684,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
while ((alter=alter_it++))
{
if (!my_strcasecmp(system_charset_info,def->field_name.str,
- alter->name))
+ alter->name.str))
break;
}
if (alter)
@@ -8375,7 +8699,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (unlikely(alter_info->alter_list.elements))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
- alter_info->alter_list.head()->name, table->s->table_name.str);
+ alter_info->alter_list.head()->name.str, table->s->table_name.str);
goto err;
}
if (unlikely(!new_create_list.elements))
@@ -8425,6 +8749,39 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
continue;
}
+ /* If this index is to stay in the table check if it has to be renamed. */
+ List_iterator<Alter_rename_key> rename_key_it(rename_key_list);
+ Alter_rename_key *rename_key;
+
+ while ((rename_key= rename_key_it++))
+ {
+ if (!my_strcasecmp(system_charset_info, key_name, rename_key->old_name.str))
+ {
+ if (!my_strcasecmp(system_charset_info, key_name, primary_key_name))
+ {
+ my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), rename_key->old_name.str);
+ goto err;
+ }
+ else if (!my_strcasecmp(system_charset_info, rename_key->new_name.str,
+ primary_key_name))
+ {
+ my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), rename_key->new_name.str);
+ goto err;
+ }
+
+ key_name= rename_key->new_name.str;
+ rename_key_it.remove();
+ /*
+ If the user has explicitly renamed the key, we should no longer
+ treat it as generated. Otherwise this key might be automatically
+ dropped by mysql_prepare_create_table() and this will confuse
+ code in fill_alter_inplace_info().
+ */
+ key_info->flags&= ~HA_GENERATED_KEY;
+ break;
+ }
+ }
+
if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
{
setup_keyinfo_hash(key_info);
@@ -8433,8 +8790,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
const char *dropped_key_part= NULL;
KEY_PART_INFO *key_part= key_info->key_part;
key_parts.empty();
+ uint key_parts_nr= key_info->user_defined_key_parts;
+ if (key_info->without_overlaps)
+ key_parts_nr-= 2;
+
bool delete_index_stat= FALSE;
- for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
+ for (uint j=0 ; j < key_parts_nr ; j++,key_part++)
{
Field *kfield= key_part->field;
if (!kfield)
@@ -8573,6 +8934,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key= new (thd->mem_root) Key(key_type, &tmp_name, &key_create_info,
MY_TEST(key_info->flags & HA_GENERATED_KEY),
&key_parts, key_info->option_list, DDL_options());
+ key->without_overlaps= key_info->without_overlaps;
+ key->period= table->s->period.name;
new_key_list.push_back(key, thd->mem_root);
}
if (long_hash_key)
@@ -8620,9 +8983,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
else
{
- Field *s= table->s->period.start_field(table->s);
- Field *e= table->s->period.end_field(table->s);
- create_info->period_info.set_period(s->field_name, e->field_name);
+ create_info->period_info.set_period(period_start_name, period_end_name);
create_info->period_info.name= table->s->period.name;
}
}
@@ -8763,6 +9124,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
}
+ if (rename_key_list.elements)
+ {
+ my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0), rename_key_list.head()->old_name.str,
+ table->s->table_name.str);
+ goto err;
+ }
+
if (!create_info->comment.str)
{
create_info->comment.str= table->s->comment.str;
@@ -9155,8 +9523,8 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
ref_table= tbuf;
}
- mdl_request.init(MDL_key::TABLE, ref_db, ref_table, MDL_SHARED_NO_WRITE,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE, ref_db, ref_table,
+ MDL_SHARED_NO_WRITE, MDL_TRANSACTION);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
DBUG_RETURN(true);
@@ -9382,6 +9750,22 @@ static int create_table_for_inplace_alter(THD *thd,
}
+/*
+ log query if slave thread and send my_ok()
+
+ Help function for mysql_alter_table()
+*/
+
+static bool log_and_ok(THD *thd)
+{
+ if (thd->slave_thread &&
+ write_bin_log(thd, true, thd->query(), thd->query_length()))
+ return(true);
+ my_ok(thd);
+ return(0);
+}
+
+
/**
Alter table
@@ -9425,8 +9809,41 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
Alter_info *alter_info,
- uint order_num, ORDER *order, bool ignore)
+ uint order_num, ORDER *order, bool ignore,
+ bool if_exists)
{
+ bool engine_changed, error;
+ bool no_ha_table= true; /* We have not created table in storage engine yet */
+ TABLE *table, *new_table;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ bool partition_changed= false;
+ bool fast_alter_partition= false;
+#endif
+ /*
+ 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.
+ */
+ 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, table_creation_was_logged= 0;
+ bool binlog_as_create_select= 0, log_if_exists= 0;
+ uint tables_opened;
+ handlerton *new_db_type, *old_db_type;
+ ha_rows copied=0, deleted=0;
+ LEX_CUSTRING frm= {0,0};
+ char index_file[FN_REFLEN], data_file[FN_REFLEN];
+ MDL_request target_mdl_request;
+ MDL_ticket *mdl_ticket= 0;
+ Alter_table_prelocking_strategy alter_prelocking_strategy;
DBUG_ENTER("mysql_alter_table");
/*
@@ -9472,6 +9889,20 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
THD_STAGE_INFO(thd, stage_init_update);
+ /* Check if the new table type is a shared table */
+ if (ha_check_if_updates_are_ignored(thd, create_info->db_type, "ALTER"))
+ {
+ /*
+ Remove old local .frm file if it exists. We should use the new
+ shared one in the future. The drop is not logged, the ALTER table is
+ logged.
+ */
+ table_list->mdl_request.type= MDL_EXCLUSIVE;
+ /* This will only drop the .frm file and local tables, not shared ones */
+ error= mysql_rm_table(thd, table_list, 1, 0, 0, 1);
+ DBUG_RETURN(log_and_ok(thd));
+ }
+
/*
Code below can handle only base tables so ensure that we won't open a view.
Note that RENAME TABLE the only ALTER clause which is supported for views
@@ -9479,21 +9910,48 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
*/
table_list->required_type= TABLE_TYPE_NORMAL;
- Alter_table_prelocking_strategy alter_prelocking_strategy;
-
DEBUG_SYNC(thd, "alter_table_before_open_tables");
- uint tables_opened;
thd->open_options|= HA_OPEN_FOR_ALTER;
thd->mdl_backup_ticket= 0;
- bool error= open_tables(thd, &table_list, &tables_opened, 0,
- &alter_prelocking_strategy);
+ error= open_tables(thd, &table_list, &tables_opened, 0,
+ &alter_prelocking_strategy);
thd->open_options&= ~HA_OPEN_FOR_ALTER;
- TABLE *table= table_list->table;
- bool versioned= table && table->versioned();
+ if (unlikely(error))
+ {
+ if (if_exists)
+ {
+ int tmp_errno= thd->get_stmt_da()->sql_errno();
+ if (tmp_errno == ER_NO_SUCH_TABLE)
+ {
+ /*
+ ALTER TABLE IF EXISTS was used on not existing table
+ We have to log the query on a slave as the table may be a shared one
+ from the master and we need to ensure that the next slave can see
+ the statement as this slave may not have the table shared
+ */
+ thd->clear_error();
+ DBUG_RETURN(log_and_ok(thd));
+ }
+ }
+ DBUG_RETURN(true);
+ }
+
+ table= table_list->table;
- if (versioned)
+#ifdef WITH_WSREP
+ if (WSREP(thd) &&
+ (thd->lex->sql_command == SQLCOM_ALTER_TABLE ||
+ thd->lex->sql_command == SQLCOM_CREATE_INDEX ||
+ thd->lex->sql_command == SQLCOM_DROP_INDEX) &&
+ !wsrep_should_replicate_ddl(thd, table_list->table->s->db_type()->db_type))
+ DBUG_RETURN(true);
+#endif
+
+ DEBUG_SYNC(thd, "alter_table_after_open_tables");
+
+ if (table->versioned())
{
if (handlerton *hton1= create_info->db_type)
{
@@ -9528,11 +9986,42 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
};);
#endif // WITH_WSREP
- if (unlikely(error))
- DBUG_RETURN(true);
+ Alter_table_ctx alter_ctx(thd, table_list, tables_opened, new_db, new_name);
+ mdl_ticket= table->mdl_ticket;
+
+ /*
+ We have to do a check also after table is opened as there could be no
+ ENGINE= on the command line or the table could a partitioned S3 table.
+ */
+ if (table->file->check_if_updates_are_ignored("ALTER"))
+ {
+ /*
+ Table is a shared table. Remove the .frm file. Discovery will create
+ a new one if needed.
+ */
+ table->s->tdc->flushed= 1; // Force close of all instances
+ if (thd->mdl_context.upgrade_shared_lock(mdl_ticket,
+ MDL_EXCLUSIVE,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(1);
+ quick_rm_table(thd, table->file->ht, &table_list->db,
+ &table_list->table_name,
+ NO_HA_TABLE, 0);
+ goto end_inplace;
+ }
+ if (!if_exists &&
+ (table->file->partition_ht()->flags &
+ HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE))
+ {
+ /*
+ Table is a shared table that may not exist on the slave.
+ We add 'if_exists' to the query if it was not used
+ */
+ log_if_exists= 1;
+ }
+ table_creation_was_logged= table->s->table_creation_was_logged;
table->use_all_columns();
- MDL_ticket *mdl_ticket= table->mdl_ticket;
/*
Prohibit changing of the UNION list of a non-temporary MERGE table
@@ -9549,10 +10038,6 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
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 (alter_ctx.is_table_renamed())
{
@@ -9574,9 +10059,9 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
MDL_request_list mdl_requests;
MDL_request target_db_mdl_request;
- target_mdl_request.init(MDL_key::TABLE,
- alter_ctx.new_db.str, alter_ctx.new_name.str,
- MDL_EXCLUSIVE, MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&target_mdl_request, MDL_key::TABLE,
+ alter_ctx.new_db.str, alter_ctx.new_name.str,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
mdl_requests.push_front(&target_mdl_request);
/*
@@ -9586,9 +10071,9 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
*/
if (alter_ctx.is_database_changed())
{
- target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db.str, "",
- MDL_INTENTION_EXCLUSIVE,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&target_db_mdl_request, MDL_key::SCHEMA,
+ alter_ctx.new_db.str, "", MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
mdl_requests.push_front(&target_db_mdl_request);
}
@@ -9751,7 +10236,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
{
if ((table->key_info[n_key].flags & HA_NOSAME) &&
my_strcasecmp(system_charset_info,
- drop->name, table->key_info[n_key].name.str) == 0) // Merge todo: review '.str'
+ drop->name, table->key_info[n_key].name.str) == 0)
{
drop->type= Alter_drop::KEY;
alter_info->flags|= ALTER_DROP_INDEX;
@@ -9784,10 +10269,9 @@ do_continue:;
my_ok(thd, 0L, 0L, alter_ctx.tmp_buff);
/* We don't replicate alter table statement on temporary tables */
- if (table->s->tmp_table == NO_TMP_TABLE ||
- !thd->is_current_stmt_binlog_format_row())
+ if (table_creation_was_logged)
{
- if (write_bin_log(thd, true, thd->query(), thd->query_length()))
+ if (write_bin_log_with_if_exists(thd, true, false, log_if_exists))
DBUG_RETURN(true);
}
@@ -9831,12 +10315,9 @@ do_continue:;
/* We have to do full alter table. */
#ifdef WITH_PARTITION_STORAGE_ENGINE
- 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))
+ &partition_changed, &fast_alter_partition))
{
DBUG_RETURN(true);
}
@@ -9953,10 +10434,9 @@ do_continue:;
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;
+ new_db_type= create_info->db_type;
+ old_db_type= table->s->db_type();
+ new_table= NULL;
/*
Handling of symlinked tables:
@@ -9982,8 +10462,6 @@ do_continue:;
Copy data.
Remove old table and symlinks.
*/
- char index_file[FN_REFLEN], data_file[FN_REFLEN];
-
if (!alter_ctx.is_database_changed())
{
if (create_info->index_file_name)
@@ -10011,24 +10489,6 @@ do_continue:;
DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock");
- /*
- 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.
- */
- 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= create_table_impl(thd, alter_ctx.db, alter_ctx.table_name,
@@ -10044,9 +10504,6 @@ do_continue:;
DBUG_RETURN(true);
}
- /* Remember that we have not created table in storage engine yet. */
- bool no_ha_table= true;
-
if (alter_info->algorithm(thd) != Alter_info::ALTER_TABLE_ALGORITHM_COPY)
{
Alter_inplace_info ha_alter_info(create_info, alter_info,
@@ -10126,6 +10583,18 @@ do_continue:;
ha_alter_info.inplace_supported=
table->file->check_if_supported_inplace_alter(&altered_table,
&ha_alter_info);
+ if (ha_alter_info.inplace_supported != HA_ALTER_INPLACE_NOT_SUPPORTED)
+ {
+ List_iterator<Key> it(alter_info->key_list);
+ while (Key *k= it++)
+ {
+ if (k->without_overlaps)
+ {
+ ha_alter_info.inplace_supported= HA_ALTER_INPLACE_NOT_SUPPORTED;
+ break;
+ }
+ }
+ }
if (alter_info->supports_algorithm(thd, &ha_alter_info) ||
alter_info->supports_lock(thd, &ha_alter_info))
@@ -10237,7 +10706,7 @@ do_continue:;
if (table->s->tmp_table != NO_TMP_TABLE)
{
/* in case of alter temp table send the tracker in OK packet */
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
}
/*
@@ -10261,14 +10730,50 @@ do_continue:;
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
goto err_new_table_cleanup;
});
+
+ /*
+ If old table was a shared table and new table is not same type,
+ the slaves will not be able to recreate the data. In this case we
+ write the CREATE TABLE statement for the new table to the log and
+ log all inserted rows to the table.
+ */
+ if ((table->file->partition_ht()->flags &
+ HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE) &&
+ (table->file->partition_ht() != new_table->file->partition_ht()) &&
+ thd->binlog_table_should_be_logged(&new_table->s->db))
+ {
+ /*
+ 'new_table' is marked as internal temp table, but we want to have
+ the logging based on the original table type
+ */
+ bool res;
+ tmp_table_type org_tmp_table= new_table->s->tmp_table;
+ new_table->s->tmp_table= table->s->tmp_table;
+
+ /* Force row logging, even if the table was created as 'temporary' */
+ new_table->s->can_do_row_logging= 1;
+ thd->binlog_start_trans_and_stmt();
+ thd->variables.option_bits|= OPTION_BIN_COMMIT_OFF;
+ res= (binlog_drop_table(thd, table) ||
+ binlog_create_table(thd, new_table, 1));
+ new_table->s->tmp_table= org_tmp_table;
+ if (res)
+ goto err_new_table_cleanup;
+ /*
+ ha_write_row() will log inserted rows in copy_data_between_tables().
+ No additional logging of query is needed
+ */
+ binlog_as_create_select= 1;
+ DBUG_ASSERT(new_table->file->row_logging);
+ new_table->mark_columns_needed_for_insert();
+ thd->binlog_write_table_map(new_table, 1);
+ }
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
{
@@ -10291,8 +10796,10 @@ do_continue:;
if (thd->locked_tables_mode != LTM_LOCK_TABLES &&
thd->locked_tables_mode != LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
- mysql_unlock_tables(thd, thd->lock);
+ int tmp_error= mysql_unlock_tables(thd, thd->lock);
thd->lock= NULL;
+ if (tmp_error)
+ goto err_new_table_cleanup;
}
else
{
@@ -10300,7 +10807,8 @@ do_continue:;
If LOCK TABLES list is not empty and contains this table,
unlock the table and remove the table from this list.
*/
- mysql_lock_remove(thd, thd->lock, table);
+ if (mysql_lock_remove(thd, thd->lock, table))
+ goto err_new_table_cleanup;
}
}
new_table->s->table_creation_was_logged=
@@ -10311,15 +10819,45 @@ do_continue:;
if (thd->rename_temporary_table(new_table, &alter_ctx.new_db,
&alter_ctx.new_name))
goto err_new_table_cleanup;
+
+ if (binlog_as_create_select)
+ {
+ /*
+ The original table is now deleted. Copy the
+ DROP + CREATE + data statement to the binary log
+ */
+ thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF;
+ (binlog_hton->commit)(binlog_hton, thd, 1);
+ }
+
/* 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()))
+ table_creation_was_logged &&
+ !binlog_as_create_select &&
+ write_bin_log_with_if_exists(thd, true, false, log_if_exists))
DBUG_RETURN(true);
my_free(const_cast<uchar*>(frm.str));
goto end_temporary;
}
/*
+ Check if file names for the engine are unique. If we change engine
+ and file names are unique then we don't need to rename the original
+ table to a temporary name during the rename phase
+
+ File names are unique if engine changed and
+ - Either new or old engine does not store the table in files
+ - Neither old or new engine uses files from another engine
+ The above is mainly true for the sequence and the partition engine.
+ */
+ engine_changed= ((new_table->file->ht != table->file->ht) &&
+ (((!(new_table->file->ha_table_flags() & HA_FILE_BASED) ||
+ !(table->file->ha_table_flags() & HA_FILE_BASED))) ||
+ (!(table->file->ha_table_flags() & HA_REUSES_FILE_NAMES) &&
+ !(new_table->file->ha_table_flags() &
+ HA_REUSES_FILE_NAMES))));
+
+ /*
Close the intermediate table that will be the new table, but do
not delete it! Even though MERGE tables do not have their children
attached here it is safe to call THD::drop_temporary_table().
@@ -10362,23 +10900,39 @@ do_continue:;
/*
Rename the old table to temporary name to have a backup in case
anything goes wrong while renaming the new table.
+ We only have to do this if name of the table is not changed.
+ If we are changing to use another table handler, we don't
+ have to do the rename as the table names will not interfer.
*/
char backup_name_buff[FN_LEN];
LEX_CSTRING backup_name;
backup_name.str= backup_name_buff;
- backup_name.length= my_snprintf(backup_name_buff, sizeof(backup_name_buff),
- "%s2-%lx-%lx", tmp_file_prefix,
- current_pid, (long) thd->thread_id);
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, backup_name_buff);
- if (mysql_rename_table(old_db_type, &alter_ctx.db, &alter_ctx.table_name,
- &alter_ctx.db, &backup_name, FN_TO_IS_TMP))
+ DBUG_PRINT("info", ("is_table_renamed: %d engine_changed: %d",
+ alter_ctx.is_table_renamed(), engine_changed));
+
+ if (!alter_ctx.is_table_renamed())
{
- // 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;
+ backup_name.length= my_snprintf(backup_name_buff, sizeof(backup_name_buff),
+ "%s-backup-%lx-%llx", tmp_file_prefix,
+ current_pid, thd->thread_id);
+ if (lower_case_table_names)
+ my_casedn_str(files_charset_info, backup_name_buff);
+ if (mysql_rename_table(old_db_type, &alter_ctx.db, &alter_ctx.table_name,
+ &alter_ctx.db, &backup_name,
+ FN_TO_IS_TMP |
+ (engine_changed ? NO_HA_TABLE | NO_PAR_TABLE : 0)))
+ {
+ // 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
+ {
+ /* The original table is the backup */
+ backup_name= alter_ctx.table_name;
}
// Rename the new table to the correct name.
@@ -10390,10 +10944,15 @@ do_continue:;
(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);
+ if (!alter_ctx.is_table_renamed())
+ {
+ // 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 |
+ (engine_changed ? NO_HA_TABLE | NO_PAR_TABLE :
+ 0));
+ }
goto err_with_mdl;
}
@@ -10413,7 +10972,9 @@ do_continue:;
// 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);
+ FN_FROM_IS_TMP | NO_FK_CHECKS |
+ (engine_changed ? NO_HA_TABLE | NO_PAR_TABLE :
+ 0));
goto err_with_mdl;
}
rename_table_in_stat_tables(thd, &alter_ctx.db, &alter_ctx.alias,
@@ -10421,7 +10982,28 @@ do_continue:;
}
// 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))
+ error= quick_rm_table(thd, old_db_type, &alter_ctx.db, &backup_name,
+ FN_IS_TMP |
+ (engine_changed ? NO_HA_TABLE | NO_PAR_TABLE: 0));
+ if (engine_changed)
+ {
+ /* the .frm file was removed but not the original table */
+ error|= quick_rm_table(thd, old_db_type, &alter_ctx.db,
+ &alter_ctx.table_name,
+ NO_FRM_RENAME |
+ (engine_changed ? 0 : FN_IS_TMP));
+ }
+ if (binlog_as_create_select)
+ {
+ /*
+ The original table is now deleted. Copy the
+ DROP + CREATE + data statement to the binary log
+ */
+ thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF;
+ binlog_hton->commit(binlog_hton, thd, 1);
+ }
+
+ if (error)
{
/*
The fact that deletion of the backup failed is not critical
@@ -10432,6 +11014,7 @@ do_continue:;
}
end_inplace:
+ thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF;
if (thd->locked_tables_list.reopen_tables(thd, false))
goto err_with_mdl_after_alter;
@@ -10443,9 +11026,11 @@ end_inplace:
DBUG_ASSERT(!(mysql_bin_log.is_open() &&
thd->is_current_stmt_binlog_format_row() &&
(create_info->tmp_table())));
- if (write_bin_log(thd, true, thd->query(), thd->query_length()))
- DBUG_RETURN(true);
-
+ if (!binlog_as_create_select)
+ {
+ if (write_bin_log_with_if_exists(thd, true, false, log_if_exists))
+ DBUG_RETURN(true);
+ }
table_list->table= NULL; // For query cache
query_cache_invalidate3(thd, table_list, false);
@@ -10459,6 +11044,8 @@ end_inplace:
}
end_temporary:
+ thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF;
+
my_snprintf(alter_ctx.tmp_buff, sizeof(alter_ctx.tmp_buff),
ER_THD(thd, ER_INSERT_INFO),
(ulong) (copied + deleted), (ulong) deleted,
@@ -10468,6 +11055,9 @@ end_temporary:
DBUG_RETURN(false);
err_new_table_cleanup:
+ DBUG_PRINT("error", ("err_new_table_cleanup"));
+ thd->variables.option_bits&= ~OPTION_BIN_COMMIT_OFF;
+
my_free(const_cast<uchar*>(frm.str));
/*
No default value was provided for a DATE/DATETIME field, the
@@ -10478,31 +11068,9 @@ err_new_table_cleanup:
if (unlikely(alter_ctx.error_if_not_empty &&
thd->get_stmt_da()->current_row_for_warning()))
{
- const char *f_val= "0000-00-00";
- const char *f_type= "date";
- switch (alter_ctx.datetime_field->real_field_type())
- {
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_NEWDATE:
- break;
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_DATETIME2:
- f_val= "0000-00-00 00:00:00";
- f_type= "datetime";
- break;
- default:
- /* Shouldn't get here. */
- DBUG_ASSERT(0);
- }
- bool save_abort_on_warning= thd->abort_on_warning;
- thd->abort_on_warning= true;
- thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN,
- f_type, f_val,
- alter_ctx.new_db.str,
- alter_ctx.new_name.str,
- alter_ctx.datetime_field->
- field_name.str);
- thd->abort_on_warning= save_abort_on_warning;
+ Abort_on_warning_instant_set aws(thd, true);
+ alter_ctx.report_implicit_default_value_error(thd, new_table
+ ? new_table->s : table->s);
}
if (new_table)
@@ -10518,11 +11086,17 @@ err_new_table_cleanup:
DBUG_RETURN(true);
err_with_mdl_after_alter:
+ DBUG_PRINT("error", ("err_with_mdl_after_alter"));
/* the table was altered. binlog the operation */
DBUG_ASSERT(!(mysql_bin_log.is_open() &&
thd->is_current_stmt_binlog_format_row() &&
(create_info->tmp_table())));
- write_bin_log(thd, true, thd->query(), thd->query_length());
+ /*
+ We can't reset error as we will return 'true' below and the server
+ expects that error is set
+ */
+ if (!binlog_as_create_select)
+ write_bin_log_with_if_exists(thd, FALSE, FALSE, log_if_exists);
err_with_mdl:
/*
@@ -10567,7 +11141,7 @@ bool mysql_trans_commit_alter_copy_data(THD *thd)
DBUG_ENTER("mysql_trans_commit_alter_copy_data");
/* Save flags as trans_commit_implicit are deleting them */
- save_unsafe_rollback_flags= thd->transaction.stmt.m_unsafe_rollback_flags;
+ save_unsafe_rollback_flags= thd->transaction->stmt.m_unsafe_rollback_flags;
DEBUG_SYNC(thd, "alter_table_copy_trans_commit");
@@ -10585,7 +11159,7 @@ bool mysql_trans_commit_alter_copy_data(THD *thd)
if (trans_commit_implicit(thd))
error= TRUE;
- thd->transaction.stmt.m_unsafe_rollback_flags= save_unsafe_rollback_flags;
+ thd->transaction->stmt.m_unsafe_rollback_flags= save_unsafe_rollback_flags;
DBUG_RETURN(error);
}
@@ -10656,6 +11230,8 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->ha_start_bulk_insert(from->file->stats.records,
ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT);
bulk_insert_started= 1;
+ mysql_stage_set_work_estimated(thd->m_stage_progress_psi, from->file->stats.records);
+
List_iterator<Create_field> it(create);
Create_field *def;
copy_end=copy;
@@ -10707,14 +11283,12 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
to->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
- bool save_abort_on_warning= thd->abort_on_warning;
- thd->abort_on_warning= false;
+ Abort_on_warning_instant_set aws(thd, false);
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, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
warn_buff);
- thd->abort_on_warning= save_abort_on_warning;
}
else
{
@@ -10754,7 +11328,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
from->file->column_bitmaps_signal();
- THD_STAGE_INFO(thd, stage_copy_to_tmp_table);
+ to->file->prepare_for_insert(0);
+ DBUG_ASSERT(to->file->inited == handler::NONE);
+
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
/* Add virtual columns to vcol_set to ensure they are updated */
@@ -10899,7 +11475,11 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
}
}
else
+ {
+ DEBUG_SYNC(thd, "copy_data_between_tables_before");
found_count++;
+ mysql_stage_set_work_completed(thd->m_stage_progress_psi, found_count);
+ }
thd->get_stmt_da()->inc_current_row_for_warning();
}
@@ -10956,7 +11536,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
ha_enable_transaction(thd, TRUE);
}
- if (to->file->ha_external_lock(thd,F_UNLCK))
+ if (to->file->ha_external_unlock(thd))
error=1;
if (error < 0 && !from->s->tmp_table &&
to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
@@ -10997,6 +11577,7 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
bzero((char*) &create_info, sizeof(create_info));
create_info.row_type=ROW_TYPE_NOT_USED;
create_info.default_table_charset=default_charset_info;
+ create_info.alter_info= &alter_info;
/* Force alter table to recreate table */
alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
@@ -11005,8 +11586,8 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
Alter_info::ALTER_TABLE_ALGORITHM_COPY);
bool res= mysql_alter_table(thd, &null_clex_str, &null_clex_str, &create_info,
- table_list, &alter_info, 0,
- (ORDER *) 0, 0);
+ table_list, &alter_info, 0,
+ (ORDER *) 0, 0, 0);
table_list->next_global= next_table;
DBUG_RETURN(res);
}
@@ -11158,6 +11739,7 @@ err:
@retval true Engine not available/supported, error has been reported.
@retval false Engine available/supported.
*/
+
bool check_engine(THD *thd, const char *db_name,
const char *table_name, HA_CREATE_INFO *create_info)
{
@@ -11277,6 +11859,13 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
goto end_with_restore_list;
}
+ /*
+ Since CREATE_INFO is not full without Alter_info, it is better to pass them
+ as a signle parameter. TODO: remove alter_info argument where create_info is
+ passed.
+ */
+ create_info.alter_info= &alter_info;
+
/* Check privileges */
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;
@@ -11495,7 +12084,8 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
(!thd->is_current_stmt_binlog_format_row() ||
!create_info.tmp_table()))
{
- WSREP_TO_ISOLATION_BEGIN(create_table->db.str, create_table->table_name.str, NULL);
+ WSREP_TO_ISOLATION_BEGIN_CREATE(create_table->db.str, create_table->table_name.str,
+ create_table, &create_info);
}
/* Regular CREATE TABLE */
res= mysql_create_table(thd, create_table, &create_info, &alter_info);
@@ -11509,7 +12099,7 @@ bool Sql_cmd_create_table_like::execute(THD *thd)
ON then send session state notification in OK packet */
if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
{
- SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+ thd->session_tracker.state_change.mark_as_changed(thd);
}
my_ok(thd);
}
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 35bff0873ea..d67ceb6ebdd 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -29,6 +29,7 @@ class THD;
struct TABLE;
struct handlerton;
class handler;
+class String;
typedef struct st_ha_check_opt HA_CHECK_OPT;
struct HA_CREATE_INFO;
struct Table_specification_st;
@@ -118,8 +119,6 @@ enum enum_explain_filename_mode
EXPLAIN_PARTITIONS_AS_COMMENT
};
-/* Maximum length of GEOM_POINT Field */
-#define MAX_LEN_GEOM_POINT_FIELD 25
/* depends on errmsg.txt Database `db`, Table `t` ... */
#define EXPLAIN_FILENAME_MAX_EXTRA_LENGTH 63
@@ -140,6 +139,8 @@ static const uint NO_HA_TABLE= 1 << 4;
static const uint SKIP_SYMDIR_ACCESS= 1 << 5;
/** Don't check foreign key constraints while renaming table */
static const uint NO_FK_CHECKS= 1 << 6;
+/* Don't delete .par table in quick_rm_table() */
+static const uint NO_PAR_TABLE= 1 << 7;
uint filename_to_tablename(const char *from, char *to, size_t to_length,
bool stay_quiet = false);
@@ -154,6 +155,8 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen);
bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
Table_specification_st *create_info,
Alter_info *alter_info);
+bool add_keyword_to_query(THD *thd, String *result, const LEX_CSTRING *keyword,
+ const LEX_CSTRING *add);
/*
mysql_create_table_no_lock can be called in one of the following
@@ -217,11 +220,13 @@ bool mysql_prepare_alter_table(THD *thd, TABLE *table,
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, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name,
+bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db,
+ const LEX_CSTRING *new_name,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
Alter_info *alter_info,
- uint order_num, ORDER *order, bool ignore);
+ uint order_num, ORDER *order, bool ignore,
+ bool if_exists);
bool mysql_compare_tables(TABLE *table,
Alter_info *alter_info,
HA_CREATE_INFO *create_info,
@@ -240,11 +245,12 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list);
bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list,
HA_CHECK_OPT* check_opt);
bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool drop_sequence);
+ bool drop_temporary, bool drop_sequence,
+ bool dont_log_query);
int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool drop_temporary, bool drop_view,
bool drop_sequence,
- bool log_query, bool dont_free_locks);
+ bool dont_log_query, bool dont_free_locks);
bool log_drop_table(THD *thd, const LEX_CSTRING *db_name,
const LEX_CSTRING *table_name, bool temporary_table);
bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db,
@@ -258,6 +264,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags);
int write_bin_log(THD *thd, bool clear_error,
char const *query, ulong query_length,
bool is_trans= FALSE);
+int write_bin_log_with_if_exists(THD *thd, bool clear_error,
+ bool is_trans, bool add_if_exists);
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
DDL_LOG_MEMORY_ENTRY **active_entry);
bool write_execute_ddl_log_entry(uint first_entry,
diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc
index d912fabe8c8..bfbaf185243 100644
--- a/sql/sql_tablespace.cc
+++ b/sql/sql_tablespace.cc
@@ -32,7 +32,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
If the user haven't defined an engine, this will fallback to using the
default storage engine.
*/
- if (hton == NULL || hton->state != SHOW_OPTION_YES)
+ if (hton == NULL)
{
hton= ha_default_handlerton(thd);
if (ts_info->storage_engine != 0)
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 08dc137bebe..cc3c9badefb 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -27,6 +27,8 @@
#include "my_json_writer.h"
#include <hash.h>
#include <thr_alarm.h>
+#include "sql_connect.h"
+#include "thread_cache.h"
#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
#include <malloc.h>
#elif defined(HAVE_MALLINFO) && defined(HAVE_SYS_MALLOC_H)
@@ -88,9 +90,8 @@ static my_bool print_cached_tables_callback(TDC_element *element,
while ((entry= it++))
{
THD *in_use= entry->in_use;
- printf("%-14.14s %-32s%6lu%8ld%6d %s\n",
+ printf("%-14.14s %-32s%8ld%6d %s\n",
entry->s->db.str, entry->s->table_name.str,
- (ulong) element->version,
in_use ? (long) in_use->thread_id : (long) 0,
entry->db_stat ? 1 : 0,
in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
@@ -110,8 +111,6 @@ static void print_cached_tables(void)
tdc_iterate(0, (my_hash_walk_action) print_cached_tables_callback, NULL, true);
- printf("\nCurrent refresh version: %ld\n",
- (long) tdc_refresh_version());
fflush(stdout);
/* purecov: end */
return;
@@ -469,7 +468,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),
+ (void) my_init_dynamic_array(key_memory_locked_thread_list,
+ &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))
@@ -570,7 +570,7 @@ void mysql_print_status()
(void) my_getwd(current_dir, sizeof(current_dir),MYF(0));
printf("Current dir: %s\n", current_dir);
printf("Running threads: %d Cached threads: %lu Stack size: %ld\n",
- count, cached_thread_count,
+ count, thread_cache.size(),
(long) my_thread_stack_size);
#ifdef EXTRA_DEBUG
thr_print_locks(); // Write some debug info
@@ -643,7 +643,8 @@ Memory allocated by threads: %s\n",
llstr(info.uordblks, llbuff[4]),
llstr(info.fordblks, llbuff[5]),
llstr(info.keepcost, llbuff[6]),
- llstr((count + cached_thread_count)* my_thread_stack_size + info.hblkhd + info.arena, llbuff[7]),
+ llstr((count + thread_cache.size()) * my_thread_stack_size +
+ info.hblkhd + info.arena, llbuff[7]),
llstr(tmp.global_memory_used, llbuff[8]),
llstr(tmp.local_memory_used, llbuff[9]));
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index abc91907a55..8bb96dfa776 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -337,9 +337,9 @@ to_ascii(CHARSET_INFO *cs,
const char *srcend= src + src_length;
char *dst0= dst, *dstend= dst + dst_length - 1;
while (dst < dstend &&
- (cnvres= (cs->cset->mb_wc)(cs, &wc,
- (const uchar*) src,
- (const uchar*) srcend)) > 0 &&
+ (cnvres= cs->mb_wc(&wc,
+ (const uchar*) src,
+ (const uchar*) srcend)) > 0 &&
wc < 128)
{
src+= cnvres;
@@ -739,9 +739,7 @@ bool parse_date_time_format(timestamp_type format_type,
this. If separators are used, they must be between each part
*/
if (format_length == 6 && !need_p &&
- !my_strnncoll(&my_charset_bin,
- (const uchar *) format, 6,
- (const uchar *) format_str, 6))
+ !my_charset_bin.strnncoll(format, 6, format_str, 6))
return 0;
if (separator_map == (1 | 2))
{
@@ -762,9 +760,9 @@ bool parse_date_time_format(timestamp_type format_type,
Between DATE and TIME we also allow space as separator
*/
if ((format_length == 12 && !need_p &&
- !my_strnncoll(&my_charset_bin,
- (const uchar *) format, 12,
- (const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
+ !my_charset_bin.strnncoll(
+ format, 12,
+ known_date_time_formats[INTERNAL_FORMAT].datetime_format,
12)) ||
(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
return 0;
@@ -837,7 +835,8 @@ DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
if (thd)
new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
else
- new_format= (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
+ new_format= (DATE_TIME_FORMAT *) my_malloc(key_memory_DATE_TIME_FORMAT,
+ length, MYF(MY_WME));
if (new_format)
{
/* Put format string after current pos */
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 5802d2c811e..9417ec667ff 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -30,11 +30,11 @@
#include "sql_table.h" // build_table_filename,
// check_n_cut_mysql50_prefix
#include "sql_db.h" // get_default_db_collation
-#include "sql_acl.h" // *_ACL
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sp_cache.h" // sp_invalidate_cache
#include <mysys_err.h>
#include "debug_sync.h"
+#include "mysql/psi/mysql_sp.h"
/*************************************************************************/
@@ -440,7 +440,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
*/
if (!trust_function_creators &&
(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
- !(thd->security_ctx->master_access & SUPER_ACL))
+ !(thd->security_ctx->master_access & PRIV_LOG_BIN_TRUSTED_SP_CREATOR))
{
my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0));
DBUG_RETURN(TRUE);
@@ -463,7 +463,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
*/
thd->lex->sql_command= backup.sql_command;
- if (opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) &&
+ if (opt_readonly &&
+ !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) &&
!thd->slave_thread)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
@@ -507,9 +508,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
goto end;
}
-#ifdef WITH_WSREP
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables);
-#endif
/* We should have only one table in table list. */
DBUG_ASSERT(tables->next_global == 0);
@@ -550,10 +549,20 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
}
table= tables->table;
+#ifdef WITH_WSREP
+ if (WSREP(thd) &&
+ !wsrep_should_replicate_ddl(thd, table->s->db_type()->db_type))
+ goto wsrep_error_label;
+#endif
+
/* Later on we will need it to downgrade the lock */
mdl_ticket= table->mdl_ticket;
- if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ /*
+ RENAME ensures that table is flushed properly and locked tables will
+ be removed from the active transaction
+ */
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
goto end;
lock_upgrade_done= TRUE;
@@ -585,10 +594,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
- if (result)
- goto end;
-
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
@@ -620,7 +627,13 @@ end:
thd->lex->restore_backup_query_tables_list(&backup);
if (!result)
+ {
my_ok(thd);
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(SP_TYPE_TRIGGER,
+ thd->lex->spname->m_db.str, static_cast<uint>(thd->lex->spname->m_db.length),
+ thd->lex->spname->m_name.str, static_cast<uint>(thd->lex->spname->m_name.length));
+ }
DBUG_RETURN(result);
#ifdef WITH_WSREP
@@ -1020,10 +1033,10 @@ bool Trigger::add_to_file_list(void* param_arg)
*/
static bool rm_trigger_file(char *path, const LEX_CSTRING *db,
- const LEX_CSTRING *table_name)
+ const LEX_CSTRING *table_name, myf MyFlags)
{
build_table_filename(path, FN_REFLEN-1, db->str, table_name->str, TRG_EXT, 0);
- return mysql_file_delete(key_file_trg, path, MYF(MY_WME));
+ return mysql_file_delete(key_file_trg, path, MyFlags);
}
@@ -1042,10 +1055,11 @@ static bool rm_trigger_file(char *path, const LEX_CSTRING *db,
*/
static bool rm_trigname_file(char *path, const LEX_CSTRING *db,
- const LEX_CSTRING *trigger_name)
+ const LEX_CSTRING *trigger_name, myf MyFlags)
{
- build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str, TRN_EXT, 0);
- return mysql_file_delete(key_file_trn, path, MYF(MY_WME));
+ build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str,
+ TRN_EXT, 0);
+ return mysql_file_delete(key_file_trn, path, MyFlags);
}
@@ -1163,7 +1177,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
parse_file.cc functionality (because we will need it
elsewhere).
*/
- if (rm_trigger_file(path, &tables->db, &tables->table_name))
+ if (rm_trigger_file(path, &tables->db, &tables->table_name, MYF(MY_WME)))
return 1;
}
else
@@ -1172,7 +1186,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
return 1;
}
- if (rm_trigname_file(path, &tables->db, sp_name))
+ if (rm_trigname_file(path, &tables->db, sp_name, MYF(MY_WME)))
return 1;
delete trigger;
@@ -1313,13 +1327,14 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
This could be avoided if there is no triggers for UPDATE and DELETE.
@retval
- False success
+ False no triggers or triggers where correctly loaded
@retval
- True error
+ True error (wrong trigger file)
*/
bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
- const LEX_CSTRING *table_name, TABLE *table,
+ const LEX_CSTRING *table_name,
+ TABLE *table,
bool names_only)
{
char path_buff[FN_REFLEN];
@@ -1550,6 +1565,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
trigger->definer= *trg_definer;
}
+ sp->m_sp_share= MYSQL_GET_SP_SHARE(SP_TYPE_TRIGGER,
+ sp->m_db.str, static_cast<uint>(sp->m_db.length),
+ sp->m_name.str, static_cast<uint>(sp->m_name.length));
+
#ifndef DBUG_OFF
/*
Let us check that we correctly update trigger definitions when we
@@ -1624,7 +1643,7 @@ err_with_lex_cleanup:
}
error:
- if (unlikely(!thd->is_error()))
+ if (unlikely(!thd->is_error()))
{
/*
We don't care about this error message much because .TRG files will
@@ -1801,20 +1820,23 @@ bool add_table_for_trigger(THD *thd,
*/
bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
- const LEX_CSTRING *name)
+ const LEX_CSTRING *name,
+ myf MyFlags)
{
TABLE table;
char path[FN_REFLEN];
bool result= 0;
- DBUG_ENTER("Triggers::drop_all_triggers");
+ DBUG_ENTER("Table_triggers_list::drop_all_triggers");
table.reset();
- init_sql_alloc(&table.mem_root, "Triggers::drop_all_triggers", 8192, 0,
- MYF(0));
+ init_sql_alloc(key_memory_Table_trigger_dispatcher,
+ &table.mem_root, 8192, 0, MYF(MY_WME));
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
result= 1;
+ /* We couldn't parse trigger file, best to just remove it */
+ rm_trigger_file(path, db, name, MyFlags);
goto end;
}
if (table.triggers)
@@ -1834,7 +1856,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
Such triggers have zero-length name and are skipped here.
*/
if (trigger->name.length &&
- rm_trigname_file(path, db, &trigger->name))
+ rm_trigname_file(path, db, &trigger->name, MyFlags))
{
/*
Instead of immediately bailing out with error if we were unable
@@ -1842,10 +1864,13 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
*/
result= 1;
}
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(SP_TYPE_TRIGGER, db->str, static_cast<uint>(db->length),
+ trigger->name.str, static_cast<uint>(trigger->name.length));
}
}
}
- if (rm_trigger_file(path, db, name))
+ if (rm_trigger_file(path, db, name, MyFlags))
result= 1;
delete table.triggers;
}
@@ -1906,9 +1931,10 @@ change_table_name_in_triggers(THD *thd,
if (save_trigger_file(thd, new_db_name, new_table_name))
return TRUE;
- if (rm_trigger_file(path_buff, old_db_name, old_table_name))
+ if (rm_trigger_file(path_buff, old_db_name, old_table_name, MYF(MY_WME)))
{
- (void) rm_trigger_file(path_buff, new_db_name, new_table_name);
+ (void) rm_trigger_file(path_buff, new_db_name, new_table_name,
+ MYF(MY_WME));
return TRUE;
}
return FALSE;
@@ -2017,9 +2043,11 @@ bool Trigger::change_on_table_name(void* param_arg)
/* Remove stale .TRN file in case of database upgrade */
if (param->old_db_name)
{
- if (rm_trigname_file(trigname_buff, param->old_db_name, &name))
+ if (rm_trigname_file(trigname_buff, param->old_db_name, &name,
+ MYF(MY_WME)))
{
- (void) rm_trigname_file(trigname_buff, param->new_db_name, &name);
+ (void) rm_trigname_file(trigname_buff, param->new_db_name, &name,
+ MYF(MY_WME));
return 1;
}
}
@@ -2061,8 +2089,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db,
DBUG_ENTER("Triggers::change_table_name");
table.reset();
- init_sql_alloc(&table.mem_root, "Triggers::change_table_name", 8192, 0,
- MYF(0));
+ init_sql_alloc(key_memory_Table_trigger_dispatcher,
+ &table.mem_root, 8192, 0, MYF(0));
/*
This method interfaces the mysql server code protected by
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index ae3d1738b16..040d8eba989 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -231,7 +231,7 @@ public:
static bool check_n_load(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name,
TABLE *table, bool names_only);
static bool drop_all_triggers(THD *thd, const LEX_CSTRING *db,
- const LEX_CSTRING *table_name);
+ const LEX_CSTRING *table_name, myf MyFlags);
static bool change_table_name(THD *thd, const LEX_CSTRING *db,
const LEX_CSTRING *old_alias,
const LEX_CSTRING *old_table,
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index bfcdda6e0e9..8ed996c0a8d 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -246,7 +246,7 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
inspite of errors.
*/
if (error == HA_ERR_WRONG_COMMAND ||
- table_ref->table->file->has_transactions())
+ table_ref->table->file->has_transactions_and_rollback())
DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
else
DBUG_RETURN(TRUNCATE_FAILED_BUT_BINLOG);
@@ -303,6 +303,12 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
versioned= table->versioned();
hton= table->file->ht;
+#ifdef WITH_WSREP
+ if (WSREP(thd) &&
+ !wsrep_should_replicate_ddl(thd, hton->db_type))
+ DBUG_RETURN(TRUE);
+#endif
+
table_ref->mdl_request.ticket= table->mdl_ticket;
}
else
@@ -320,8 +326,20 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
versioned= share->versioned;
sequence= share->table_type == TABLE_TYPE_SEQUENCE;
hton= share->db_type();
+#ifdef WITH_WSREP
+ if (WSREP(thd) &&
+ hton != view_pseudo_hton &&
+ !wsrep_should_replicate_ddl(thd, hton->db_type))
+ {
+ tdc_release_share(share);
+ DBUG_RETURN(TRUE);
+ }
+#endif
- tdc_release_share(share);
+ if (!versioned)
+ tdc_remove_referenced_share(thd, share);
+ else
+ tdc_release_share(share);
if (hton == view_pseudo_hton)
{
@@ -357,13 +375,6 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
if (*hton_can_recreate)
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
- else
- {
- /* Table is already locked exclusively. Remove cached instances. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db.str,
- table_ref->table_name.str, FALSE);
- }
-
DBUG_RETURN(FALSE);
}
@@ -417,9 +428,10 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
#ifdef WITH_WSREP
if (WSREP(thd) &&
- wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, 0))
- DBUG_RETURN(TRUE);
+ wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, NULL))
+ DBUG_RETURN(TRUE);
#endif /* WITH_WSREP */
+
if (lock_table(thd, table_ref, &hton_can_recreate))
DBUG_RETURN(TRUE);
diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc
index 763fd8ec45b..df774a5d8dd 100644
--- a/sql/sql_tvc.cc
+++ b/sql/sql_tvc.cc
@@ -22,6 +22,37 @@
#include "sql_explain.h"
#include "sql_parse.h"
#include "sql_cte.h"
+#include "my_json_writer.h"
+
+
+/**
+ @brief
+ Walk through all VALUES items.
+ @param
+ @param processor - the processor to call for each Item
+ @param walk_qubquery - if should dive into subquery items
+ @param argument - the argument to pass recursively
+ @retval
+ true on error
+ false on success
+*/
+bool table_value_constr::walk_values(Item_processor processor,
+ bool walk_subquery,
+ void *argument)
+{
+ List_iterator_fast<List_item> list_item_it(lists_of_values);
+ while (List_item *list= list_item_it++)
+ {
+ List_iterator_fast<Item> item_it(*list);
+ while (Item *item= item_it++)
+ {
+ if (item->walk(&Item::unknown_splocal_processor, false, argument))
+ return true;
+ }
+ }
+ return false;
+}
+
/**
@brief
@@ -394,9 +425,10 @@ bool table_value_constr::exec(SELECT_LEX *sl)
while ((elem= li++))
{
- if (send_records >= sl->master_unit()->select_limit_cnt)
+ if (send_records >= sl->master_unit()->lim.get_select_limit())
break;
- int rc= result->send_data(*elem);
+ int rc=
+ result->send_data_with_check(*elem, sl->master_unit(), send_records);
if (!rc)
send_records++;
else if (rc > 0)
@@ -655,7 +687,7 @@ st_select_lex *wrap_tvc(THD *thd, st_select_lex *tvc_sl,
wrapper_sl->set_linkage(tvc_sl->get_linkage());
wrapper_sl->parsing_place= SELECT_LIST;
item= new (thd->mem_root) Item_field(thd, &wrapper_sl->context,
- NULL, NULL, &star_clex_str);
+ star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
(wrapper_sl->with_wild)++;
@@ -759,6 +791,7 @@ st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl)
{
wrapper_sl->master_unit()->union_distinct= wrapper_sl;
}
+ wrapper_sl->distinct= tvc_sl->distinct;
thd->lex->current_select= wrapper_sl;
return wrapper_sl;
}
@@ -871,6 +904,10 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
if (!transform_into_subq)
return this;
+ Json_writer_object trace_wrapper(thd);
+ Json_writer_object trace_conv(thd, "in_to_subquery_conversion");
+ trace_conv.add("item", this);
+
transform_into_subq= false;
List<List_item> values;
@@ -890,13 +927,29 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
uint32 length= max_length_of_left_expr();
if (!length || length > tmp_table_max_key_length() ||
args[0]->cols() > tmp_table_max_key_parts())
+ {
+ trace_conv.add("done", false);
+ trace_conv.add("reason", "key is too long");
return this;
-
+ }
+
for (uint i=1; i < arg_count; i++)
{
- if (!args[i]->const_item() || cmp_row_types(args[0], args[i]))
+ if (!args[i]->const_item())
+ {
+ trace_conv.add("done", false);
+ trace_conv.add("reason", "non-constant element in the IN-list");
return this;
+ }
+
+ if (cmp_row_types(args[0], args[i]))
+ {
+ trace_conv.add("done", false);
+ trace_conv.add("reason", "type mismatch");
+ return this;
+ }
}
+ Json_writer_array trace_nested_obj(thd, "conversion");
Query_arena backup;
Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
@@ -913,7 +966,7 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
sq_select= lex->current_select;
sq_select->parsing_place= SELECT_LIST;
item= new (thd->mem_root) Item_field(thd, &sq_select->context,
- NULL, NULL, &star_clex_str);
+ star_clex_str);
if (item == NULL || add_item_to_list(thd, item))
goto err;
(sq_select->with_wild)++;
@@ -988,6 +1041,7 @@ Item *Item_func_in::in_predicate_to_in_subs_transformer(THD *thd,
goto err;
parent_select->curr_tvc_name++;
+
return sq;
err:
diff --git a/sql/sql_tvc.h b/sql/sql_tvc.h
index 594a77af65c..b5b8b979f43 100644
--- a/sql/sql_tvc.h
+++ b/sql/sql_tvc.h
@@ -18,6 +18,7 @@
#include "sql_type.h"
typedef List<Item> List_item;
+typedef bool (Item::*Item_processor) (void *arg);
class select_result;
class Explain_select;
class Explain_query;
@@ -65,6 +66,7 @@ public:
bool exec(SELECT_LEX *sl);
void print(THD *thd_arg, String *str, enum_query_type query_type);
+ bool walk_values(Item_processor processor, bool walk_subquery, void *arg);
};
st_select_lex *wrap_tvc_with_tail(THD *thd, st_select_lex *tvc_sl);
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index ad356b4c874..00b9c71cc37 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -16,12 +16,15 @@
#include "mariadb.h"
#include "sql_type.h"
+#include "sql_type_geom.h"
#include "sql_const.h"
#include "sql_class.h"
#include "sql_time.h"
#include "item.h"
#include "log.h"
#include "tztime.h"
+#include <mysql/plugin_data_type.h>
+
const DTCollation &DTCollation_numeric::singleton()
{
@@ -29,56 +32,158 @@ const DTCollation &DTCollation_numeric::singleton()
return tmp;
}
-Type_handler_row type_handler_row;
-
-Type_handler_null type_handler_null;
-
-Type_handler_bool type_handler_bool;
-Type_handler_tiny type_handler_tiny;
-Type_handler_short type_handler_short;
-Type_handler_long type_handler_long;
-Type_handler_int24 type_handler_int24;
-Type_handler_longlong type_handler_longlong;
-Type_handler_longlong type_handler_ulonglong; // Only used for CAST() for now
-Type_handler_vers_trx_id type_handler_vers_trx_id;
-Type_handler_float type_handler_float;
-Type_handler_double type_handler_double;
-Type_handler_bit type_handler_bit;
-
-Type_handler_olddecimal type_handler_olddecimal;
-Type_handler_newdecimal type_handler_newdecimal;
-
-Type_handler_year type_handler_year;
-Type_handler_year type_handler_year2;
-Type_handler_time type_handler_time;
-Type_handler_date type_handler_date;
-Type_handler_timestamp type_handler_timestamp;
-Type_handler_timestamp2 type_handler_timestamp2;
-Type_handler_datetime type_handler_datetime;
-Type_handler_time2 type_handler_time2;
-Type_handler_newdate type_handler_newdate;
-Type_handler_datetime2 type_handler_datetime2;
-
-Type_handler_enum type_handler_enum;
-Type_handler_set type_handler_set;
-
-Type_handler_string type_handler_string;
-Type_handler_var_string type_handler_var_string;
-Type_handler_varchar type_handler_varchar;
-Type_handler_hex_hybrid type_handler_hex_hybrid;
-static Type_handler_varchar_compressed type_handler_varchar_compressed;
-
-Type_handler_tiny_blob type_handler_tiny_blob;
-Type_handler_medium_blob type_handler_medium_blob;
-Type_handler_long_blob type_handler_long_blob;
-Type_handler_blob type_handler_blob;
-static Type_handler_blob_compressed type_handler_blob_compressed;
+Named_type_handler<Type_handler_row> type_handler_row("row");
+
+Named_type_handler<Type_handler_null> type_handler_null("null");
+
+Named_type_handler<Type_handler_bool> type_handler_bool("boolean");
+Named_type_handler<Type_handler_tiny> type_handler_stiny("tinyint");
+Named_type_handler<Type_handler_short> type_handler_sshort("smallint");
+Named_type_handler<Type_handler_long> type_handler_slong("int");
+Named_type_handler<Type_handler_int24> type_handler_sint24("mediumint");
+Named_type_handler<Type_handler_longlong> type_handler_slonglong("bigint");
+Named_type_handler<Type_handler_utiny> type_handler_utiny("tiny unsigned");
+Named_type_handler<Type_handler_ushort> type_handler_ushort("smallint unsigned");
+Named_type_handler<Type_handler_ulong> type_handler_ulong("int unsigned");
+Named_type_handler<Type_handler_uint24> type_handler_uint24("mediumint unsigned");
+Named_type_handler<Type_handler_ulonglong> type_handler_ulonglong("bigint unsigned");
+Named_type_handler<Type_handler_vers_trx_id> type_handler_vers_trx_id("bigint unsigned");
+Named_type_handler<Type_handler_float> type_handler_float("float");
+Named_type_handler<Type_handler_double> type_handler_double("double");
+Named_type_handler<Type_handler_bit> type_handler_bit("bit");
+
+Named_type_handler<Type_handler_olddecimal> type_handler_olddecimal("decimal");
+Named_type_handler<Type_handler_newdecimal> type_handler_newdecimal("decimal");
+
+Named_type_handler<Type_handler_year> type_handler_year("year");
+Named_type_handler<Type_handler_year> type_handler_year2("year");
+Named_type_handler<Type_handler_time> type_handler_time("time");
+Named_type_handler<Type_handler_date> type_handler_date("date");
+Named_type_handler<Type_handler_timestamp> type_handler_timestamp("timestamp");
+Named_type_handler<Type_handler_timestamp2> type_handler_timestamp2("timestamp");
+Named_type_handler<Type_handler_datetime> type_handler_datetime("datetime");
+Named_type_handler<Type_handler_time2> type_handler_time2("time");
+Named_type_handler<Type_handler_newdate> type_handler_newdate("date");
+Named_type_handler<Type_handler_datetime2> type_handler_datetime2("datetime");
+
+Named_type_handler<Type_handler_enum> type_handler_enum("enum");
+Named_type_handler<Type_handler_set> type_handler_set("set");
+
+Named_type_handler<Type_handler_string> type_handler_string("char");
+Named_type_handler<Type_handler_var_string> type_handler_var_string("varchar");
+Named_type_handler<Type_handler_varchar> type_handler_varchar("varchar");
+Named_type_handler<Type_handler_hex_hybrid> type_handler_hex_hybrid("hex_hybrid");
+Named_type_handler<Type_handler_varchar_compressed> type_handler_varchar_compressed("varchar");
+
+Named_type_handler<Type_handler_tiny_blob> type_handler_tiny_blob("tinyblob");
+Named_type_handler<Type_handler_medium_blob> type_handler_medium_blob("mediumblob");
+Named_type_handler<Type_handler_long_blob> type_handler_long_blob("longblob");
+Named_type_handler<Type_handler_blob> type_handler_blob("blob");
+Named_type_handler<Type_handler_blob_compressed> type_handler_blob_compressed("blob");
Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff;
+Vers_type_timestamp vers_type_timestamp;
+Vers_type_trx vers_type_trx;
+
+/***************************************************************************/
+
+
+
+class Type_collection_std: public Type_collection
+{
+public:
+ const Type_handler *handler_by_name(const LEX_CSTRING &name) const override
+ {
+ return NULL;
+ }
+ const Type_handler *aggregate_for_result(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ {
+ return Type_handler::aggregate_for_result_traditional(a, b);
+ }
+ const Type_handler *aggregate_for_comparison(const Type_handler *a,
+ const Type_handler *b)
+ const override;
+ const Type_handler *aggregate_for_min_max(const Type_handler *a,
+ const Type_handler *b)
+ const override;
+ const Type_handler *aggregate_for_num_op(const Type_handler *a,
+ const Type_handler *b)
+ const override;
+};
+
+
+static Type_collection_std type_collection_std;
+
+const Type_collection *Type_handler::type_collection() const
+{
+ return &type_collection_std;
+}
+
+
+bool Type_handler::is_traditional_scalar_type() const
+{
+ return type_collection() == &type_collection_std;
+}
+
+
+class Type_collection_row: public Type_collection
+{
+public:
+ bool init(Type_handler_data *data) override
+ {
+ return false;
+ }
+ const Type_handler *handler_by_name(const LEX_CSTRING &name) const override
+ {
+ return NULL;
+ }
+ const Type_handler *aggregate_for_result(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ {
+ return NULL;
+ }
+ const Type_handler *aggregate_for_comparison(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ {
+ DBUG_ASSERT(a == &type_handler_row);
+ DBUG_ASSERT(b == &type_handler_row);
+ return &type_handler_row;
+ }
+ const Type_handler *aggregate_for_min_max(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ {
+ return NULL;
+ }
+ const Type_handler *aggregate_for_num_op(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ {
+ return NULL;
+ }
+};
+
+
+static Type_collection_row type_collection_row;
+
+const Type_collection *Type_handler_row::type_collection() const
+{
+ return &type_collection_row;
+}
+
+
+bool Type_handler_data::init()
+{
#ifdef HAVE_SPATIAL
-Type_handler_geometry type_handler_geometry;
+ return type_collection_geometry.init(this);
#endif
+ return false;
+}
Schema *Type_handler::schema() const
@@ -87,59 +192,60 @@ Schema *Type_handler::schema() const
}
-bool Type_handler_data::init()
+const Type_handler *
+Type_handler::handler_by_name(THD *thd, const LEX_CSTRING &name)
{
+ plugin_ref plugin;
+ if ((plugin= my_plugin_lock_by_name(thd, &name, MariaDB_DATA_TYPE_PLUGIN)))
+ {
+ /*
+ Data type plugins do not maintain ref_count yet.
+ For now we have only mandatory built-in plugins
+ and dynamic plugins for test purposes.
+ It should be safe to unlock the plugin immediately.
+ */
+ const Type_handler *ph= reinterpret_cast<st_mariadb_data_type*>
+ (plugin_decl(plugin)->info)->type_handler;
+ plugin_unlock(thd, plugin);
+ return ph;
+ }
+
#ifdef HAVE_SPATIAL
+ const Type_handler *ha= type_collection_geometry.handler_by_name(name);
+ if (ha)
+ return ha;
+#endif
+ return NULL;
+}
+
#ifndef DBUG_OFF
- if (m_type_aggregator_non_commutative_test.add(&type_handler_geometry,
- &type_handler_geometry,
- &type_handler_geometry) ||
- m_type_aggregator_non_commutative_test.add(&type_handler_geometry,
- &type_handler_varchar,
- &type_handler_long_blob))
- return true;
+static const Type_handler *frm_data_type_info_emulate(const LEX_CSTRING &name)
+{
+ if (Name(STRING_WITH_LEN("xchar")).eq(name))
+ return &type_handler_string;
+ if (Name(STRING_WITH_LEN("xblob")).eq(name))
+ return &type_handler_blob;
+ return NULL;
+}
#endif
- return
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_null,
- &type_handler_geometry) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_geometry,
- &type_handler_geometry) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_hex_hybrid,
- &type_handler_long_blob) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_tiny_blob,
- &type_handler_long_blob) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_blob,
- &type_handler_long_blob) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_medium_blob,
- &type_handler_long_blob) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_long_blob,
- &type_handler_long_blob) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_varchar,
- &type_handler_long_blob) ||
- m_type_aggregator_for_result.add(&type_handler_geometry,
- &type_handler_string,
- &type_handler_long_blob) ||
- m_type_aggregator_for_comparison.add(&type_handler_geometry,
- &type_handler_geometry,
- &type_handler_geometry) ||
- m_type_aggregator_for_comparison.add(&type_handler_geometry,
- &type_handler_null,
- &type_handler_geometry) ||
- m_type_aggregator_for_comparison.add(&type_handler_geometry,
- &type_handler_long_blob,
- &type_handler_long_blob);
-#endif
- return false;
+
+const Type_handler *
+Type_handler::handler_by_name_or_error(THD *thd, const LEX_CSTRING &name)
+{
+ const Type_handler *h= handler_by_name(thd, name);
+ DBUG_EXECUTE_IF("emulate_handler_by_name_or_error_failure", h= NULL;);
+ if (!h)
+ {
+ DBUG_EXECUTE_IF("frm_data_type_info_emulate",
+ if ((h= frm_data_type_info_emulate(name)))
+ return h;
+ );
+ my_error(ER_UNKNOWN_DATA_TYPE, MYF(0),
+ ErrConvString(name.str, name.length, system_charset_info).ptr());
+ }
+ return h;
}
@@ -1072,7 +1178,7 @@ Datetime_truncation_not_needed::Datetime_truncation_not_needed(THD *thd, Item *i
/********************************************************************/
-uint Type_std_attributes::count_max_decimals(Item **item, uint nitems)
+uint Type_numeric_attributes::find_max_decimals(Item **item, uint nitems)
{
uint res= 0;
for (uint i= 0; i < nitems; i++)
@@ -1081,55 +1187,61 @@ uint Type_std_attributes::count_max_decimals(Item **item, uint nitems)
}
-/**
- Set max_length/decimals of function if function is fixed point and
- result length/precision depends on argument ones.
-*/
-
-void Type_std_attributes::count_decimal_length(Item **item, uint nitems)
+uint Type_numeric_attributes::count_unsigned(Item **item, uint nitems)
{
- int max_int_part= 0;
- decimals= 0;
- unsigned_flag= 1;
- for (uint i=0 ; i < nitems ; i++)
+ uint res= 0;
+ for (uint i= 0 ; i < nitems ; i++)
{
- set_if_bigger(decimals, item[i]->decimals);
- set_if_bigger(max_int_part, item[i]->decimal_int_part());
- set_if_smaller(unsigned_flag, item[i]->unsigned_flag);
+ if (item[i]->unsigned_flag)
+ res++;
}
- int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
- fix_char_length(my_decimal_precision_to_length_no_truncation(precision,
- (uint8) decimals,
- unsigned_flag));
+ return res;
}
-/**
- Set max_length of if it is maximum length of its arguments.
-*/
-
-void Type_std_attributes::count_only_length(Item **item, uint nitems)
+uint32 Type_numeric_attributes::find_max_char_length(Item **item, uint nitems)
{
uint32 char_length= 0;
- unsigned_flag= 0;
for (uint i= 0; i < nitems ; i++)
- {
set_if_bigger(char_length, item[i]->max_char_length());
- set_if_bigger(unsigned_flag, item[i]->unsigned_flag);
- }
- fix_char_length(char_length);
+ return char_length;
}
-void Type_std_attributes::count_octet_length(Item **item, uint nitems)
+uint32 Type_numeric_attributes::find_max_octet_length(Item **item, uint nitems)
{
- max_length= 0;
- unsigned_flag= 0;
+ uint32 octet_length= 0;
for (uint i= 0; i < nitems ; i++)
- {
- set_if_bigger(max_length, item[i]->max_length);
- set_if_bigger(unsigned_flag, item[i]->unsigned_flag);
- }
+ set_if_bigger(octet_length, item[i]->max_length);
+ return octet_length;
+}
+
+
+int Type_numeric_attributes::find_max_decimal_int_part(Item **item, uint nitems)
+{
+ int max_int_part= 0;
+ for (uint i=0 ; i < nitems ; i++)
+ set_if_bigger(max_int_part, item[i]->decimal_int_part());
+ return max_int_part;
+}
+
+
+/**
+ Set max_length/decimals of function if function is fixed point and
+ result length/precision depends on argument ones.
+*/
+
+void
+Type_numeric_attributes::aggregate_numeric_attributes_decimal(Item **item,
+ uint nitems,
+ bool unsigned_arg)
+{
+ int max_int_part= find_max_decimal_int_part(item, nitems);
+ decimals= find_max_decimals(item, nitems);
+ int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+ max_length= my_decimal_precision_to_length_no_truncation(precision,
+ (uint8) decimals,
+ unsigned_flag);
}
@@ -1138,7 +1250,9 @@ void Type_std_attributes::count_octet_length(Item **item, uint nitems)
result length/precision depends on argument ones.
*/
-void Type_std_attributes::count_real_length(Item **items, uint nitems)
+void
+Type_numeric_attributes::aggregate_numeric_attributes_real(Item **items,
+ uint nitems)
{
uint32 length= 0;
decimals= 0;
@@ -1177,16 +1291,17 @@ void Type_std_attributes::count_real_length(Item **items, uint nitems)
@retval False on success, true on error.
*/
-bool Type_std_attributes::count_string_length(const char *func_name,
- Item **items, uint nitems)
+bool Type_std_attributes::aggregate_attributes_string(const char *func_name,
+ Item **items, uint nitems)
{
if (agg_arg_charsets_for_string_result(collation, func_name,
items, nitems, 1))
return true;
if (collation.collation == &my_charset_bin)
- count_octet_length(items, nitems);
+ max_length= find_max_octet_length(items, nitems);
else
- count_only_length(items, nitems);
+ fix_char_length(find_max_char_length(items, nitems));
+ unsigned_flag= false;
decimals= max_length ? NOT_FIXED_DEC : 0;
return false;
}
@@ -1333,7 +1448,7 @@ Type_handler::get_handler_by_cmp_type(Item_result type)
{
switch (type) {
case REAL_RESULT: return &type_handler_double;
- case INT_RESULT: return &type_handler_longlong;
+ case INT_RESULT: return &type_handler_slonglong;
case DECIMAL_RESULT: return &type_handler_newdecimal;
case STRING_RESULT: return &type_handler_long_blob;
case TIME_RESULT: return &type_handler_datetime;
@@ -1344,6 +1459,37 @@ Type_handler::get_handler_by_cmp_type(Item_result type)
}
+/*
+ If we have a mixture of:
+ - a MariaDB standard (built-in permanent) data type, and
+ - a non-standard (optionally compiled or pluggable) data type,
+ then we ask the type collection of the non-standard type to aggregate
+ the mixture.
+ The standard type collection type_collection_std knows nothing
+ about non-standard types, while non-standard type collections
+ know everything about standard data types.
+*/
+const Type_collection *
+Type_handler::type_collection_for_aggregation(const Type_handler *h0,
+ const Type_handler *h1)
+{
+ const Type_collection *c0= h0->type_collection();
+ const Type_collection *c1= h1->type_collection();
+ if (c0 == c1)
+ return c0;
+ if (c0 == &type_collection_std)
+ return c1;
+ if (c1 == &type_collection_std)
+ return c0;
+ /*
+ A mixture of two non-standard collections.
+ The caller code will continue to aggregate through
+ the type aggregators in Type_handler_data.
+ */
+ return NULL;
+}
+
+
Type_handler_hybrid_field_type::Type_handler_hybrid_field_type()
:m_type_handler(&type_handler_double)
{
@@ -1365,66 +1511,149 @@ uint Type_handler_time::m_hires_bytes[MAX_DATETIME_PRECISION + 1]=
{ 3, 4, 4, 5, 5, 5, 6 };
/***************************************************************************/
-const Name Type_handler_row::m_name_row(STRING_WITH_LEN("row"));
-
-const Name Type_handler_null::m_name_null(STRING_WITH_LEN("null"));
-
-const Name
- Type_handler_string::m_name_char(STRING_WITH_LEN("char")),
- Type_handler_var_string::m_name_var_string(STRING_WITH_LEN("varchar")),
- Type_handler_varchar::m_name_varchar(STRING_WITH_LEN("varchar")),
- Type_handler_hex_hybrid::m_name_hex_hybrid(STRING_WITH_LEN("hex_hybrid")),
- Type_handler_tiny_blob::m_name_tinyblob(STRING_WITH_LEN("tinyblob")),
- Type_handler_medium_blob::m_name_mediumblob(STRING_WITH_LEN("mediumblob")),
- Type_handler_long_blob::m_name_longblob(STRING_WITH_LEN("longblob")),
- Type_handler_blob::m_name_blob(STRING_WITH_LEN("blob"));
-
-const Name
- Type_handler_enum::m_name_enum(STRING_WITH_LEN("enum")),
- Type_handler_set::m_name_set(STRING_WITH_LEN("set"));
-
-const Name
- Type_handler_bool::m_name_bool(STRING_WITH_LEN("boolean")),
- Type_handler_tiny::m_name_tiny(STRING_WITH_LEN("tinyint")),
- Type_handler_short::m_name_short(STRING_WITH_LEN("smallint")),
- Type_handler_long::m_name_int(STRING_WITH_LEN("int")),
- Type_handler_longlong::m_name_longlong(STRING_WITH_LEN("bigint")),
- Type_handler_int24::m_name_mediumint(STRING_WITH_LEN("mediumint")),
- Type_handler_year::m_name_year(STRING_WITH_LEN("year")),
- Type_handler_bit::m_name_bit(STRING_WITH_LEN("bit"));
-
-const Name
- Type_handler_float::m_name_float(STRING_WITH_LEN("float")),
- Type_handler_double::m_name_double(STRING_WITH_LEN("double"));
-
-const Name
- Type_handler_olddecimal::m_name_decimal(STRING_WITH_LEN("decimal")),
- Type_handler_newdecimal::m_name_decimal(STRING_WITH_LEN("decimal"));
-
-const Name
- Type_handler_time_common::m_name_time(STRING_WITH_LEN("time")),
- Type_handler_date_common::m_name_date(STRING_WITH_LEN("date")),
- Type_handler_datetime_common::m_name_datetime(STRING_WITH_LEN("datetime")),
- Type_handler_timestamp_common::m_name_timestamp(STRING_WITH_LEN("timestamp"));
-
-const Name
- Type_handler::m_version_default(STRING_WITH_LEN("")),
- Type_handler::m_version_mariadb53(STRING_WITH_LEN("mariadb-5.3")),
- Type_handler::m_version_mysql56(STRING_WITH_LEN("mysql-5.6"));
-
-
-const Type_limits_int
- Type_handler_tiny::m_limits_sint8= Type_limits_sint8(),
- Type_handler_tiny::m_limits_uint8= Type_limits_uint8(),
- Type_handler_short::m_limits_sint16= Type_limits_sint16(),
- Type_handler_short::m_limits_uint16= Type_limits_uint16(),
- Type_handler_int24::m_limits_sint24= Type_limits_sint24(),
- Type_handler_int24::m_limits_uint24= Type_limits_uint24(),
- Type_handler_long::m_limits_sint32= Type_limits_sint32(),
- Type_handler_long::m_limits_uint32= Type_limits_uint32(),
- Type_handler_longlong::m_limits_sint64= Type_limits_sint64(),
- Type_handler_longlong::m_limits_uint64= Type_limits_uint64();
+const Name Type_handler::version() const
+{
+ static const Name ver(STRING_WITH_LEN(""));
+ return ver;
+}
+
+const Name & Type_handler::version_mariadb53()
+{
+ static const Name ver(STRING_WITH_LEN("mariadb-5.3"));
+ return ver;
+}
+
+const Name & Type_handler::version_mysql56()
+{
+ static const Name ver(STRING_WITH_LEN("mysql-5.6"));
+ return ver;
+}
+
+
+/***************************************************************************/
+
+const Type_limits_int *Type_handler_tiny::type_limits_int() const
+{
+ static const Type_limits_sint8 limits_sint8;
+ return &limits_sint8;
+}
+
+const Type_limits_int *Type_handler_utiny::type_limits_int() const
+{
+ static const Type_limits_uint8 limits_uint8;
+ return &limits_uint8;
+}
+
+const Type_limits_int *Type_handler_short::type_limits_int() const
+{
+ static const Type_limits_sint16 limits_sint16;
+ return &limits_sint16;
+}
+
+const Type_limits_int *Type_handler_ushort::type_limits_int() const
+{
+ static const Type_limits_uint16 limits_uint16;
+ return &limits_uint16;
+}
+
+const Type_limits_int *Type_handler_int24::type_limits_int() const
+{
+ static const Type_limits_sint24 limits_sint24;
+ return &limits_sint24;
+}
+
+const Type_limits_int *Type_handler_uint24::type_limits_int() const
+{
+ static const Type_limits_uint24 limits_uint24;
+ return &limits_uint24;
+}
+
+const Type_limits_int *Type_handler_long::type_limits_int() const
+{
+ static const Type_limits_sint32 limits_sint32;
+ return &limits_sint32;
+}
+
+const Type_limits_int *Type_handler_ulong::type_limits_int() const
+{
+ static const Type_limits_uint32 limits_uint32;
+ return &limits_uint32;
+}
+
+const Type_limits_int *Type_handler_longlong::type_limits_int() const
+{
+ static const Type_limits_sint64 limits_sint64;
+ return &limits_sint64;
+}
+
+const Type_limits_int *Type_handler_ulonglong::type_limits_int() const
+{
+ static const Type_limits_uint64 limits_uint64;
+ return &limits_uint64;
+}
+
+
+/***************************************************************************/
+const Type_handler *Type_handler_bool::type_handler_signed() const
+{
+ return &type_handler_bool;
+}
+
+const Type_handler *Type_handler_bool::type_handler_unsigned() const
+{
+ return &type_handler_bool;
+}
+
+const Type_handler *Type_handler_tiny::type_handler_signed() const
+{
+ return &type_handler_stiny;
+}
+
+const Type_handler *Type_handler_tiny::type_handler_unsigned() const
+{
+ return &type_handler_utiny;
+}
+
+const Type_handler *Type_handler_short::type_handler_signed() const
+{
+ return &type_handler_sshort;
+}
+
+const Type_handler *Type_handler_short::type_handler_unsigned() const
+{
+ return &type_handler_ushort;
+}
+
+const Type_handler *Type_handler_int24::type_handler_signed() const
+{
+ return &type_handler_sint24;
+}
+
+const Type_handler *Type_handler_int24::type_handler_unsigned() const
+{
+ return &type_handler_uint24;
+}
+
+const Type_handler *Type_handler_long::type_handler_signed() const
+{
+ return &type_handler_slong;
+}
+
+const Type_handler *Type_handler_long::type_handler_unsigned() const
+{
+ return &type_handler_ulong;
+}
+
+const Type_handler *Type_handler_longlong::type_handler_signed() const
+{
+ return &type_handler_slonglong;
+}
+
+const Type_handler *Type_handler_longlong::type_handler_unsigned() const
+{
+ return &type_handler_ulonglong;
+}
/***************************************************************************/
@@ -1436,7 +1665,7 @@ const Type_handler *Type_handler_null::type_handler_for_comparison() const
const Type_handler *Type_handler_int_result::type_handler_for_comparison() const
{
- return &type_handler_longlong;
+ return &type_handler_slonglong;
}
@@ -1512,7 +1741,7 @@ const Type_handler *Type_handler_typelib::type_handler_for_item_field() const
const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const
{
- return &type_handler_longlong;
+ return &type_handler_slonglong;
}
@@ -1521,29 +1750,35 @@ const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const
bool
Type_handler_hybrid_field_type::aggregate_for_result(const Type_handler *other)
{
- if (m_type_handler->is_traditional_type() && other->is_traditional_type())
- {
- m_type_handler=
- Type_handler::aggregate_for_result_traditional(m_type_handler, other);
- return false;
- }
- other= type_handler_data->
- m_type_aggregator_for_result.find_handler(m_type_handler, other);
- if (!other)
+ const Type_handler *hres;
+ const Type_collection *c;
+ if (!(c= Type_handler::type_collection_for_aggregation(m_type_handler, other)) ||
+ !(hres= c->aggregate_for_result(m_type_handler, other)))
+ hres= type_handler_data->
+ m_type_aggregator_for_result.find_handler(m_type_handler, other);
+ if (!hres)
return true;
- m_type_handler= other;
+ m_type_handler= hres;
return false;
}
const Type_handler *
-Type_handler::type_handler_long_or_longlong(uint max_char_length)
+Type_handler::type_handler_long_or_longlong(uint max_char_length,
+ bool unsigned_flag)
{
+ if (unsigned_flag)
+ {
+ if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS - 2)
+ return &type_handler_ulong;
+ return &type_handler_ulonglong;
+ }
if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS - 2)
- return &type_handler_long;
- return &type_handler_longlong;
+ return &type_handler_slong;
+ return &type_handler_slonglong;
}
+
/*
This method is called for CASE (and its abbreviations) and LEAST/GREATEST
when data type aggregation returned LONGLONG and there were some BIT
@@ -1554,8 +1789,8 @@ const Type_handler *
Type_handler::bit_and_int_mixture_handler(uint max_char_length)
{
if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS)
- return &type_handler_long;
- return &type_handler_longlong;
+ return &type_handler_slong;
+ return &type_handler_slonglong;
}
@@ -1617,9 +1852,9 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
{
bit_and_non_bit_mixture_found= true;
if (type_handler() == &type_handler_bit)
- set_handler(&type_handler_longlong); // BIT + non-BIT
+ set_handler(&type_handler_slonglong); // BIT + non-BIT
else
- cur= &type_handler_longlong; // non-BIT + BIT
+ cur= &type_handler_slonglong; // non-BIT + BIT
}
if (aggregate_for_result(cur))
{
@@ -1628,7 +1863,7 @@ Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname,
return true;
}
}
- if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong)
+ if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_slonglong)
set_handler(Type_handler::bit_and_int_mixture_handler(max_display_length));
return false;
}
@@ -1647,37 +1882,42 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
{
DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison());
DBUG_ASSERT(h == h->type_handler_for_comparison());
+ const Type_handler *hres;
+ const Type_collection *c;
+ if (!(c= Type_handler::type_collection_for_aggregation(m_type_handler, h)) ||
+ !(hres= c->aggregate_for_comparison(m_type_handler, h)))
+ hres= type_handler_data->
+ m_type_aggregator_for_comparison.find_handler(m_type_handler, h);
+ if (!hres)
+ return true;
+ m_type_handler= hres;
+ DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison());
+ return false;
+}
- if (!m_type_handler->is_traditional_type() ||
- !h->is_traditional_type())
- {
- h= type_handler_data->
- m_type_aggregator_for_comparison.find_handler(m_type_handler, h);
- if (!h)
- return true;
- m_type_handler= h;
- DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison());
- return false;
- }
- Item_result a= cmp_type();
- Item_result b= h->cmp_type();
+const Type_handler *
+Type_collection_std::aggregate_for_comparison(const Type_handler *ha,
+ const Type_handler *hb) const
+{
+ Item_result a= ha->cmp_type();
+ Item_result b= hb->cmp_type();
if (a == STRING_RESULT && b == STRING_RESULT)
- m_type_handler= &type_handler_long_blob;
- else if (a == INT_RESULT && b == INT_RESULT)
- m_type_handler= &type_handler_longlong;
- else if (a == ROW_RESULT || b == ROW_RESULT)
- m_type_handler= &type_handler_row;
- else if (a == TIME_RESULT || b == TIME_RESULT)
+ return &type_handler_long_blob;
+ if (a == INT_RESULT && b == INT_RESULT)
+ return &type_handler_slonglong;
+ if (a == ROW_RESULT || b == ROW_RESULT)
+ return &type_handler_row;
+ if (a == TIME_RESULT || b == TIME_RESULT)
{
if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
{
/*
We're here if there's only one temporal data type:
either m_type_handler or h.
+ Temporal types bit non-temporal types.
*/
- if (b == TIME_RESULT)
- m_type_handler= h; // Temporal types bit non-temporal types
+ const Type_handler *res= b == TIME_RESULT ? hb : ha;
/*
Compare TIMESTAMP to a non-temporal type as DATETIME.
This is needed to make queries with fuzzy dates work:
@@ -1685,9 +1925,9 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
WHERE
ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00';
*/
- if (m_type_handler->type_handler_for_native_format() ==
- &type_handler_timestamp2)
- m_type_handler= &type_handler_datetime;
+ if (res->type_handler_for_native_format() == &type_handler_timestamp2)
+ return &type_handler_datetime;
+ return res;
}
else
{
@@ -1699,19 +1939,15 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
to print DATE constants using proper format:
'YYYY-MM-DD' rather than 'YYYY-MM-DD 00:00:00'.
*/
- if (m_type_handler->field_type() != h->field_type())
- m_type_handler= &type_handler_datetime;
+ if (ha->field_type() != hb->field_type())
+ return &type_handler_datetime;
+ return ha;
}
}
- else if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
- (b == INT_RESULT || b == DECIMAL_RESULT))
- {
- m_type_handler= &type_handler_newdecimal;
- }
- else
- m_type_handler= &type_handler_double;
- DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison());
- return false;
+ if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
+ (b == INT_RESULT || b == DECIMAL_RESULT))
+ return &type_handler_newdecimal;
+ return &type_handler_double;
}
@@ -1727,12 +1963,12 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
bool
Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
{
- if (!m_type_handler->is_traditional_type() ||
- !h->is_traditional_type())
+ const Type_handler *hres;
+ const Type_collection *c;
+ if (!(c= Type_handler::type_collection_for_aggregation(m_type_handler, h))||
+ !(hres= c->aggregate_for_min_max(m_type_handler, h)))
{
/*
- If at least one data type is non-traditional,
- do aggregation for result immediately.
For now we suppose that these two expressions:
- LEAST(type1, type2)
- COALESCE(type1, type2)
@@ -1740,79 +1976,74 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
if type1 and/or type2 are non-traditional.
This may change in the future.
*/
- h= type_handler_data->
- m_type_aggregator_for_result.find_handler(m_type_handler, h);
- if (!h)
- return true;
- m_type_handler= h;
- return false;
+ hres= type_handler_data->
+ m_type_aggregator_for_result.find_handler(m_type_handler, h);
}
+ if (!hres)
+ return true;
+ m_type_handler= hres;
+ return false;
+}
- Item_result a= cmp_type();
- Item_result b= h->cmp_type();
+
+const Type_handler *
+Type_collection_std::aggregate_for_min_max(const Type_handler *ha,
+ const Type_handler *hb) const
+{
+ Item_result a= ha->cmp_type();
+ Item_result b= hb->cmp_type();
DBUG_ASSERT(a != ROW_RESULT); // Disallowed by check_cols() in fix_fields()
DBUG_ASSERT(b != ROW_RESULT); // Disallowed by check_cols() in fix_fields()
if (a == STRING_RESULT && b == STRING_RESULT)
- m_type_handler=
- Type_handler::aggregate_for_result_traditional(m_type_handler, h);
- else if (a == INT_RESULT && b == INT_RESULT)
+ return Type_collection_std::aggregate_for_result(ha, hb);
+ if (a == INT_RESULT && b == INT_RESULT)
{
// BIT aggregates with non-BIT as BIGINT
- if (m_type_handler != h)
+ if (ha != hb)
{
- if (m_type_handler == &type_handler_bit)
- m_type_handler= &type_handler_longlong;
- else if (h == &type_handler_bit)
- h= &type_handler_longlong;
+ if (ha == &type_handler_bit)
+ ha= &type_handler_slonglong;
+ else if (hb == &type_handler_bit)
+ hb= &type_handler_slonglong;
}
- m_type_handler=
- Type_handler::aggregate_for_result_traditional(m_type_handler, h);
+ return Type_collection_std::aggregate_for_result(ha, hb);
}
- else if (a == TIME_RESULT || b == TIME_RESULT)
+ if (a == TIME_RESULT || b == TIME_RESULT)
{
- if ((m_type_handler->type_handler_for_native_format() ==
- &type_handler_timestamp2) +
- (h->type_handler_for_native_format() ==
- &type_handler_timestamp2) == 1)
+ if ((ha->type_handler_for_native_format() == &type_handler_timestamp2) +
+ (hb->type_handler_for_native_format() == &type_handler_timestamp2) == 1)
{
/*
Handle LEAST(TIMESTAMP, non-TIMESTAMP) as DATETIME,
to make sure fuzzy dates work in this context:
LEAST('2001-00-00', timestamp_field)
*/
- m_type_handler= &type_handler_datetime2;
+ return &type_handler_datetime2;
}
- else if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
+ if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
{
/*
We're here if there's only one temporal data type:
either m_type_handler or h.
+ Temporal types bit non-temporal types.
*/
- if (b == TIME_RESULT)
- m_type_handler= h; // Temporal types bit non-temporal types
- }
- else
- {
- /*
- We're here if both m_type_handler and h are temporal data types.
- */
- m_type_handler=
- Type_handler::aggregate_for_result_traditional(m_type_handler, h);
+ return (b == TIME_RESULT) ? hb : ha;
}
+ /*
+ We're here if both m_type_handler and h are temporal data types.
+ */
+ return Type_collection_std::aggregate_for_result(ha, hb);
}
- else if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
- (b == INT_RESULT || b == DECIMAL_RESULT))
+ if ((a == INT_RESULT || a == DECIMAL_RESULT) &&
+ (b == INT_RESULT || b == DECIMAL_RESULT))
{
- m_type_handler= &type_handler_newdecimal;
- }
- else
- {
- // Preserve FLOAT if two FLOATs, set to DOUBLE otherwise.
- if (m_type_handler != &type_handler_float || h != &type_handler_float)
- m_type_handler= &type_handler_double;
+ return &type_handler_newdecimal;
}
- return false;
+ // Preserve FLOAT if two FLOATs, set to DOUBLE otherwise.
+ if (ha == &type_handler_float && hb == &type_handler_float)
+ return &type_handler_float;
+ return &type_handler_double;
}
@@ -1821,15 +2052,12 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname,
Item **items, uint nitems)
{
bool bit_and_non_bit_mixture_found= false;
- uint32 max_display_length;
// LEAST/GREATEST require at least two arguments
DBUG_ASSERT(nitems > 1);
set_handler(items[0]->type_handler());
- max_display_length= items[0]->max_display_length();
for (uint i= 1; i < nitems; i++)
{
const Type_handler *cur= items[i]->type_handler();
- set_if_bigger(max_display_length, items[i]->max_display_length());
// Check if BIT + non-BIT, or non-BIT + BIT
bit_and_non_bit_mixture_found|= (m_type_handler == &type_handler_bit) !=
(cur == &type_handler_bit);
@@ -1840,15 +2068,20 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname,
return true;
}
}
- if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong)
+ if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_slonglong)
+ {
+ uint32 max_display_length= items[0]->max_display_length();
+ for (uint i= 1; i < nitems; i++)
+ set_if_bigger(max_display_length, items[i]->max_display_length());
set_handler(Type_handler::bit_and_int_mixture_handler(max_display_length));
+ }
return false;
}
const Type_handler *
-Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0,
- const Type_handler *h1)
+Type_collection_std::aggregate_for_num_op(const Type_handler *h0,
+ const Type_handler *h1) const
{
Item_result r0= h0->cmp_type();
Item_result r1= h1->cmp_type();
@@ -1864,7 +2097,7 @@ Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0,
return &type_handler_newdecimal;
DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT);
- return &type_handler_longlong;
+ return &type_handler_slonglong;
}
@@ -1889,17 +2122,14 @@ Type_handler_hybrid_field_type::aggregate_for_num_op(const Type_aggregator *agg,
const Type_handler *h1)
{
const Type_handler *hres;
- if (h0->is_traditional_type() && h1->is_traditional_type())
- {
- set_handler(Type_handler::aggregate_for_num_op_traditional(h0, h1));
- return false;
- }
- if ((hres= agg->find_handler(h0, h1)))
- {
- set_handler(hres);
- return false;
- }
- return true;
+ const Type_collection *c;
+ if (!(c= Type_handler::type_collection_for_aggregation(h0, h1)) ||
+ !(hres= c->aggregate_for_num_op(h0, h1)))
+ hres= agg->find_handler(h0, h1);
+ if (!hres)
+ return true;
+ m_type_handler= hres;
+ return false;
}
@@ -1911,11 +2141,11 @@ Type_handler::get_handler_by_field_type(enum_field_types type)
switch (type) {
case MYSQL_TYPE_DECIMAL: return &type_handler_olddecimal;
case MYSQL_TYPE_NEWDECIMAL: return &type_handler_newdecimal;
- case MYSQL_TYPE_TINY: return &type_handler_tiny;
- case MYSQL_TYPE_SHORT: return &type_handler_short;
- case MYSQL_TYPE_LONG: return &type_handler_long;
- case MYSQL_TYPE_LONGLONG: return &type_handler_longlong;
- case MYSQL_TYPE_INT24: return &type_handler_int24;
+ case MYSQL_TYPE_TINY: return &type_handler_stiny;
+ case MYSQL_TYPE_SHORT: return &type_handler_sshort;
+ case MYSQL_TYPE_LONG: return &type_handler_slong;
+ case MYSQL_TYPE_LONGLONG: return &type_handler_slonglong;
+ case MYSQL_TYPE_INT24: return &type_handler_sint24;
case MYSQL_TYPE_YEAR: return &type_handler_year;
case MYSQL_TYPE_BIT: return &type_handler_bit;
case MYSQL_TYPE_FLOAT: return &type_handler_float;
@@ -1966,11 +2196,11 @@ Type_handler::get_handler_by_real_type(enum_field_types type)
switch (type) {
case MYSQL_TYPE_DECIMAL: return &type_handler_olddecimal;
case MYSQL_TYPE_NEWDECIMAL: return &type_handler_newdecimal;
- case MYSQL_TYPE_TINY: return &type_handler_tiny;
- case MYSQL_TYPE_SHORT: return &type_handler_short;
- case MYSQL_TYPE_LONG: return &type_handler_long;
- case MYSQL_TYPE_LONGLONG: return &type_handler_longlong;
- case MYSQL_TYPE_INT24: return &type_handler_int24;
+ case MYSQL_TYPE_TINY: return &type_handler_stiny;
+ case MYSQL_TYPE_SHORT: return &type_handler_sshort;
+ case MYSQL_TYPE_LONG: return &type_handler_slong;
+ case MYSQL_TYPE_LONGLONG: return &type_handler_slonglong;
+ case MYSQL_TYPE_INT24: return &type_handler_sint24;
case MYSQL_TYPE_YEAR: return &type_handler_year;
case MYSQL_TYPE_BIT: return &type_handler_bit;
case MYSQL_TYPE_FLOAT: return &type_handler_float;
@@ -1983,13 +2213,7 @@ Type_handler::get_handler_by_real_type(enum_field_types type)
case MYSQL_TYPE_LONG_BLOB: return &type_handler_long_blob;
case MYSQL_TYPE_BLOB: return &type_handler_blob;
case MYSQL_TYPE_BLOB_COMPRESSED: return &type_handler_blob_compressed;
- case MYSQL_TYPE_VAR_STRING:
- /*
- VAR_STRING is actually a field_type(), not a real_type(),
- but it's used around the code in real_type() context.
- We should clean up the code and add DBUG_ASSERT(0) here.
- */
- return &type_handler_string;
+ case MYSQL_TYPE_VAR_STRING: return &type_handler_var_string;
case MYSQL_TYPE_STRING: return &type_handler_string;
case MYSQL_TYPE_ENUM: return &type_handler_enum;
case MYSQL_TYPE_SET: return &type_handler_set;
@@ -2008,8 +2232,7 @@ Type_handler::get_handler_by_real_type(enum_field_types type)
case MYSQL_TYPE_DATETIME2: return &type_handler_datetime2;
case MYSQL_TYPE_NEWDATE: return &type_handler_newdate;
};
- DBUG_ASSERT(0);
- return &type_handler_string;
+ return NULL;
}
@@ -2078,7 +2301,8 @@ Type_handler_int_result::make_num_distinct_aggregator_field(MEM_ROOT *mem_root,
/***********************************************************************/
-Field *Type_handler_tiny::make_conversion_table_field(TABLE *table,
+Field *Type_handler_tiny::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
@@ -2089,84 +2313,91 @@ Field *Type_handler_tiny::make_conversion_table_field(TABLE *table,
using conversions so it should be true also when using conversions.
*/
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
- return new (table->in_use->mem_root)
+ return new (root)
Field_tiny(NULL, 4 /*max_length*/, (uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
-Field *Type_handler_short::make_conversion_table_field(TABLE *table,
+Field *Type_handler_short::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
- return new (table->in_use->mem_root)
+ return new (root)
Field_short(NULL, 6 /*max_length*/, (uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
-Field *Type_handler_int24::make_conversion_table_field(TABLE *table,
+Field *Type_handler_int24::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
- return new (table->in_use->mem_root)
+ return new (root)
Field_medium(NULL, 9 /*max_length*/, (uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
-Field *Type_handler_long::make_conversion_table_field(TABLE *table,
+Field *Type_handler_long::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
- return new (table->in_use->mem_root)
+ return new (root)
Field_long(NULL, 11 /*max_length*/, (uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
-Field *Type_handler_longlong::make_conversion_table_field(TABLE *table,
+Field *Type_handler_longlong::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
bool unsigned_flag= ((Field_num*) target)->unsigned_flag;
- return new (table->in_use->mem_root)
+ return new (root)
Field_longlong(NULL, 20 /*max_length*/,(uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*zerofill*/, unsigned_flag);
}
-Field *Type_handler_float::make_conversion_table_field(TABLE *table,
+Field *Type_handler_float::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_float(NULL, 12 /*max_length*/, (uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/);
}
-Field *Type_handler_double::make_conversion_table_field(TABLE *table,
+Field *Type_handler_double::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_double(NULL, 22 /*max_length*/, (uchar *) "", 1, Field::NONE,
&empty_clex_str, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/);
}
-Field *Type_handler_newdecimal::make_conversion_table_field(TABLE *table,
+Field *Type_handler_newdecimal::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
@@ -2175,13 +2406,14 @@ Field *Type_handler_newdecimal::make_conversion_table_field(TABLE *table,
uint8 decimals= metadata & 0x00ff;
uint32 max_length= my_decimal_precision_to_length(precision, decimals, false);
DBUG_ASSERT(decimals <= DECIMAL_MAX_SCALE);
- return new (table->in_use->mem_root)
+ return new (root)
Field_new_decimal(NULL, max_length, (uchar *) "", 1, Field::NONE,
&empty_clex_str, decimals, 0/*zerofill*/, 0/*unsigned*/);
}
-Field *Type_handler_olddecimal::make_conversion_table_field(TABLE *table,
+Field *Type_handler_olddecimal::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
@@ -2198,153 +2430,169 @@ Field *Type_handler_olddecimal::make_conversion_table_field(TABLE *table,
}
-Field *Type_handler_year::make_conversion_table_field(TABLE *table,
+Field *Type_handler_year::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_year(NULL, 4, (uchar *) "", 1, Field::NONE, &empty_clex_str);
}
-Field *Type_handler_null::make_conversion_table_field(TABLE *table,
+Field *Type_handler_null::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_null(NULL, 0, Field::NONE, &empty_clex_str, target->charset());
}
-Field *Type_handler_timestamp::make_conversion_table_field(TABLE *table,
+Field *Type_handler_timestamp::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new_Field_timestamp(table->in_use->mem_root, NULL, (uchar *) "", 1,
- Field::NONE, &empty_clex_str, table->s, target->decimals());
+ return new_Field_timestamp(root, NULL, (uchar *) "", 1,
+ Field::NONE, &empty_clex_str,
+ table->s, target->decimals());
}
-Field *Type_handler_timestamp2::make_conversion_table_field(TABLE *table,
+Field *Type_handler_timestamp2::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_timestampf(NULL, (uchar *) "", 1, Field::NONE,
&empty_clex_str, table->s, metadata);
}
-Field *Type_handler_newdate::make_conversion_table_field(TABLE *table,
+Field *Type_handler_newdate::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_newdate(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str);
}
-Field *Type_handler_date::make_conversion_table_field(TABLE *table,
+Field *Type_handler_date::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_date(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str);
}
-Field *Type_handler_time::make_conversion_table_field(TABLE *table,
+Field *Type_handler_time::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new_Field_time(table->in_use->mem_root, NULL, (uchar *) "", 1,
+ return new_Field_time(root, NULL, (uchar *) "", 1,
Field::NONE, &empty_clex_str, target->decimals());
}
-Field *Type_handler_time2::make_conversion_table_field(TABLE *table,
+Field *Type_handler_time2::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_timef(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, metadata);
}
-Field *Type_handler_datetime::make_conversion_table_field(TABLE *table,
+Field *Type_handler_datetime::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new_Field_datetime(table->in_use->mem_root, NULL, (uchar *) "", 1,
+ return new_Field_datetime(root, NULL, (uchar *) "", 1,
Field::NONE, &empty_clex_str, target->decimals());
}
-Field *Type_handler_datetime2::make_conversion_table_field(TABLE *table,
+Field *Type_handler_datetime2::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_datetimef(NULL, (uchar *) "", 1,
Field::NONE, &empty_clex_str, metadata);
}
-Field *Type_handler_bit::make_conversion_table_field(TABLE *table,
+Field *Type_handler_bit::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
DBUG_ASSERT((metadata & 0xff) <= 7);
uint32 max_length= 8 * (metadata >> 8U) + (metadata & 0x00ff);
- return new(table->in_use->mem_root)
+ return new(root)
Field_bit_as_char(NULL, max_length, (uchar *) "", 1,
Field::NONE, &empty_clex_str);
}
-Field *Type_handler_string::make_conversion_table_field(TABLE *table,
+Field *Type_handler_string::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
/* This is taken from Field_string::unpack. */
uint32 max_length= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff);
- return new(table->in_use->mem_root)
+ return new(root)
Field_string(NULL, max_length, (uchar *) "", 1,
Field::NONE, &empty_clex_str, target->charset());
}
-Field *Type_handler_varchar::make_conversion_table_field(TABLE *table,
+Field *Type_handler_varchar::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(metadata) <= MAX_FIELD_VARCHARLENGTH);
- return new(table->in_use->mem_root)
+ return new(root)
Field_varstring(NULL, metadata, HA_VARCHAR_PACKLENGTH(metadata),
(uchar *) "", 1, Field::NONE, &empty_clex_str,
table->s, target->charset());
}
-Field *Type_handler_varchar_compressed::make_conversion_table_field(TABLE *table,
+Field *Type_handler_varchar_compressed::make_conversion_table_field(
+ MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
- return new(table->in_use->mem_root)
+ return new(root)
Field_varstring_compressed(NULL, metadata,
HA_VARCHAR_PACKLENGTH(metadata),
(uchar *) "", 1, Field::NONE,
@@ -2355,7 +2603,9 @@ Field *Type_handler_varchar_compressed::make_conversion_table_field(TABLE *table
-Field *Type_handler_blob_compressed::make_conversion_table_field(TABLE *table,
+Field *Type_handler_blob_compressed::make_conversion_table_field(
+ MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
@@ -2363,7 +2613,7 @@ Field *Type_handler_blob_compressed::make_conversion_table_field(TABLE *table,
uint pack_length= metadata & 0x00ff;
if (pack_length < 1 || pack_length > 4)
return NULL; // Broken binary log?
- return new(table->in_use->mem_root)
+ return new(root)
Field_blob_compressed(NULL, (uchar *) "", 1, Field::NONE,
&empty_clex_str,
table->s, pack_length, target->charset(),
@@ -2371,43 +2621,15 @@ Field *Type_handler_blob_compressed::make_conversion_table_field(TABLE *table,
}
-#ifdef HAVE_SPATIAL
-const Name Type_handler_geometry::m_name_geometry(STRING_WITH_LEN("geometry"));
-
-
-const Type_handler *Type_handler_geometry::type_handler_for_comparison() const
-{
- return &type_handler_geometry;
-}
-
-
-Field *Type_handler_geometry::make_conversion_table_field(TABLE *table,
- uint metadata,
- const Field *target)
- const
-{
- DBUG_ASSERT(target->type() == MYSQL_TYPE_GEOMETRY);
- /*
- We do not do not update feature_gis statistics here:
- status_var_increment(target->table->in_use->status_var.feature_gis);
- as this is only a temporary field.
- The statistics was already incremented when "target" was created.
- */
- return new(table->in_use->mem_root)
- Field_geom(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 4,
- ((const Field_geom*) target)->geom_type,
- ((const Field_geom*) target)->srid);
-}
-#endif
-
-Field *Type_handler_enum::make_conversion_table_field(TABLE *table,
+Field *Type_handler_enum::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
DBUG_ASSERT(target->type() == MYSQL_TYPE_STRING);
DBUG_ASSERT(target->real_type() == MYSQL_TYPE_ENUM);
- return new(table->in_use->mem_root)
+ return new(root)
Field_enum(NULL, target->field_length,
(uchar *) "", 1, Field::NONE, &empty_clex_str,
metadata & 0x00ff/*pack_length()*/,
@@ -2415,14 +2637,15 @@ Field *Type_handler_enum::make_conversion_table_field(TABLE *table,
}
-Field *Type_handler_set::make_conversion_table_field(TABLE *table,
+Field *Type_handler_set::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
{
DBUG_ASSERT(target->type() == MYSQL_TYPE_STRING);
DBUG_ASSERT(target->real_type() == MYSQL_TYPE_SET);
- return new(table->in_use->mem_root)
+ return new(root)
Field_set(NULL, target->field_length,
(uchar *) "", 1, Field::NONE, &empty_clex_str,
metadata & 0x00ff/*pack_length()*/,
@@ -2430,6 +2653,27 @@ Field *Type_handler_set::make_conversion_table_field(TABLE *table,
}
+Field *Type_handler_enum::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ const Typelib *typelib= def.typelib();
+ DBUG_ASSERT(typelib);
+ /*
+ Assume I_S columns don't have non-ASCII characters in names.
+ If we eventually want to, Typelib::max_char_length() must be implemented.
+ */
+ return new (root)
+ Field_enum(addr.ptr(), (uint32) typelib->max_octet_length(),
+ addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name,
+ get_enum_pack_length(typelib->count),
+ typelib, system_charset_info);
+
+}
+
+
/*************************************************************************/
bool Type_handler::
@@ -2441,6 +2685,108 @@ bool Type_handler::
/*************************************************************************/
+
+bool
+Type_handler::Column_definition_set_attributes(THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const
+{
+ def->charset= cs;
+ def->set_length_and_dec(attr);
+ return false;
+}
+
+
+/*
+ In sql_mode=ORACLE, real size of VARCHAR and CHAR with no length
+ in SP parameters is fixed at runtime with the length of real args.
+ Let's translate VARCHAR to VARCHAR(4000) for return value.
+
+ Since Oracle 9, maximum size for VARCHAR in PL/SQL is 32767.
+
+ In MariaDB the limit for VARCHAR is 65535 bytes.
+ We could translate VARCHAR with no length to VARCHAR(65535), but
+ it would mean that for multi-byte character sets we'd have to translate
+ VARCHAR to MEDIUMTEXT, to guarantee 65535 characters.
+
+ Also we could translate VARCHAR to VARCHAR(16383), where 16383 is
+ the maximum possible length in characters in case of mbmaxlen=4
+ (e.g. utf32, utf16, utf8mb4). However, we'll have character sets with
+ mbmaxlen=5 soon (e.g. gb18030).
+*/
+
+bool
+Type_handler_string::Column_definition_set_attributes(
+ THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const
+{
+ Type_handler::Column_definition_set_attributes(thd, def, attr, cs, type);
+ if (attr.length())
+ return false;
+ switch (type) {
+ case COLUMN_DEFINITION_ROUTINE_PARAM:
+ case COLUMN_DEFINITION_FUNCTION_RETURN:
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ {
+ // See Type_handler_varchar::Column_definition_set_attributes()
+ def->length= def->decimals= 2000;
+ def->set_handler(&type_handler_varchar);
+ return false;
+ }
+ break;
+ case COLUMN_DEFINITION_ROUTINE_LOCAL:
+ case COLUMN_DEFINITION_TABLE_FIELD:
+ break;
+ }
+ def->length= 1;
+ return false;
+}
+
+
+bool
+Type_handler_varchar::Column_definition_set_attributes(
+ THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const
+{
+ Type_handler::Column_definition_set_attributes(thd, def, attr, cs, type);
+ if (attr.length())
+ return false;
+ switch (type) {
+ case COLUMN_DEFINITION_ROUTINE_PARAM:
+ case COLUMN_DEFINITION_FUNCTION_RETURN:
+ if (thd->variables.sql_mode & MODE_ORACLE)
+ {
+ /*
+ Type_handler_varchar::adjust_spparam_type() tests "decimals"
+ to detect if the formal parameter length needs to be adjusted to
+ the actual parameter length. Non-zero decimals means that the length
+ was set implicitly to the default value and needs to be adjusted.
+ */
+ def->length= def->decimals= 4000;
+ return false;
+ }
+ break;
+ case COLUMN_DEFINITION_ROUTINE_LOCAL:
+ case COLUMN_DEFINITION_TABLE_FIELD:
+ break;
+ }
+ thd->parse_error();
+ return true;
+}
+
+
+/*************************************************************************/
bool Type_handler_null::
Column_definition_fix_attributes(Column_definition *def) const
{
@@ -2520,14 +2866,6 @@ bool Type_handler_blob_common::
return def->check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_BLOBLENGTH);
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Column_definition_fix_attributes(Column_definition *def) const
-{
- def->flags|= BLOB_FLAG;
- return false;
-}
-#endif
bool Type_handler_year::
Column_definition_fix_attributes(Column_definition *def) const
@@ -2601,15 +2939,6 @@ bool Type_handler_bit::
/*************************************************************************/
-void Type_handler_blob_common::
- Column_definition_reuse_fix_attributes(THD *thd,
- Column_definition *def,
- const Field *field) const
-{
- DBUG_ASSERT(def->key_length == 0);
-}
-
-
void Type_handler_typelib::
Column_definition_reuse_fix_attributes(THD *thd,
Column_definition *def,
@@ -2620,18 +2949,6 @@ void Type_handler_typelib::
}
-#ifdef HAVE_SPATIAL
-void Type_handler_geometry::
- Column_definition_reuse_fix_attributes(THD *thd,
- Column_definition *def,
- const Field *field) const
-{
- def->geom_type= ((Field_geom*) field)->geom_type;
- def->srid= ((Field_geom*) field)->srid;
-}
-#endif
-
-
void Type_handler_year::
Column_definition_reuse_fix_attributes(THD *thd,
Column_definition *def,
@@ -2741,20 +3058,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Column_definition_prepare_stage1(THD *thd,
- MEM_ROOT *mem_root,
- Column_definition *def,
- handler *file,
- ulonglong table_flags) const
-{
- def->create_length_to_internal_length_string();
- return def->prepare_blob_field(thd);
-}
-#endif
-
-
/*************************************************************************/
bool Type_handler::
@@ -2857,8 +3160,7 @@ bool Type_handler::
Column_definition_prepare_stage2_legacy_num(Column_definition *def,
enum_field_types type) const
{
- def->pack_flag= def->pack_flag_numeric(def->decimals) |
- f_settype((uint) type);
+ def->pack_flag= def->pack_flag_numeric() | f_settype((uint) type);
return false;
}
@@ -2874,7 +3176,8 @@ bool Type_handler::
*/
if (dec >= FLOATING_POINT_DECIMALS)
dec= FLOATING_POINT_DECIMALS;
- def->pack_flag= def->pack_flag_numeric(dec) | f_settype((uint) type);
+ def->decimals= dec;
+ def->pack_flag= def->pack_flag_numeric() | f_settype((uint) type);
return false;
}
@@ -2883,7 +3186,7 @@ bool Type_handler_newdecimal::
handler *file,
ulonglong table_flags) const
{
- def->pack_flag= def->pack_flag_numeric(def->decimals);
+ def->pack_flag= def->pack_flag_numeric();
return false;
}
@@ -2895,21 +3198,6 @@ bool Type_handler_blob_common::
return def->prepare_stage2_blob(file, table_flags, FIELDFLAG_BLOB);
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Column_definition_prepare_stage2(Column_definition *def,
- handler *file,
- ulonglong table_flags) const
-{
- if (!(table_flags & HA_CAN_GEOMETRY))
- {
- my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY");
- return true;
- }
- return def->prepare_stage2_blob(file, table_flags, FIELDFLAG_GEOM);
-}
-#endif
-
bool Type_handler_varchar::
Column_definition_prepare_stage2(Column_definition *def,
handler *file,
@@ -2965,6 +3253,93 @@ bool Type_handler_bit::
return false;
}
+
+/*************************************************************************/
+bool Type_handler::Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return false;
+}
+
+
+bool Type_handler::Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *has_field_needed) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return false;
+}
+
+
+bool Type_handler::Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return false;
+}
+
+
+bool Type_handler::Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return false;
+}
+
+
+bool Type_handler::Key_part_spec_init_spatial(Key_part_spec *part,
+ const Column_definition &def)
+ const
+{
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
+ return true;
+}
+
+
+bool Type_handler_blob_common::Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return part->check_primary_key_for_blob(file);
+}
+
+
+bool Type_handler_blob_common::Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *hash_field_needed) const
+{
+ if (!(part->length*= def.charset->mbmaxlen))
+ *hash_field_needed= true;
+ return part->check_key_for_blob(file);
+}
+
+
+bool Type_handler_blob_common::Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return part->init_multiple_key_for_blob(file);
+}
+
+
+bool Type_handler_blob_common::Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length*= def.charset->mbmaxlen;
+ return part->check_foreign_key_for_blob(file);
+}
+
+
+
/*************************************************************************/
uint32 Type_handler_time::calc_pack_length(uint32 length) const
@@ -3023,13 +3398,6 @@ uint32 Type_handler_long_blob::calc_pack_length(uint32 length) const
return 4 + portable_sizeof_char_ptr;
}
-#ifdef HAVE_SPATIAL
-uint32 Type_handler_geometry::calc_pack_length(uint32 length) const
-{
- return 4 + portable_sizeof_char_ptr;
-}
-#endif
-
uint32 Type_handler_newdecimal::calc_pack_length(uint32 length) const
{
abort(); // This shouldn't happen
@@ -3050,87 +3418,83 @@ uint32 Type_handler_enum::calc_pack_length(uint32 length) const
/*************************************************************************/
-Field *Type_handler::make_and_init_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
+uint Type_handler::calc_key_length(const Column_definition &def) const
{
- Field *field= make_table_field(name, addr, attr, table);
- if (field)
- field->init(table);
- return field;
+ DBUG_ASSERT(def.pack_length == calc_pack_length((uint32) def.length));
+ return def.pack_length;
}
-
-Field *Type_handler_tiny::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
+uint Type_handler_bit::calc_key_length(const Column_definition &def) const
{
- return new (table->in_use->mem_root)
- Field_tiny(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
+ if (f_bit_as_char(def.pack_flag))
+ return def.pack_length;
+ /* We need one extra byte to store the bits we save among the null bits */
+ return def.pack_length + MY_TEST(def.length & 7);
}
-
-Field *Type_handler_short::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
-
+uint Type_handler_newdecimal::calc_key_length(const Column_definition &def) const
{
- return new (table->in_use->mem_root)
- Field_short(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
+ return def.pack_length;
}
-
-Field *Type_handler_int24::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
+uint
+Type_handler_string_result::calc_key_length(const Column_definition &def) const
{
- return new (table->in_use->mem_root)
- Field_medium(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name,
- 0/*zerofill*/, attr.unsigned_flag);
+ return (uint) def.length;
}
+uint Type_handler_enum::calc_key_length(const Column_definition &def) const
+{
+ DBUG_ASSERT(def.interval);
+ return get_enum_pack_length(def.interval->count);
+}
-Field *Type_handler_long::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
+uint Type_handler_set::calc_key_length(const Column_definition &def) const
{
- return new (table->in_use->mem_root)
- Field_long(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag);
+ DBUG_ASSERT(def.interval);
+ return get_set_pack_length(def.interval->count);
}
+uint Type_handler_blob_common::calc_key_length(const Column_definition &def) const
+{
+ return 0;
+}
-Field *Type_handler_longlong::make_table_field(const LEX_CSTRING *name,
+/*************************************************************************/
+Field *Type_handler::make_and_init_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const
{
- return new (table->in_use->mem_root)
- Field_longlong(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name,
- 0/*zerofill*/, attr.unsigned_flag);
+ Field *field= make_table_field(root, name, addr, attr, table->s);
+ if (field)
+ field->init(table);
+ return field;
}
-Field *Type_handler_vers_trx_id::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_int_result::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE_SHARE *share) const
+{
+ DBUG_ASSERT(is_unsigned() == attr.unsigned_flag);
+ Column_definition_attributes dattr(attr);
+ return make_table_field_from_def(share, root, name, addr,
+ Bit_addr(), &dattr, 0);
+}
+
+
+Field *Type_handler_vers_trx_id::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ DBUG_ASSERT(is_unsigned() == attr.unsigned_flag);
+ return new (root)
Field_vers_trx_id(addr.ptr(), attr.max_char_length(),
addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
@@ -3138,37 +3502,25 @@ Field *Type_handler_vers_trx_id::make_table_field(const LEX_CSTRING *name,
}
-Field *Type_handler_float::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
-{
- return new (table->in_use->mem_root)
- Field_float(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name,
- (uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag);
-}
-
-
-Field *Type_handler_double::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
+Field *
+Type_handler_real_result::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
- Field_double(addr.ptr(), attr.max_char_length(),
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name,
- (uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag);
+ Column_definition_attributes dattr(attr);
+ return make_table_field_from_def(share, root, name, addr,
+ Bit_addr(), &dattr, 0);
}
Field *
-Type_handler_olddecimal::make_table_field(const LEX_CSTRING *name,
+Type_handler_olddecimal::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
/*
Currently make_table_field() is used for Item purpose only.
@@ -3178,19 +3530,18 @@ Type_handler_olddecimal::make_table_field(const LEX_CSTRING *name,
in make_field() in field.cc, to open old tables with old decimal.
*/
DBUG_ASSERT(0);
- return new (table->in_use->mem_root)
- Field_decimal(addr.ptr(), attr.max_length,
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, (uint8) attr.decimals,
- 0/*zerofill*/,attr.unsigned_flag);
+ Column_definition_attributes dattr(attr);
+ return make_table_field_from_def(share, root, name, addr,
+ Bit_addr(), &dattr, 0);
}
Field *
-Type_handler_newdecimal::make_table_field(const LEX_CSTRING *name,
+Type_handler_newdecimal::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
uint8 dec= (uint8) attr.decimals;
uint8 intg= (uint8) (attr.decimal_precision() - dec);
@@ -3226,81 +3577,74 @@ Type_handler_newdecimal::make_table_field(const LEX_CSTRING *name,
/* Corrected value fits. */
len= required_length;
}
- return new (table->in_use->mem_root)
+ return new (root)
Field_new_decimal(addr.ptr(), len, addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
dec, 0/*zerofill*/, attr.unsigned_flag);
}
-Field *Type_handler_year::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_null::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
-{
- return new (table->in_use->mem_root)
- Field_year(addr.ptr(), attr.max_length,
- addr.null_ptr(), addr.null_bit(),
- Field::NONE, name);
-}
-
-
-Field *Type_handler_null::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_null(addr.ptr(), attr.max_length,
Field::NONE, name, attr.collation.collation);
}
-Field *Type_handler_timestamp::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_timestamp::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new_Field_timestamp(table->in_use->mem_root,
+ return new_Field_timestamp(root,
addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s, attr.decimals);
+ Field::NONE, name, share, attr.decimals);
}
-Field *Type_handler_timestamp2::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_timestamp2::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
/*
Will be changed to "new Field_timestampf" when we reuse
make_table_field() for make_field() purposes in field.cc.
*/
- return new_Field_timestamp(table->in_use->mem_root,
+ return new_Field_timestamp(root,
addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s, attr.decimals);
+ Field::NONE, name, share, attr.decimals);
}
-Field *Type_handler_newdate::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_newdate::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_newdate(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
-Field *Type_handler_date::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_date::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
/*
@@ -3308,28 +3652,30 @@ Field *Type_handler_date::make_table_field(const LEX_CSTRING *name,
for make_field() in field.cc
*/
DBUG_ASSERT(0);
- return new (table->in_use->mem_root)
+ return new (root)
Field_date(addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
-Field *Type_handler_time::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_time::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new_Field_time(table->in_use->mem_root,
+ return new_Field_time(root,
addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
-Field *Type_handler_time2::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_time2::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
@@ -3337,159 +3683,153 @@ Field *Type_handler_time2::make_table_field(const LEX_CSTRING *name,
Will be changed to "new Field_timef" when we reuse
make_table_field() for make_field() purposes in field.cc.
*/
- return new_Field_time(table->in_use->mem_root,
+ return new_Field_time(root,
addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
-Field *Type_handler_datetime::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_datetime::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new_Field_datetime(table->in_use->mem_root,
+ return new_Field_datetime(root,
addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
-Field *Type_handler_datetime2::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_datetime2::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
/*
Will be changed to "new Field_datetimef" when we reuse
make_table_field() for make_field() purposes in field.cc.
*/
- return new_Field_datetime(table->in_use->mem_root,
+ return new_Field_datetime(root,
addr.ptr(), addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.decimals);
}
-Field *Type_handler_bit::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_bit::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_bit_as_char(addr.ptr(), attr.max_length,
addr.null_ptr(), addr.null_bit(),
Field::NONE, name);
}
-Field *Type_handler_string::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_string::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_string(addr.ptr(), attr.max_length,
addr.null_ptr(), addr.null_bit(),
Field::NONE, name, attr.collation);
}
-Field *Type_handler_varchar::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_varchar::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(attr.max_length) <=
MAX_FIELD_VARCHARLENGTH);
- return new (table->in_use->mem_root)
+ return new (root)
Field_varstring(addr.ptr(), attr.max_length,
HA_VARCHAR_PACKLENGTH(attr.max_length),
addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
- table->s, attr.collation);
+ share, attr.collation);
}
-Field *Type_handler_tiny_blob::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_tiny_blob::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s,
+ Field::NONE, name, share,
1, attr.collation);
}
-Field *Type_handler_blob::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_blob::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s,
+ Field::NONE, name, share,
2, attr.collation);
}
Field *
-Type_handler_medium_blob::make_table_field(const LEX_CSTRING *name,
+Type_handler_medium_blob::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s,
+ Field::NONE, name, share,
3, attr.collation);
}
-Field *Type_handler_long_blob::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_long_blob::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- return new (table->in_use->mem_root)
+ return new (root)
Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s,
+ Field::NONE, name, share,
4, attr.collation);
}
-
-#ifdef HAVE_SPATIAL
-Field *Type_handler_geometry::make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const
-{
- return new (table->in_use->mem_root)
- Field_geom(addr.ptr(), addr.null_ptr(), addr.null_bit(),
- Field::NONE, name, table->s, 4,
- (Field::geometry_type) attr.uint_geometry_type(),
- 0);
-}
-#endif
-
-
-Field *Type_handler_enum::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_enum::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- TYPELIB *typelib= attr.get_typelib();
+ const TYPELIB *typelib= attr.get_typelib();
DBUG_ASSERT(typelib);
- return new (table->in_use->mem_root)
+ return new (root)
Field_enum(addr.ptr(), attr.max_length,
addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
@@ -3498,15 +3838,16 @@ Field *Type_handler_enum::make_table_field(const LEX_CSTRING *name,
}
-Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
+Field *Type_handler_set::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const
{
- TYPELIB *typelib= attr.get_typelib();
+ const TYPELIB *typelib= attr.get_typelib();
DBUG_ASSERT(typelib);
- return new (table->in_use->mem_root)
+ return new (root)
Field_set(addr.ptr(), attr.max_length,
addr.null_ptr(), addr.null_bit(),
Field::NONE, name,
@@ -3514,6 +3855,177 @@ Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
attr.collation);
}
+
+/*************************************************************************/
+
+Field *Type_handler_float::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_float(addr.ptr(), def.char_length(),
+ addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name,
+ (uint8) NOT_FIXED_DEC,
+ 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_double::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_double(addr.ptr(), def.char_length(),
+ addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name,
+ (uint8) NOT_FIXED_DEC,
+ 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_decimal_result::make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ uint dec= def.decimal_scale();
+ uint prec= def.decimal_precision();
+ DBUG_ASSERT(dec <= DECIMAL_MAX_SCALE);
+ uint32 len= my_decimal_precision_to_length(prec, dec, def.unsigned_flag());
+ return new (root)
+ Field_new_decimal(addr.ptr(), len, addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name,
+ (uint8) dec, 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_blob_common::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name, table->s,
+ length_bytes(),
+ &my_charset_bin);
+}
+
+
+Field *Type_handler_varchar::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ DBUG_ASSERT(def.char_length());
+ LEX_CSTRING name= def.name();
+ uint32 octet_length= (uint32) def.char_length() * 3;
+ if (octet_length > MAX_FIELD_VARCHARLENGTH)
+ {
+ Field *field= new (root)
+ Field_blob(addr.ptr(), addr.null_ptr(), addr.null_bit(), Field::NONE,
+ &name, table->s, 4, system_charset_info);
+ if (field)
+ field->field_length= octet_length;
+ return field;
+ }
+ else
+ {
+ return new (root)
+ Field_varstring(addr.ptr(), octet_length,
+ HA_VARCHAR_PACKLENGTH(octet_length),
+ addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name,
+ table->s, system_charset_info);
+ }
+}
+
+
+Field *Type_handler_tiny::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_tiny(addr.ptr(), def.char_length(),
+ addr.null_ptr(), addr.null_bit(), Field::NONE, &name,
+ 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_short::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_short(addr.ptr(), def.char_length(),
+ addr.null_ptr(), addr.null_bit(), Field::NONE, &name,
+ 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_long::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_long(addr.ptr(), def.char_length(),
+ addr.null_ptr(), addr.null_bit(), Field::NONE, &name,
+ 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_longlong::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_longlong(addr.ptr(), def.char_length(),
+ addr.null_ptr(), addr.null_bit(), Field::NONE, &name,
+ 0/*zerofill*/, def.unsigned_flag());
+}
+
+
+Field *Type_handler_date_common::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root)
+ Field_newdate(addr.ptr(), addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name);
+}
+
+
+Field *Type_handler_time_common::make_schema_field(MEM_ROOT *root, TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new_Field_time(root,
+ addr.ptr(), addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name, def.fsp());
+}
+
+
+Field *Type_handler_datetime_common::make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+{
+ LEX_CSTRING name= def.name();
+ return new (root) Field_datetimef(addr.ptr(),
+ addr.null_ptr(), addr.null_bit(),
+ Field::NONE, &name, def.fsp());
+}
+
+
/*************************************************************************/
/*
@@ -3569,13 +4081,6 @@ uint32 Type_handler_bit::max_display_length(const Item *item) const
return item->max_length;
}
-
-uint32 Type_handler_general_purpose_int::max_display_length(const Item *item)
- const
-{
- return type_limits_int_by_unsigned_flag(item->unsigned_flag)->char_length();
-}
-
/*************************************************************************/
uint32
@@ -3606,7 +4111,7 @@ uint32
Type_handler_general_purpose_int::Item_decimal_notation_int_digits(
const Item *item) const
{
- return type_limits_int_by_unsigned_flag(item->unsigned_flag)->precision();
+ return type_limits_int()->precision();
}
/*************************************************************************/
@@ -3659,6 +4164,19 @@ void Type_handler_temporal_with_date::Item_update_null_value(Item *item) const
(void) item->get_date(thd, &ltime, Datetime::Options(thd));
}
+bool
+Type_handler_timestamp_common::
+Column_definition_set_attributes(THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type) const
+{
+ Type_handler::Column_definition_set_attributes(thd, def, attr, cs, type);
+ if (!opt_explicit_defaults_for_timestamp)
+ def->flags|= NOT_NULL_FLAG;
+ return false;
+}
void Type_handler_string_result::Item_update_null_value(Item *item) const
{
@@ -3846,7 +4364,7 @@ bool Type_handler_string_result::
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,
+ ('o-umlaut' is not equal to 'oe' in utf8mb3_general_ci,
which is the collation of the field "a").
If we change the right part from:
@@ -3987,11 +4505,14 @@ bool Type_handler_int_result::
{
// Convert a mixture of signed and unsigned int to decimal
handler->set_handler(&type_handler_newdecimal);
- func->aggregate_attributes_decimal(items, nitems);
+ func->aggregate_attributes_decimal(items, nitems, false);
return false;
}
}
func->aggregate_attributes_int(items, nitems);
+ handler->set_handler(func->unsigned_flag ?
+ handler->type_handler()->type_handler_unsigned() :
+ handler->type_handler()->type_handler_signed());
return false;
}
@@ -4015,7 +4536,8 @@ bool Type_handler_decimal_result::
Type_all_attributes *func,
Item **items, uint nitems) const
{
- func->aggregate_attributes_decimal(items, nitems);
+ uint unsigned_count= func->count_unsigned(items, nitems);
+ func->aggregate_attributes_decimal(items, nitems, unsigned_count == nitems);
return false;
}
@@ -4043,10 +4565,10 @@ bool Type_handler_typelib::
Type_all_attributes *func,
Item **items, uint nitems) const
{
- TYPELIB *typelib= NULL;
+ const TYPELIB *typelib= NULL;
for (uint i= 0; i < nitems; i++)
{
- TYPELIB *typelib2;
+ const TYPELIB *typelib2;
if ((typelib2= items[i]->get_typelib()))
{
if (typelib)
@@ -4129,29 +4651,6 @@ bool Type_handler_timestamp_common::
return false;
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_hybrid_func_fix_attributes(THD *thd,
- const char *func_name,
- Type_handler_hybrid_field_type *handler,
- Type_all_attributes *func,
- Item **items, uint nitems) const
-{
- DBUG_ASSERT(nitems > 0);
- Type_geometry_attributes gattr(items[0]->type_handler(), items[0]);
- for (uint i= 1; i < nitems; i++)
- gattr.join(items[i]);
- func->set_geometry_type(gattr.get_geometry_type());
- func->collation.set(&my_charset_bin);
- func->unsigned_flag= false;
- func->decimals= 0;
- func->max_length= (uint32) UINT_MAX32;
- func->set_maybe_null(true);
- return false;
-}
-#endif
-
-
/*************************************************************************/
bool Type_handler::
@@ -4307,7 +4806,15 @@ bool Type_handler_real_result::
bool Type_handler_int_result::
Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
{
- return func->fix_length_and_dec_numeric(&type_handler_longlong);
+ /*
+ "this" is equal func->args[0]->type_handler() here, e.g. for MIN()/MAX().
+ func->unsigned_flag is not reliably set yet.
+ It will be set by the call below (copied from args[0]).
+ */
+ const Type_handler *h= is_unsigned()
+ ? (Type_handler *)&type_handler_ulonglong
+ : (Type_handler *)&type_handler_slonglong;
+ return func->fix_length_and_dec_numeric(h);
}
@@ -4390,13 +4897,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
-{
- return Item_func_or_sum_illegal_param("sum");
-}
-#endif
/*************************************************************************/
@@ -4441,13 +4941,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
-{
- return Item_func_or_sum_illegal_param("avg");
-}
-#endif
/*************************************************************************/
@@ -4492,15 +4985,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-#endif
-
-
/*************************************************************************/
bool Type_handler_real_result::Item_val_bool(Item *item) const
@@ -5202,9 +5686,7 @@ static int srtcmp_in(const void *cs_, const void *x_, const void *y_)
const CHARSET_INFO *cs= static_cast<const CHARSET_INFO *>(cs_);
const String *x= static_cast<const String *>(x_);
const String *y= static_cast<const String *>(y_);
- return cs->coll->strnncollsp(cs,
- (uchar *) x->ptr(),x->length(),
- (uchar *) y->ptr(),y->length());
+ return cs->strnncollsp(x->ptr(), x->length(), y->ptr(), y->length());
}
in_vector *Type_handler_string_result::make_in_vector(THD *thd,
@@ -5758,7 +6240,7 @@ bool Type_handler_int_result::
bool Type_handler_year::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
- item->fix_arg_int(&type_handler_long, item->arguments()[0], false); // 10.5 merge: fix to type_handler_ulong
+ item->fix_arg_int(&type_handler_ulong, item->arguments()[0], false);
return false;
}
@@ -5807,9 +6289,9 @@ bool Type_handler_decimal_result::
bool Type_handler_date_common::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
- static const Type_std_attributes attr(8, 0/*dec*/, true/*unsigned*/,
- DTCollation_numeric::singleton());
- item->fix_arg_int(&type_handler_long, &attr, false); // 10.5 merge: fix to *_ulong
+ static const Type_std_attributes attr(Type_numeric_attributes(8, 0, true),
+ DTCollation_numeric());
+ item->fix_arg_int(&type_handler_ulong, &attr, false);
return false;
}
@@ -5846,14 +6328,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_func_round_fix_length_and_dec(Item_func_round *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-#endif
-
/***************************************************************************/
bool Type_handler_row::
@@ -5877,7 +6351,7 @@ bool Type_handler_year::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
item->Type_std_attributes::set(item->arguments()[0]);
- item->set_handler(&type_handler_long);
+ item->set_handler(&type_handler_ulong);
return false;
}
@@ -5927,10 +6401,9 @@ bool Type_handler_decimal_result::
bool Type_handler_date_common::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
- static const Type_std_attributes attr(8, 0/*dec*/, true/*unsigned*/,
- DTCollation_numeric::singleton());
- item->Type_std_attributes::set(attr);
- item->set_handler(&type_handler_long); // 10.5 merge: fix to *_ulong
+ static const Type_numeric_attributes attr(8, 0/*dec*/, true/*unsigned*/);
+ item->Type_std_attributes::set(attr, DTCollation_numeric());
+ item->set_handler(&type_handler_ulong);
return false;
}
@@ -5967,14 +6440,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-#endif
-
/***************************************************************************/
bool Type_handler_row::
@@ -6025,14 +6490,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-#endif
-
/***************************************************************************/
bool Type_handler_row::
@@ -6083,15 +6540,6 @@ bool Type_handler_string_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-#endif
-
-
/***************************************************************************/
bool Type_handler::
@@ -6241,78 +6689,6 @@ bool Type_handler::
}
-#ifdef HAVE_SPATIAL
-
-bool Type_handler_geometry::
- Item_func_signed_fix_length_and_dec(Item_func_signed *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-bool Type_handler_geometry::
- Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-bool Type_handler_geometry::
- Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-bool Type_handler_geometry::
- Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-bool Type_handler_geometry::
- Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-bool Type_handler_geometry::
- Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const
-{
- if (item->cast_charset() != &my_charset_bin)
- return Item_func_or_sum_illegal_param(item); // CAST(geom AS CHAR)
- item->fix_length_and_dec_str();
- return false; // CAST(geom AS BINARY)
-}
-
-
-bool Type_handler_geometry::
- Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-
-bool Type_handler_geometry::
- Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const
-{
- return Item_func_or_sum_illegal_param(item);
-}
-
-
-bool Type_handler_geometry::
- Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item)
- const
-{
- return Item_func_or_sum_illegal_param(item);
-
-}
-
-#endif /* HAVE_SPATIAL */
-
/***************************************************************************/
bool Type_handler_row::
@@ -6560,6 +6936,24 @@ bool Type_handler_string_result::
/***************************************************************************/
+const Vers_type_handler* Type_handler_temporal_result::vers() const
+{
+ return &vers_type_timestamp;
+}
+
+const Vers_type_handler* Type_handler_string_result::vers() const
+{
+ return &vers_type_timestamp;
+}
+
+const Vers_type_handler* Type_handler_blob_common::vers() const
+
+{
+ return &vers_type_timestamp;
+}
+
+/***************************************************************************/
+
uint Type_handler::Item_time_precision(THD *thd, Item *item) const
{
return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS);
@@ -6931,21 +7325,6 @@ bool Type_handler_temporal_result::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
- Item_param_set_from_value(THD *thd,
- Item_param *param,
- const Type_all_attributes *attr,
- const st_value *val) const
-{
- param->unsigned_flag= false;
- param->setup_conversion_blob(thd);
- param->set_geometry_type(attr->uint_geometry_type());
- return param->set_str(val->m_string.ptr(), val->m_string.length(),
- &my_charset_bin, &my_charset_bin);
-}
-#endif
-
/***************************************************************************/
bool Type_handler_null::
@@ -7013,7 +7392,7 @@ bool Type_handler::
{
float nr= (float) item->val_real();
if (!item->null_value)
- return protocol->store(nr, item->decimals, &buf->m_string);
+ return protocol->store_float(nr, item->decimals);
return protocol->store_null();
}
@@ -7023,7 +7402,7 @@ bool Type_handler::
{
double nr= item->val_real();
if (!item->null_value)
- return protocol->store(nr, item->decimals, &buf->m_string);
+ return protocol->store_double(nr, item->decimals);
return protocol->store_null();
}
@@ -7113,10 +7492,10 @@ Item *Type_handler_string_result::
String *result= item->val_str(&tmp);
if (item->null_value)
return new (thd->mem_root) Item_null(thd, item->name.str);
- uint length= result->length();
- char *tmp_str= thd->strmake(result->ptr(), length);
- return new (thd->mem_root) Item_string(thd, item->name.str,
- tmp_str, length, result->charset());
+ LEX_CSTRING value;
+ thd->make_lex_string(&value, result->ptr(), result->length());
+ return new (thd->mem_root) Item_string(thd, item->name, value,
+ result->charset());
}
@@ -7466,7 +7845,8 @@ void Type_handler_datetime_common::Item_param_set_param_func(Item_param *param,
param->set_param_datetime(pos, len);
}
-Field *Type_handler_blob_common::make_conversion_table_field(TABLE *table,
+Field *Type_handler_blob_common::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target)
const
@@ -7474,7 +7854,7 @@ Field *Type_handler_blob_common::make_conversion_table_field(TABLE *table,
uint pack_length= metadata & 0x00ff;
if (pack_length < 1 || pack_length > 4)
return NULL; // Broken binary log?
- return new(table->in_use->mem_root)
+ return new(root)
Field_blob(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str,
table->s, pack_length, target->charset());
}
@@ -7504,15 +7884,6 @@ void Type_handler_typelib::Item_param_set_param_func(Item_param *param,
}
-#ifdef HAVE_SPATIAL
-void Type_handler_geometry::Item_param_set_param_func(Item_param *param,
- uchar **pos,
- ulong len) const
-{
- param->set_null(); // Not possible type code in the client-server protocol
-}
-#endif
-
/***************************************************************************/
Field *Type_handler_row::
@@ -7535,11 +7906,12 @@ Field *Type_handler_olddecimal::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(f_decimals(attr->pack_flag) == 0);
return new (mem_root)
Field_decimal(rec.ptr(), (uint32) attr->length,
rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name,
- f_decimals(attr->pack_flag),
+ (uint8) attr->decimals,
f_is_zerofill(attr->pack_flag) != 0,
f_is_dec(attr->pack_flag) == 0);
}
@@ -7552,11 +7924,12 @@ Field *Type_handler_newdecimal::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(f_decimals(attr->pack_flag) == 0);
return new (mem_root)
Field_new_decimal(rec.ptr(), (uint32) attr->length,
rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name,
- f_decimals(attr->pack_flag),
+ (uint8) attr->decimals,
f_is_zerofill(attr->pack_flag) != 0,
f_is_dec(attr->pack_flag) == 0);
}
@@ -7569,7 +7942,8 @@ Field *Type_handler_float::
const Column_definition_attributes *attr,
uint32 flags) const
{
- int decimals= f_decimals(attr->pack_flag);
+ DBUG_ASSERT(f_decimals(attr->pack_flag) == 0);
+ uint decimals= attr->decimals;
if (decimals == FLOATING_POINT_DECIMALS)
decimals= NOT_FIXED_DEC;
return new (mem_root)
@@ -7588,7 +7962,8 @@ Field *Type_handler_double::
const Column_definition_attributes *attr,
uint32 flags) const
{
- int decimals= f_decimals(attr->pack_flag);
+ DBUG_ASSERT(f_decimals(attr->pack_flag) == 0);
+ uint decimals= attr->decimals;
if (decimals == FLOATING_POINT_DECIMALS)
decimals= NOT_FIXED_DEC;
return new (mem_root)
@@ -7692,6 +8067,7 @@ Field *Type_handler_timestamp::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(attr->decimals == attr->temporal_dec(MAX_DATETIME_WIDTH));
return new_Field_timestamp(mem_root,
rec.ptr(), rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name, share,
@@ -7706,6 +8082,7 @@ Field *Type_handler_timestamp2::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(attr->decimals == attr->temporal_dec(MAX_DATETIME_WIDTH));
return new (mem_root)
Field_timestampf(rec.ptr(), rec.null_ptr(), rec.null_bit(),
attr->unireg_check,
@@ -7759,6 +8136,7 @@ Field *Type_handler_time::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(attr->decimals == attr->temporal_dec(MIN_TIME_WIDTH));
return new_Field_time(mem_root, rec.ptr(), rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name,
attr->temporal_dec(MIN_TIME_WIDTH));
@@ -7772,6 +8150,7 @@ Field *Type_handler_time2::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(attr->decimals == attr->temporal_dec(MIN_TIME_WIDTH));
return new (mem_root)
Field_timef(rec.ptr(), rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name,
@@ -7786,6 +8165,7 @@ Field *Type_handler_datetime::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(attr->decimals == attr->temporal_dec(MAX_DATETIME_WIDTH));
return new_Field_datetime(mem_root, rec.ptr(), rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name,
attr->temporal_dec(MAX_DATETIME_WIDTH));
@@ -7799,6 +8179,7 @@ Field *Type_handler_datetime2::
const Column_definition_attributes *attr,
uint32 flags) const
{
+ DBUG_ASSERT(attr->decimals == attr->temporal_dec(MAX_DATETIME_WIDTH));
return new (mem_root)
Field_datetimef(rec.ptr(), rec.null_ptr(), rec.null_bit(),
attr->unireg_check, name,
@@ -7836,21 +8217,6 @@ Field *Type_handler_bit::
}
-#ifdef HAVE_SPATIAL
-Field *Type_handler_geometry::
- make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root,
- const LEX_CSTRING *name,
- const Record_addr &rec, const Bit_addr &bit,
- const Column_definition_attributes *attr,
- uint32 flags) const
-{
- status_var_increment(current_thd->status_var.feature_gis);
- return new (mem_root)
- Field_geom(rec.ptr(), rec.null_ptr(), rec.null_bit(),
- attr->unireg_check, name, share,
- attr->pack_flag_to_pack_length(), attr->geom_type, attr->srid);
-}
-#endif
Field *Type_handler_string::
@@ -7948,16 +8314,110 @@ void Type_handler::
}
-#ifdef HAVE_SPATIAL
-void Type_handler_geometry::
+void Type_handler_real_result::
Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
uchar *buff) const
{
- def->frm_pack_basic(buff);
- buff[11]= 0;
- buff[14]= (uchar) def->geom_type;
+ def->frm_pack_numeric_with_dec(buff);
+}
+
+
+void Type_handler_decimal_result::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ def->frm_pack_numeric_with_dec(buff);
+}
+
+
+void Type_handler_int_result::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag) == 0);
+ DBUG_ASSERT(def->decimals == 0);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_date_common::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag) == 0);
+ DBUG_ASSERT(def->decimals == 0);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_bit::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag & ~FIELDFLAG_TREAT_BIT_AS_CHAR) == 0);
+ DBUG_ASSERT(def->decimals == 0);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_blob_common::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag & ~FIELDFLAG_BLOB) == 0);
+ DBUG_ASSERT(def->decimals == 0 ||
+ def->decimals == NOT_FIXED_DEC);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_null::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag) == 0);
+ DBUG_ASSERT(def->decimals == NOT_FIXED_DEC);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_string_result::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag) == 0);
+ DBUG_ASSERT(def->decimals == 0 || def->decimals == NOT_FIXED_DEC);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_enum::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag & ~FIELDFLAG_INTERVAL) == 0);
+ DBUG_ASSERT(def->decimals == 0);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_set::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag & ~FIELDFLAG_BITFIELD) == 0);
+ DBUG_ASSERT(def->decimals == 0);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
+}
+
+
+void Type_handler_temporal_result::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag) == 0);
+ Type_handler::Column_definition_attributes_frm_pack(def, buff);
}
-#endif
/***************************************************************************/
@@ -7974,89 +8434,60 @@ bool Type_handler::
}
-#ifdef HAVE_SPATIAL
-bool Type_handler_geometry::
+bool Type_handler_real_result::
Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
TABLE_SHARE *share,
const uchar *buffer,
LEX_CUSTRING *gis_options)
const
{
- uint gis_opt_read, gis_length, gis_decimals;
- Field_geom::storage_type st_type;
- attr->frm_unpack_basic(buffer);
- // charset and geometry_type share the same byte in frm
- attr->geom_type= (Field::geometry_type) buffer[14];
- gis_opt_read= gis_field_options_read(gis_options->str,
- gis_options->length,
- &st_type, &gis_length,
- &gis_decimals, &attr->srid);
- gis_options->str+= gis_opt_read;
- gis_options->length-= gis_opt_read;
- return false;
-}
-#endif
-
-/***************************************************************************/
-
-bool Type_handler::Vers_history_point_resolve_unit(THD *thd,
- Vers_history_point *point)
- const
-{
- /*
- Disallow using non-relevant data types in history points.
- Even expressions with explicit TRANSACTION or TIMESTAMP units.
- */
- point->bad_expression_data_type_error(name().ptr());
- return true;
-}
-
-
-bool Type_handler_typelib::
- Vers_history_point_resolve_unit(THD *thd,
- Vers_history_point *point) const
-{
- /*
- ENUM/SET have dual type properties (string and numeric).
- Require explicit CAST to avoid ambiguity.
- */
- point->bad_expression_data_type_error(name().ptr());
- return true;
+ return attr->frm_unpack_numeric_with_dec(share, buffer);
}
-bool Type_handler_general_purpose_int::
- Vers_history_point_resolve_unit(THD *thd,
- Vers_history_point *point) const
+bool Type_handler_decimal_result::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
{
- return point->resolve_unit_trx_id(thd);
+ return attr->frm_unpack_numeric_with_dec(share, buffer);
}
-bool Type_handler_bit::
- Vers_history_point_resolve_unit(THD *thd,
- Vers_history_point *point) const
+bool Type_handler_time_common::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
{
- return point->resolve_unit_trx_id(thd);
+ return attr->frm_unpack_temporal_with_dec(share, MIN_TIME_WIDTH, buffer);
}
-bool Type_handler_temporal_result::
- Vers_history_point_resolve_unit(THD *thd,
- Vers_history_point *point) const
+bool Type_handler_datetime_common::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
{
- return point->resolve_unit_timestamp(thd);
+ return attr->frm_unpack_temporal_with_dec(share, MAX_DATETIME_WIDTH, buffer);
}
-bool Type_handler_general_purpose_string::
- Vers_history_point_resolve_unit(THD *thd,
- Vers_history_point *point) const
+bool Type_handler_timestamp_common::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
{
- return point->resolve_unit_timestamp(thd);
+ return attr->frm_unpack_temporal_with_dec(share, MAX_DATETIME_WIDTH, buffer);
}
-/***************************************************************************/
bool Type_handler_null::Item_const_eq(const Item_const *a,
const Item_const *b,
@@ -8135,14 +8566,7 @@ Type_handler_temporal_result::Item_const_eq(const Item_const *a,
const Type_handler *
Type_handler_hex_hybrid::cast_to_int_type_handler() const
{
- return &type_handler_longlong;
-}
-
-
-const Type_handler *
-Type_handler_hex_hybrid::type_handler_for_system_time() const
-{
- return &type_handler_longlong;
+ return &type_handler_ulonglong;
}
@@ -8608,6 +9032,238 @@ Type_handler_time_common::Item_param_val_native(THD *thd,
}
+/***************************************************************************/
+
+bool Type_handler::validate_implicit_default_value(THD *thd,
+ const Column_definition &def) const
+{
+ DBUG_EXECUTE_IF("validate_implicit_default_value_error", return true;);
+ return false;
+}
+
+
+bool Type_handler_date_common::validate_implicit_default_value(THD *thd,
+ const Column_definition &def) const
+{
+ return thd->variables.sql_mode & MODE_NO_ZERO_DATE;
+}
+
+
+bool Type_handler_datetime_common::validate_implicit_default_value(THD *thd,
+ const Column_definition &def) const
+{
+ return thd->variables.sql_mode & MODE_NO_ZERO_DATE;
+}
+
+
+/***************************************************************************/
+
+const Name & Type_handler_row::default_value() const
+{
+ DBUG_ASSERT(0);
+ static Name def(STRING_WITH_LEN(""));
+ return def;
+}
+
+const Name & Type_handler_numeric::default_value() const
+{
+ static Name def(STRING_WITH_LEN("0"));
+ return def;
+}
+
+const Name & Type_handler_string_result::default_value() const
+{
+ static Name def(STRING_WITH_LEN(""));
+ return def;
+}
+
+const Name & Type_handler_time_common::default_value() const
+{
+ static Name def(STRING_WITH_LEN("00:00:00"));
+ return def;
+}
+
+const Name & Type_handler_date_common::default_value() const
+{
+ static Name def(STRING_WITH_LEN("0000-00-00"));
+ return def;
+}
+
+const Name & Type_handler_datetime_common::default_value() const
+{
+ static Name def(STRING_WITH_LEN("0000-00-00 00:00:00"));
+ return def;
+}
+
+const Name & Type_handler_timestamp_common::default_value() const
+{
+ static Name def(STRING_WITH_LEN("0000-00-00 00:00:00"));
+ return def;
+}
+
+/***************************************************************************/
+
+bool Type_handler::Column_definition_data_type_info_image(Binary_string *to,
+ const Column_definition &def)
+ const
+{
+ // Have *some* columns write type info (let's use string fields as an example)
+ DBUG_EXECUTE_IF("frm_data_type_info_emulate",
+ if (cmp_type() == STRING_RESULT)
+ return to->append("x", 1) ||
+ to->append(name().lex_cstring()););
+ if (type_collection() != &type_collection_std)
+ return to->append(name().lex_cstring());
+ return false;
+}
+
+
+/***************************************************************************/
+
+void
+Type_handler::partition_field_type_not_allowed(const LEX_CSTRING &field_name)
+{
+ my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0),
+ field_name.str);
+}
+
+
+bool
+Type_handler::partition_field_check_result_type(Item *item,
+ Item_result expected_type)
+{
+ if (item->result_type() != expected_type)
+ {
+ my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0));
+ return TRUE;
+ }
+ return false;
+}
+
+
+bool
+Type_handler_blob_common::partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const
+{
+ my_error(ER_BLOB_FIELD_IN_PART_FUNC_ERROR, MYF(0));
+ return true;
+}
+
+
+bool
+Type_handler_general_purpose_int::partition_field_append_value(
+ String *str,
+ Item *item_expr,
+ CHARSET_INFO *field_cs,
+ partition_value_print_mode_t mode)
+ const
+{
+ DBUG_ASSERT(item_expr->cmp_type() == INT_RESULT);
+ StringBuffer<21> tmp;
+ longlong value= item_expr->val_int();
+ tmp.set(value, system_charset_info);
+ return str->append(tmp);
+}
+
+
+/*
+ Append an Item value to a String using a desired mode.
+
+ @param [OUT] str The string to append the value to.
+ @param item_expr The item to get the value from
+ @param field_cs The character set of the value owner field.
+ @param mode The mode.
+ @retval true on error
+ @retval false on success
+
+ The value is added using system_charset_info (no matter what mode is).
+
+ (1) If mode is equal to PARTITION_VALUE_PRINT_MODE_FRM,
+ the value is appended as a pure ASCII string in the format '_latin1 0xdf',
+ i.e. a character set introducer followed by a hex hybrid.
+
+ Before appending, we value is first converted to field_cs.
+ a) If the conversion succeeds, the value is printed in its field_cs
+ represenation.
+ b) If the conversion fails, the value is printed without conversion,
+ using the original character set introducer followed by the original
+ string hex representation.
+ In this case, open_table_from_share() will later notice that
+ the value cannot be actually stored to the field, and report
+ the error. So here we don't need to report errors such as
+ ER_PARTITION_FUNCTION_IS_NOT_ALLOWED.
+
+ (2) If the mode is equal to PARTITION_VALUE_PRINT_SHOW,
+ then the value is needed for:
+ - SHOW CREATE TABLE, or
+ - the PARTITION_DESCRIPTION column in a
+ INFORMATION_SCHEMA.PARTITION query.
+
+ The value generated here will be later sent to the client and
+ therefore will be converted to the client character set in the protocol.
+
+ We try to generate the value as a simple quoted utf8 string without
+ introducers (e.g. 'utf8-string') when possible, to make it:
+ - as human readable as possible
+ - but still safe for mysqldump purposes.
+
+ Simple quoted utf8 string is generated when these two conditions are true
+ at the same time:
+ a) The value can be safely converted to utf8,
+ so we can return it without data loss from this function.
+ b) The value can be safely converted to the client character set,
+ so we can convert it later without data loss to the client character
+ set in the protocol.
+
+ If one of the conditions fail, the value is returned using
+ PARTITION_VALUE_PRINT_MODE_FRM representation. See (1).
+*/
+bool Type_handler::partition_field_append_value(
+ String *str,
+ Item *item_expr,
+ CHARSET_INFO *field_cs,
+ partition_value_print_mode_t mode)
+ const
+{
+ DBUG_ASSERT(cmp_type() != INT_RESULT);
+ StringBuffer<MAX_KEY_LENGTH> buf;
+ String *res;
+
+ if (!(res= item_expr->val_str(&buf)))
+ return str->append(STRING_WITH_LEN("NULL"), system_charset_info);
+
+ if (!res->length())
+ return str->append(STRING_WITH_LEN("''"), system_charset_info);
+
+ if (mode == PARTITION_VALUE_PRINT_MODE_FRM ||
+ !res->can_be_safely_converted_to(current_thd->
+ variables.character_set_client) ||
+ !res->can_be_safely_converted_to(system_charset_info))
+ {
+ StringBuffer<64> buf2;
+ uint cnverr2= 0;
+ buf2.copy(res->ptr(), res->length(), res->charset(), field_cs, &cnverr2);
+ if (!cnverr2)
+ return str->append_introducer_and_hex(&buf2);
+ return str->append_introducer_and_hex(res);
+ }
+
+ StringBuffer<64> val(system_charset_info);
+ uint cnverr= 0;
+ val.copy(res->ptr(), res->length(), res->charset(),
+ system_charset_info, &cnverr);
+ append_unescaped(str, val.ptr(), val.length());
+ return false;
+}
+
+
+bool Type_handler::can_return_extract_source(interval_type int_type) const
+{
+ return type_collection() == &type_collection_std;
+}
+
+/***************************************************************************/
+
LEX_CSTRING Charset::collation_specific_name() const
{
/*
@@ -8653,3 +9309,16 @@ Charset::eq_collation_specific_names(CHARSET_INFO *cs) const
LEX_CSTRING name1= Charset(cs).collation_specific_name();
return name0.length && !cmp(&name0, &name1);
}
+
+int initialize_data_type_plugin(st_plugin_int *plugin)
+{
+ st_mariadb_data_type *data= (st_mariadb_data_type*) plugin->plugin->info;
+ data->type_handler->set_name(Name(plugin->name));
+ if (plugin->plugin->init && plugin->plugin->init(NULL))
+ {
+ sql_print_error("Plugin '%s' init function returned error.",
+ plugin->name.str);
+ return 1;
+ }
+ return 0;
+}
diff --git a/sql/sql_type.h b/sql/sql_type.h
index c1b13dfa811..1db503b926e 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -1,7 +1,8 @@
#ifndef SQL_TYPE_H_INCLUDED
#define SQL_TYPE_H_INCLUDED
/*
- Copyright (c) 2015, 2020, MariaDB
+ Copyright (c) 2015 MariaDB Foundation.
+ Copyright (c) 2015, 2020, MariaDB Corporation.
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,15 +23,21 @@
#include "mysqld.h"
+#include "lex_string.h"
#include "sql_array.h"
#include "sql_const.h"
#include "sql_time.h"
+#include "sql_type_string.h"
#include "sql_type_real.h"
#include "compat56.h"
+C_MODE_START
+#include <ma_dyncol.h>
+C_MODE_END
class Field;
class Column_definition;
class Column_definition_attributes;
+class Key_part_spec;
class Item;
class Item_const;
class Item_literal;
@@ -69,6 +76,7 @@ class Item_func_div;
class Item_func_mod;
class cmp_item;
class in_vector;
+class Type_handler_data;
class Type_handler_hybrid_field_type;
class Sort_param;
class Arg_comparator;
@@ -79,8 +87,13 @@ class handler;
struct Schema_specification_st;
struct TABLE;
struct SORT_FIELD_ATTR;
+struct SORT_FIELD;
class Vers_history_point;
class Virtual_column_info;
+class Conv_source;
+class ST_FIELD_INFO;
+class Type_collection;
+class Create_func;
#define my_charset_numeric my_charset_latin1
@@ -110,6 +123,133 @@ enum scalar_comparison_op
};
+enum partition_value_print_mode_t
+{
+ PARTITION_VALUE_PRINT_MODE_SHOW= 0,
+ PARTITION_VALUE_PRINT_MODE_FRM= 1
+};
+
+
+enum column_definition_type_t
+{
+ COLUMN_DEFINITION_TABLE_FIELD,
+ COLUMN_DEFINITION_ROUTINE_PARAM,
+ COLUMN_DEFINITION_ROUTINE_LOCAL,
+ COLUMN_DEFINITION_FUNCTION_RETURN
+};
+
+
+class Send_field_extended_metadata
+{
+ LEX_CSTRING m_attr[MARIADB_FIELD_ATTR_LAST+1];
+public:
+ Send_field_extended_metadata()
+ {
+ bzero(this, sizeof(*this));
+ }
+ bool set_data_type_name(const LEX_CSTRING &str)
+ {
+ m_attr[MARIADB_FIELD_ATTR_DATA_TYPE_NAME]= str;
+ return false;
+ }
+ bool set_format_name(const LEX_CSTRING &str)
+ {
+ m_attr[MARIADB_FIELD_ATTR_FORMAT_NAME]= str;
+ return false;
+ }
+ bool has_extended_metadata() const
+ {
+ for (uint i= 0; i <= MARIADB_FIELD_ATTR_LAST; i++)
+ {
+ if (m_attr[i].str)
+ return true;
+ }
+ return false;
+ }
+ const LEX_CSTRING &attr(uint i) const
+ {
+ DBUG_ASSERT(i <= MARIADB_FIELD_ATTR_LAST);
+ return m_attr[i];
+ }
+};
+
+
+class Data_type_statistics
+{
+public:
+ uint m_uneven_bit_length;
+ uint m_fixed_string_total_length;
+ uint m_fixed_string_count;
+ uint m_variable_string_total_length;
+ uint m_variable_string_count;
+ uint m_blob_count;
+ Data_type_statistics()
+ :m_uneven_bit_length(0),
+ m_fixed_string_total_length(0),
+ m_fixed_string_count(0),
+ m_variable_string_total_length(0),
+ m_variable_string_count(0),
+ m_blob_count(0)
+ { }
+ uint string_count() const
+ {
+ return m_fixed_string_count + m_variable_string_count;
+ }
+ uint string_total_length() const
+ {
+ return m_fixed_string_total_length + m_variable_string_total_length;
+ }
+};
+
+
+class Typelib: public TYPELIB
+{
+public:
+ Typelib(uint count, const char **type_names, unsigned int *type_lengths)
+ {
+ TYPELIB::count= count;
+ TYPELIB::name= "";
+ TYPELIB::type_names= type_names;
+ TYPELIB::type_lengths= type_lengths;
+ }
+ uint max_octet_length() const
+ {
+ uint max_length= 0;
+ for (uint i= 0; i < TYPELIB::count; i++)
+ {
+ const uint length= TYPELIB::type_lengths[i];
+ set_if_bigger(max_length, length);
+ }
+ return max_length;
+ }
+};
+
+
+template<uint sz>
+class TypelibBuffer: public Typelib
+{
+ const char *m_type_names[sz + 1];
+ uint m_type_lengths[sz + 1];
+public:
+ TypelibBuffer(uint count, const LEX_CSTRING *values)
+ :Typelib(count, m_type_names, m_type_lengths)
+ {
+ DBUG_ASSERT(sz >= count);
+ for (uint i= 0; i < count; i++)
+ {
+ DBUG_ASSERT(values[i].str != NULL);
+ m_type_names[i]= values[i].str;
+ m_type_lengths[i]= (uint) values[i].length;
+ }
+ m_type_names[sz]= NullS; // End marker
+ m_type_lengths[sz]= 0; // End marker
+ }
+ TypelibBuffer(const LEX_CSTRING *values)
+ :TypelibBuffer(sz, values)
+ { }
+};
+
+
class Native: public Binary_string
{
public:
@@ -198,6 +338,10 @@ public:
double to_double() const { return m_ptr ? m_ptr->to_double() : 0.0; }
longlong to_longlong(bool unsigned_flag)
{ return m_ptr ? m_ptr->to_longlong(unsigned_flag) : 0; }
+ Longlong_null to_xlonglong_null()
+ {
+ return m_ptr ? Longlong_null(m_ptr->to_xlonglong()) : Longlong_null();
+ }
bool to_bool() const { return m_ptr ? m_ptr->to_bool() : false; }
String *to_string(String *to) const
{
@@ -581,7 +725,7 @@ public:
if (m_error)
return true;
to_hh24mmssff(ltime, MYSQL_TIMESTAMP_TIME);
- ltime->hour+= to_days_abs() * 24;
+ ltime->hour+= static_cast<unsigned>(to_days_abs() * 24);
return adjust_time_range_with_warn(thd, ltime, decimals);
}
bool to_datetime(MYSQL_TIME *ltime) const
@@ -772,7 +916,8 @@ protected:
my_decimal *to_decimal(my_decimal *to) const;
static double to_double(bool negate, ulonglong num, ulong frac)
{
- double d= (double) num + frac / (double) TIME_SECOND_PART_FACTOR;
+ double d= static_cast<double>(num) + static_cast<double>(frac) /
+ TIME_SECOND_PART_FACTOR;
return negate ? -d : d;
}
longlong to_packed() const { return ::pack_time(this); }
@@ -900,7 +1045,7 @@ protected:
{
return ::check_date(this, flags, warn);
}
- void time_hhmmssff_set_max(ulong max_hour)
+ void time_hhmmssff_set_max(uint max_hour)
{
hour= max_hour;
minute= TIME_MAX_MINUTE;
@@ -2706,6 +2851,19 @@ public:
#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII
+static inline my_repertoire_t operator|(const my_repertoire_t a,
+ const my_repertoire_t b)
+{
+ return (my_repertoire_t) ((uint) a | (uint) b);
+}
+
+static inline my_repertoire_t &operator|=(my_repertoire_t &a,
+ const my_repertoire_t b)
+{
+ return a= (my_repertoire_t) ((uint) a | (uint) b);
+}
+
+
enum Derivation
{
DERIVATION_IGNORABLE= 6,
@@ -2727,7 +2885,7 @@ class DTCollation {
public:
CHARSET_INFO *collation;
enum Derivation derivation;
- uint repertoire;
+ my_repertoire_t repertoire;
void set_repertoire_from_charset(CHARSET_INFO *cs)
{
@@ -2763,16 +2921,14 @@ public:
}
DTCollation(CHARSET_INFO *collation_arg,
Derivation derivation_arg,
- uint repertoire_arg)
+ my_repertoire_t repertoire_arg)
:collation(collation_arg),
derivation(derivation_arg),
repertoire(repertoire_arg)
{ }
void set(const DTCollation &dt)
{
- collation= dt.collation;
- derivation= dt.derivation;
- repertoire= dt.repertoire;
+ *this= dt;
}
void set(CHARSET_INFO *collation_arg, Derivation derivation_arg)
{
@@ -2782,18 +2938,12 @@ public:
}
void set(CHARSET_INFO *collation_arg,
Derivation derivation_arg,
- uint repertoire_arg)
+ my_repertoire_t repertoire_arg)
{
collation= collation_arg;
derivation= derivation_arg;
repertoire= repertoire_arg;
}
- void set_numeric()
- {
- collation= &my_charset_numeric;
- derivation= DERIVATION_NUMERIC;
- repertoire= MY_REPERTOIRE_NUMERIC;
- }
void set(CHARSET_INFO *collation_arg)
{
collation= collation_arg;
@@ -2818,11 +2968,10 @@ public:
default: return "UNKNOWN";
}
}
- int sortcmp(const String *s, const String *t) const
+ int sortcmp(const Binary_string *s, const Binary_string *t) const
{
- return collation->coll->strnncollsp(collation,
- (uchar *) s->ptr(), s->length(),
- (uchar *) t->ptr(), t->length());
+ return collation->strnncollsp(s->ptr(), s->length(),
+ t->ptr(), t->length());
}
};
@@ -2845,38 +2994,87 @@ char_to_byte_length_safe(size_t char_length_arg, uint32 mbmaxlen_arg)
return tmp > UINT_MAX32 ? (uint32) UINT_MAX32 : static_cast<uint32>(tmp);
}
-/**
- A class to store type attributes for the standard data types.
- Does not include attributes for the extended data types
- such as ENUM, SET, GEOMETRY.
-*/
-class Type_std_attributes
+
+class Type_numeric_attributes
{
public:
- DTCollation collation;
- uint decimals;
+ static uint count_unsigned(Item **item, uint nitems);
+ static uint32 find_max_char_length(Item **item, uint nitems);
+ static uint32 find_max_octet_length(Item **item, uint nitems);
+ static int find_max_decimal_int_part(Item **item, uint nitems);
+ static uint find_max_decimals(Item **item, uint nitems);
+public:
/*
The maximum value length in characters multiplied by collation->mbmaxlen.
Almost always it's the maximum value length in bytes.
*/
uint32 max_length;
+ uint decimals;
bool unsigned_flag;
- Type_std_attributes()
- :collation(&my_charset_bin, DERIVATION_COERCIBLE),
- decimals(0), max_length(0), unsigned_flag(false)
+public:
+ Type_numeric_attributes()
+ :max_length(0), decimals(0), unsigned_flag(false)
{ }
- Type_std_attributes(const Type_std_attributes *other)
- :collation(other->collation),
- decimals(other->decimals),
- max_length(other->max_length),
- unsigned_flag(other->unsigned_flag)
+ Type_numeric_attributes(uint32 max_length_arg, uint decimals_arg,
+ bool unsigned_flag_arg)
+ :max_length(max_length_arg),
+ decimals(decimals_arg),
+ unsigned_flag(unsigned_flag_arg)
{ }
- Type_std_attributes(uint32 max_length_arg, uint decimals_arg,
- bool unsigned_flag_arg, const DTCollation &dtc)
- :collation(dtc),
- decimals(decimals_arg),
- max_length(max_length_arg),
- unsigned_flag(unsigned_flag_arg)
+protected:
+ void aggregate_numeric_attributes_real(Item **item, uint nitems);
+ void aggregate_numeric_attributes_decimal(Item **item, uint nitems,
+ bool unsigned_arg);
+};
+
+
+
+class Type_temporal_attributes: public Type_numeric_attributes
+{
+public:
+ Type_temporal_attributes(uint int_part_length, uint dec, bool unsigned_arg)
+ :Type_numeric_attributes(int_part_length + (dec ? 1 : 0),
+ MY_MIN(dec, TIME_SECOND_PART_DIGITS),
+ unsigned_arg)
+ {
+ max_length+= decimals;
+ }
+};
+
+
+class Type_temporal_attributes_not_fixed_dec: public Type_numeric_attributes
+{
+public:
+ Type_temporal_attributes_not_fixed_dec(uint32 int_part_length, uint dec,
+ bool unsigned_flag)
+ :Type_numeric_attributes(int_part_length, dec, unsigned_flag)
+ {
+ if (decimals == NOT_FIXED_DEC)
+ max_length+= TIME_SECOND_PART_DIGITS + 1;
+ else if (decimals)
+ {
+ set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
+ max_length+= decimals + 1;
+ }
+ }
+};
+
+
+/**
+ A class to store type attributes for the standard data types.
+ Does not include attributes for the extended data types
+ such as ENUM, SET, GEOMETRY.
+*/
+class Type_std_attributes: public Type_numeric_attributes
+{
+public:
+ DTCollation collation;
+ Type_std_attributes()
+ :collation(&my_charset_bin, DERIVATION_COERCIBLE)
+ { }
+ Type_std_attributes(const Type_numeric_attributes &nattr,
+ const DTCollation &dtc)
+ :Type_numeric_attributes(nattr), collation(dtc)
{ }
void set(const Type_std_attributes *other)
{
@@ -2886,6 +3084,10 @@ public:
{
*this= other;
}
+ void set(const Type_numeric_attributes &nattr, const DTCollation &dtc)
+ {
+ *this= Type_std_attributes(nattr, dtc);
+ }
uint32 max_char_length() const
{ return max_length / collation.collation->mbmaxlen; }
void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs)
@@ -2898,41 +3100,11 @@ public:
max_length= char_to_byte_length_safe(max_char_length_arg,
collation.collation->mbmaxlen);
}
- void fix_char_length_temporal_not_fixed_dec(uint int_part_length, uint dec)
+ void fix_attributes_temporal(uint32 int_part_length, uint dec)
{
- uint char_length= int_part_length;
- if ((decimals= dec))
- {
- if (decimals == NOT_FIXED_DEC)
- char_length+= TIME_SECOND_PART_DIGITS + 1;
- else
- {
- set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
- char_length+= decimals + 1;
- }
- }
- fix_char_length(char_length);
- }
- void fix_attributes_temporal_not_fixed_dec(uint int_part_length, uint dec)
- {
- collation.set_numeric();
- unsigned_flag= 0;
- fix_char_length_temporal_not_fixed_dec(int_part_length, dec);
- }
- void fix_attributes_time_not_fixed_dec(uint dec)
- {
- fix_attributes_temporal_not_fixed_dec(MIN_TIME_WIDTH, dec);
- }
- void fix_attributes_datetime_not_fixed_dec(uint dec)
- {
- fix_attributes_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec);
- }
- void fix_attributes_temporal(uint int_part_length, uint dec)
- {
- collation.set_numeric();
- unsigned_flag= 0;
- decimals= MY_MIN(dec, TIME_SECOND_PART_DIGITS);
- max_length= decimals + int_part_length + (dec ? 1 : 0);
+ *this= Type_std_attributes(
+ Type_temporal_attributes(int_part_length, dec, false),
+ DTCollation_numeric());
}
void fix_attributes_date()
{
@@ -2947,38 +3119,31 @@ public:
fix_attributes_temporal(MAX_DATETIME_WIDTH, dec);
}
- void count_only_length(Item **item, uint nitems);
- void count_octet_length(Item **item, uint nitems);
- void count_real_length(Item **item, uint nitems);
- void count_decimal_length(Item **item, uint nitems);
- bool count_string_length(const char *func_name, Item **item, uint nitems);
- uint count_max_decimals(Item **item, uint nitems);
-
void aggregate_attributes_int(Item **items, uint nitems)
{
- collation.set_numeric();
- count_only_length(items, nitems);
+ collation= DTCollation_numeric();
+ fix_char_length(find_max_char_length(items, nitems));
+ unsigned_flag= count_unsigned(items, nitems) > 0;
decimals= 0;
}
void aggregate_attributes_real(Item **items, uint nitems)
{
- collation.set_numeric();
- count_real_length(items, nitems);
+ collation= DTCollation_numeric();
+ aggregate_numeric_attributes_real(items, nitems);
}
- void aggregate_attributes_decimal(Item **items, uint nitems)
+ void aggregate_attributes_decimal(Item **items, uint nitems,
+ bool unsigned_arg)
{
- collation.set_numeric();
- count_decimal_length(items, nitems);
+ collation= DTCollation_numeric();
+ aggregate_numeric_attributes_decimal(items, nitems,
+ (unsigned_flag= unsigned_arg));
}
bool aggregate_attributes_string(const char *func_name,
- Item **item, uint nitems)
- {
- return count_string_length(func_name, item, nitems);
- }
+ Item **item, uint nitems);
void aggregate_attributes_temporal(uint int_part_length,
Item **item, uint nitems)
{
- fix_attributes_temporal(int_part_length, count_max_decimals(item, nitems));
+ fix_attributes_temporal(int_part_length, find_max_decimals(item, nitems));
}
bool agg_item_collations(DTCollation &c, const char *name,
@@ -3082,23 +3247,15 @@ public:
Type_all_attributes()
:Type_std_attributes()
{ }
- Type_all_attributes(const Type_all_attributes *other)
+ Type_all_attributes(const Type_all_attributes &other)
:Type_std_attributes(other)
{ }
virtual ~Type_all_attributes() {}
virtual void set_maybe_null(bool maybe_null_arg)= 0;
// Returns total number of decimal digits
virtual uint decimal_precision() const= 0;
- /*
- Field::geometry_type is not visible here.
- Let's use an "uint" wrapper for now. Later when we move Field_geom
- into a plugin, this method will be replaced to some generic
- datatype indepented method.
- */
- virtual uint uint_geometry_type() const= 0;
- virtual void set_geometry_type(uint type)= 0;
- virtual TYPELIB *get_typelib() const= 0;
- virtual void set_typelib(TYPELIB *typelib)= 0;
+ virtual const TYPELIB *get_typelib() const= 0;
+ virtual void set_typelib(const TYPELIB *typelib)= 0;
};
@@ -3157,8 +3314,19 @@ public:
LEX_CSTRING::str= str_arg;
LEX_CSTRING::length= length_arg;
}
+ Name(const LEX_CSTRING &lcs)
+ {
+ LEX_CSTRING::str= lcs.str;
+ LEX_CSTRING::length= lcs.length;
+ }
const char *ptr() const { return LEX_CSTRING::str; }
uint length() const { return (uint) LEX_CSTRING::length; }
+ const LEX_CSTRING &lex_cstring() const { return *this; }
+ bool eq(const LEX_CSTRING &other) const
+ {
+ return !system_charset_info->strnncoll(LEX_CSTRING::str, LEX_CSTRING::length,
+ other.str, other.length);
+ }
};
@@ -3190,7 +3358,7 @@ public:
{ }
uchar *ptr() const { return m_ptr; }
uchar offs() const { return m_offs; }
- uchar bit() const { return m_ptr ? ((uchar) 1) << m_offs : 0; }
+ uchar bit() const { return static_cast<uchar>(m_ptr ? 1U << m_offs : 0); }
void inc()
{
DBUG_ASSERT(m_ptr);
@@ -3294,12 +3462,63 @@ public:
};
+enum vers_kind_t
+{
+ VERS_UNDEFINED= 0,
+ VERS_TIMESTAMP,
+ VERS_TRX_ID
+};
+
+
+class Vers_type_handler
+{
+protected:
+ Vers_type_handler() {}
+public:
+ virtual ~Vers_type_handler() {}
+ virtual vers_kind_t kind() const
+ {
+ DBUG_ASSERT(0);
+ return VERS_UNDEFINED;
+ }
+ virtual bool check_sys_fields(const LEX_CSTRING &table_name,
+ const Column_definition *row_start,
+ const Column_definition *row_end) const= 0;
+};
+
+
+class Vers_type_timestamp: public Vers_type_handler
+{
+public:
+ virtual vers_kind_t kind() const
+ {
+ return VERS_TIMESTAMP;
+ }
+ bool check_sys_fields(const LEX_CSTRING &table_name,
+ const Column_definition *row_start,
+ const Column_definition *row_end) const;
+};
+extern Vers_type_timestamp vers_type_timestamp;
+
+
+class Vers_type_trx: public Vers_type_handler
+{
+public:
+ virtual vers_kind_t kind() const
+ {
+ return VERS_TRX_ID;
+ }
+ bool check_sys_fields(const LEX_CSTRING &table_name,
+ const Column_definition *row_start,
+ const Column_definition *row_end) const;
+};
+extern MYSQL_PLUGIN_IMPORT Vers_type_trx vers_type_trx;
+
+
class Type_handler
{
+ Name m_name;
protected:
- static const Name m_version_default;
- static const Name m_version_mysql56;
- static const Name m_version_mariadb53;
String *print_item_value_csstr(THD *thd, Item *item, String *str) const;
String *print_item_value_temporal(THD *thd, Item *item, String *str,
const Name &type_name, String *buf) const;
@@ -3307,10 +3526,16 @@ protected:
bool maybe_null, bool null_value,
bool unsigned_flag,
longlong value) const;
- bool
- Item_func_or_sum_illegal_param(const char *name) const;
- bool
- Item_func_or_sum_illegal_param(const Item_func_or_sum *) const;
+ void store_sort_key_longlong(uchar *to, bool unsigned_flag,
+ longlong value) const;
+
+ uint make_packed_sort_key_longlong(uchar *to, bool maybe_null,
+ bool null_value, bool unsigned_flag,
+ longlong value,
+ const SORT_FIELD_ATTR *sort_field) const;
+
+ bool Item_func_or_sum_illegal_param(const char *name) const;
+ bool Item_func_or_sum_illegal_param(const Item_func_or_sum *) const;
bool check_null(const Item *item, st_value *value) const;
bool Item_send_str(Item *item, Protocol *protocol, st_value *buf) const;
bool Item_send_tiny(Item *item, Protocol *protocol, st_value *buf) const;
@@ -3333,11 +3558,15 @@ protected:
enum_field_types type)
const;
public:
+ static const Type_handler *handler_by_name(THD *thd, const LEX_CSTRING &name);
+ static const Type_handler *handler_by_name_or_error(THD *thd,
+ const LEX_CSTRING &name);
static const Type_handler *odbc_literal_type_handler(const LEX_CSTRING *str);
static const Type_handler *blob_type_handler(uint max_octet_length);
static const Type_handler *string_type_handler(uint max_octet_length);
static const Type_handler *bit_and_int_mixture_handler(uint max_char_len);
- static const Type_handler *type_handler_long_or_longlong(uint max_char_len);
+ static const Type_handler *type_handler_long_or_longlong(uint max_char_len,
+ bool unsigned_flag);
/**
Return a string type handler for Item
If too_big_for_varchar() returns a BLOB variant, according to length.
@@ -3350,25 +3579,27 @@ public:
static const Type_handler *get_handler_by_field_type(enum_field_types type);
static const Type_handler *get_handler_by_real_type(enum_field_types type);
static const Type_handler *get_handler_by_cmp_type(Item_result type);
- static const Type_handler *get_handler_by_result_type(Item_result type)
- {
- /*
- As result_type() returns STRING_RESULT for temporal Items,
- type should never be equal to TIME_RESULT here.
- */
- DBUG_ASSERT(type != TIME_RESULT);
- return get_handler_by_cmp_type(type);
- }
+ static const Type_collection *
+ type_collection_for_aggregation(const Type_handler *h1,
+ const Type_handler *h2);
+ virtual const Type_collection *type_collection() const;
static const
Type_handler *aggregate_for_result_traditional(const Type_handler *h1,
const Type_handler *h2);
- static const
- Type_handler *aggregate_for_num_op_traditional(const Type_handler *h1,
- const Type_handler *h2);
-
virtual Schema *schema() const;
- virtual const Name name() const= 0;
- virtual const Name version() const { return m_version_default; }
+ static void partition_field_type_not_allowed(const LEX_CSTRING &field_name);
+ static bool partition_field_check_result_type(Item *item,
+ Item_result expected_type);
+ static const Name & version_mysql56();
+ static const Name & version_mariadb53();
+
+ void set_name(Name n) { DBUG_ASSERT(!m_name.ptr()); m_name= n; }
+ const Name name() const { return m_name; }
+ virtual const Name version() const;
+ virtual const Name &default_value() const= 0;
+ virtual uint32 flags() const { return 0; }
+ virtual ulong KEY_pack_flags(uint column_nr) const { return 0; }
+ bool is_unsigned() const { return flags() & UNSIGNED_FLAG; }
virtual enum_field_types field_type() const= 0;
virtual enum_field_types real_field_type() const { return field_type(); }
/**
@@ -3390,7 +3621,7 @@ public:
*/
virtual enum_field_types traditional_merge_field_type() const
{
- DBUG_ASSERT(is_traditional_type());
+ DBUG_ASSERT(is_traditional_scalar_type());
return field_type();
}
virtual enum_field_types type_code_for_protocol() const
@@ -3398,8 +3629,15 @@ public:
return field_type();
}
virtual protocol_send_type_t protocol_send_type() const= 0;
+ virtual bool Item_append_extended_type_info(Send_field_extended_metadata *to,
+ const Item *item) const
+ {
+ return false;
+ }
virtual Item_result result_type() const= 0;
virtual Item_result cmp_type() const= 0;
+ virtual enum_dynamic_column_type
+ dyncol_type(const Type_all_attributes *attr) const= 0;
virtual enum_mysql_timestamp_type mysql_timestamp_type() const
{
return MYSQL_TIMESTAMP_ERROR;
@@ -3414,6 +3652,16 @@ public:
{
return false;
}
+ /*
+ If operations such as:
+ UPDATE t1 SET binary_string_field=this_type_field;
+ should store this_type_field->val_native() rather than
+ this_type_field->val_str().
+ */
+ virtual bool convert_to_binary_using_val_native() const
+ {
+ return false;
+ }
virtual bool is_timestamp_type() const
{
return false;
@@ -3480,10 +3728,25 @@ public:
{
return this;
}
- virtual const Type_handler *type_handler_for_system_time() const
+ virtual const Type_handler *type_handler_unsigned() const
+ {
+ return this;
+ }
+ virtual const Type_handler *type_handler_signed() const
{
return this;
}
+ virtual bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const
+ {
+ partition_field_type_not_allowed(field_name);
+ return true;
+ }
+ virtual bool partition_field_append_value(String *str,
+ Item *item_expr,
+ CHARSET_INFO *field_cs,
+ partition_value_print_mode_t mode)
+ const;
virtual int
stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const= 0;
virtual CHARSET_INFO *charset_for_protocol(const Item *item) const;
@@ -3495,15 +3758,13 @@ public:
{
return false;
}
+ Type_handler() : m_name(0,0) {}
virtual ~Type_handler() {}
/**
- Determines MariaDB traditional data types that always present
+ Determines MariaDB traditional scalar data types that always present
in the server.
*/
- virtual bool is_traditional_type() const
- {
- return true;
- }
+ bool is_traditional_scalar_type() const;
virtual bool is_scalar_type() const { return true; }
virtual bool can_return_int() const { return true; }
virtual bool can_return_decimal() const { return true; }
@@ -3512,6 +3773,7 @@ public:
virtual bool can_return_text() const { return true; }
virtual bool can_return_date() const { return true; }
virtual bool can_return_time() const { return true; }
+ virtual bool can_return_extract_source(interval_type type) const;
virtual bool is_bool_type() const { return false; }
virtual bool is_general_purpose_string_type() const { return false; }
virtual uint Item_time_precision(THD *thd, Item *item) const;
@@ -3556,9 +3818,13 @@ public:
This information is not available in the binary log, so
we assume that these fields are the same on the master and on the slave.
*/
- virtual Field *make_conversion_table_field(TABLE *TABLE,
+ virtual Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
const Field *target) const= 0;
+ virtual void show_binlog_type(const Conv_source &src, const Field &dst,
+ String *str) const;
+ virtual uint32 max_display_length_for_field(const Conv_source &src) const= 0;
/*
Performs the final data type validation for a UNION element,
after the regular "aggregation for result" was done.
@@ -3567,6 +3833,19 @@ public:
{
return false;
}
+ virtual uint Column_definition_gis_options_image(uchar *buff,
+ const Column_definition &def)
+ const
+ {
+ return 0;
+ }
+ virtual bool Column_definition_data_type_info_image(Binary_string *to,
+ const Column_definition &def)
+ const;
+ // Check if the implicit default value is Ok in the current sql_mode
+ virtual bool validate_implicit_default_value(THD *thd,
+ const Column_definition &def)
+ const;
// Automatic upgrade, e.g. for ALTER TABLE t1 FORCE
virtual void Column_definition_implicit_upgrade(Column_definition *c) const
{ }
@@ -3574,6 +3853,13 @@ public:
virtual bool Column_definition_validate_check_constraint(THD *thd,
Column_definition *c)
const;
+ // Set attributes in the parser
+ virtual bool Column_definition_set_attributes(THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const;
// Fix attributes after the parser
virtual bool Column_definition_fix_attributes(Column_definition *c) const= 0;
/*
@@ -3615,14 +3901,44 @@ public:
virtual bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
ulonglong table_flags) const= 0;
- virtual Field *make_table_field(const LEX_CSTRING *name,
+ virtual bool Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const;
+ virtual bool Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *has_key_needed) const;
+ virtual bool Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const;
+ virtual bool Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const;
+ virtual bool Key_part_spec_init_spatial(Key_part_spec *part,
+ const Column_definition &def) const;
+ virtual bool Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def) const
+ {
+ return true; // Error
+ }
+ virtual Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const= 0;
- Field *make_and_init_table_field(const LEX_CSTRING *name,
+ TABLE_SHARE *share) const= 0;
+ Field *make_and_init_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
TABLE *table) const;
+ virtual Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const
+ {
+ DBUG_ASSERT(0);
+ return NULL;
+ }
virtual Field *
make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
@@ -3634,22 +3950,40 @@ public:
virtual void
Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
uchar *buff) const;
+ virtual const Type_handler *type_handler_frm_unpack(const uchar *buffer) const
+ {
+ return this;
+ }
virtual bool
Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
TABLE_SHARE *share,
const uchar *buffer,
LEX_CUSTRING *gis_options) const;
- virtual void make_sort_key(uchar *to, Item *item,
- const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const= 0;
- virtual void sortlength(THD *thd,
+ /*
+ Create a fixed size key part for a sort key
+ */
+ virtual void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const= 0;
+
+ /*
+ create a compact size key part for a sort key
+ */
+ virtual uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const=0;
+
+ virtual void sort_length(THD *thd,
const Type_std_attributes *item,
SORT_FIELD_ATTR *attr) const= 0;
+ virtual bool is_packable() const { return false; }
+
virtual uint32 max_display_length(const Item *item) const= 0;
virtual uint32 Item_decimal_notation_int_digits(const Item *item) const { return 0; }
virtual uint32 calc_pack_length(uint32 length) const= 0;
+ virtual uint calc_key_length(const Column_definition &def) const;
virtual void Item_update_null_value(Item *item) const= 0;
virtual bool Item_save_in_value(THD *thd, Item *item, st_value *value) const= 0;
virtual void Item_param_setup_conversion(THD *thd, Item_param *) const {}
@@ -3732,6 +4066,10 @@ public:
Item *src,
const Item *cmp) const= 0;
virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0;
+ virtual Item *make_constructor_item(THD *thd, List<Item> *args) const
+ {
+ return NULL;
+ }
/**
A builder for literals with data type name prefix, e.g.:
TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
@@ -3761,7 +4099,6 @@ public:
virtual Item *create_typecast_item(THD *thd, Item *item,
const Type_cast_attributes &attr) const
{
- DBUG_ASSERT(0);
return NULL;
}
virtual Item_copy *create_item_copy(THD *thd, Item *item) const;
@@ -3909,8 +4246,7 @@ public:
virtual bool
Item_func_mod_fix_length_and_dec(Item_func_mod *func) const= 0;
- virtual bool
- Vers_history_point_resolve_unit(THD *thd, Vers_history_point *point) const;
+ virtual const Vers_type_handler *vers() const { return NULL; }
};
@@ -3919,67 +4255,82 @@ public:
*/
class Type_handler_row: public Type_handler
{
- static const Name m_name_row;
public:
virtual ~Type_handler_row() {}
- const Name name() const { return m_name_row; }
- bool is_scalar_type() const { return false; }
- bool can_return_int() const { return false; }
- bool can_return_decimal() const { return false; }
- bool can_return_real() const { return false; }
- bool can_return_str() const { return false; }
- bool can_return_text() const { return false; }
- bool can_return_date() const { return false; }
- bool can_return_time() const { return false; }
- enum_field_types field_type() const
+ const Name &default_value() const override;
+ bool validate_implicit_default_value(THD *thd,
+ const Column_definition &def) const
+ override
+ {
+ DBUG_ASSERT(0);
+ return true;
+ }
+ const Type_collection *type_collection() const override;
+ bool is_scalar_type() const override { return false; }
+ bool can_return_int() const override { return false; }
+ bool can_return_decimal() const override { return false; }
+ bool can_return_real() const override { return false; }
+ bool can_return_str() const override { return false; }
+ bool can_return_text() const override { return false; }
+ bool can_return_date() const override { return false; }
+ bool can_return_time() const override { return false; }
+ enum_field_types field_type() const override
{
DBUG_ASSERT(0);
return MYSQL_TYPE_NULL;
};
- protocol_send_type_t protocol_send_type() const
+ protocol_send_type_t protocol_send_type() const override
{
DBUG_ASSERT(0);
return PROTOCOL_SEND_STRING;
}
- Item_result result_type() const
+ Item_result result_type() const override
{
return ROW_RESULT;
}
- Item_result cmp_type() const
+ Item_result cmp_type() const override
{
return ROW_RESULT;
}
- const Type_handler *type_handler_for_comparison() const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ DBUG_ASSERT(0);
+ return DYN_COL_NULL;
+ }
+ const Type_handler *type_handler_for_comparison() const override;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer) const override
{
DBUG_ASSERT(0);
return false;
}
- Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const
+ Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override
{
DBUG_ASSERT(0);
return NULL;
}
- Field *make_conversion_table_field(TABLE *TABLE,
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
uint metadata,
- const Field *target) const
+ const Field *target) const override
{
DBUG_ASSERT(0);
return NULL;
}
- bool Column_definition_fix_attributes(Column_definition *c) const
+ bool Column_definition_fix_attributes(Column_definition *c) const override
{
return false;
}
void Column_definition_reuse_fix_attributes(THD *thd,
Column_definition *c,
- const Field *field) const
+ const Field *field)
+ const override
{
DBUG_ASSERT(0);
}
@@ -3987,26 +4338,27 @@ public:
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags) const override;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file,
const Schema_specification_st *schema)
- const
+ const override
{
DBUG_ASSERT(0);
return true;
}
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{
return false;
}
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const
+ TABLE_SHARE *share) const override
{
DBUG_ASSERT(0);
return NULL;
@@ -4017,145 +4369,162 @@ public:
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
- void make_sort_key(uchar *to, Item *item,
- const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const
+ uint32 flags) const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override
{
DBUG_ASSERT(0);
}
- void sortlength(THD *thd, const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ void sort_length(THD *thd, const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override
{
DBUG_ASSERT(0);
}
- uint32 max_display_length(const Item *item) const
+ uint32 max_display_length(const Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
- uint32 calc_pack_length(uint32 length) const
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ uint32 calc_pack_length(uint32 length) const override
{
DBUG_ASSERT(0);
return 0;
}
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- uint Item_decimal_precision(const Item *item) const
+ Item *a, Item *b) const override;
+ uint Item_decimal_precision(const Item *item) const override
{
DBUG_ASSERT(0);
return DECIMAL_MAX_PRECISION;
}
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
- const st_value *value) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const st_value *value) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
DBUG_ASSERT(0);
return true;
}
- void Item_update_null_value(Item *item) const;
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const
+ void Item_update_null_value(Item *item) const override;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions)
+ const override
{
DBUG_ASSERT(0);
return 1;
}
- String *print_item_value(THD *thd, Item *item, String *str) const;
+ String *print_item_value(THD *thd, Item *item, String *str) const override;
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) const
+ Item *source_expr, Item *source_const)
+ const override
{
DBUG_ASSERT(0);
return false;
}
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- Item_copy *create_item_copy(THD *thd, Item *item) const
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp)
+ const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ Item_copy *create_item_copy(THD *thd, Item *item) const override
{
DBUG_ASSERT(0);
return NULL;
}
- bool set_comparator_func(Arg_comparator *cmp) const;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const
+ Item **items, uint nitems)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_val_bool(Item *item) const
+ bool Item_val_bool(Item *item) const override
{
DBUG_ASSERT(0);
return false;
}
void Item_get_date(THD *thd, Item *item,
Temporal::Warn *warn, MYSQL_TIME *ltime,
- date_mode_t fuzzydate) const
+ date_mode_t fuzzydate) const override
{
DBUG_ASSERT(0);
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
- longlong Item_val_int_signed_typecast(Item *item) const
+ longlong Item_val_int_signed_typecast(Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
- longlong Item_val_int_unsigned_typecast(Item *item) const
+ longlong Item_val_int_unsigned_typecast(Item *item) const override
{
DBUG_ASSERT(0);
return 0;
}
- String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const
+ String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str)
+ const override
{
DBUG_ASSERT(0);
return NULL;
}
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const
+ String *) const override
{
DBUG_ASSERT(0);
return NULL;
}
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const
+ const override
{
DBUG_ASSERT(0);
return 0.0;
}
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const
+ const override
{
DBUG_ASSERT(0);
return 0;
}
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const
+ my_decimal *) const override
{
DBUG_ASSERT(0);
return NULL;
@@ -4164,105 +4533,116 @@ public:
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *ltime,
- date_mode_t fuzzydate) const
+ date_mode_t fuzzydate)
+ const override
{
DBUG_ASSERT(0);
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
}
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override
{
DBUG_ASSERT(0);
return NULL;
}
- double Item_func_min_max_val_real(Item_func_min_max *) const
+ double Item_func_min_max_val_real(Item_func_min_max *) const override
{
DBUG_ASSERT(0);
return 0;
}
- longlong Item_func_min_max_val_int(Item_func_min_max *) const
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override
{
DBUG_ASSERT(0);
return 0;
}
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const
+ my_decimal *) const override
{
DBUG_ASSERT(0);
return NULL;
}
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
- MYSQL_TIME *, date_mode_t fuzzydate) const
+ MYSQL_TIME *, date_mode_t fuzzydate)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_func_between_fix_length_and_dec(Item_func_between *func) const
+ bool Item_func_between_fix_length_and_dec(Item_func_between *func)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- longlong Item_func_between_val_int(Item_func_between *func) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const;
- bool Item_func_in_fix_comparator_compatible_types(THD *thd,
- Item_func_in *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs)
+ const override;
+ bool Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *)
+ const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
- bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const
+ bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const
+ bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const
+ bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *) const
+ bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const
+ bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const
+ bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const
+ bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const
+ bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *) const
+ bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *)
+ const override
{
DBUG_ASSERT(0);
return true;
}
- bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
- bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
- bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
- bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
- bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
+ bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
+ bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
+ bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
+ bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
+ bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
};
@@ -4272,20 +4652,23 @@ public:
class Type_handler_numeric: public Type_handler
{
public:
- String *print_item_value(THD *thd, Item *item, String *str) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ const Name &default_value() const override;
+ String *print_item_value(THD *thd, Item *item, String *str) const override;
+ double Item_func_min_max_val_real(Item_func_min_max *) const override;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
+ my_decimal *) const override;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
- MYSQL_TIME *, date_mode_t fuzzydate) const;
+ MYSQL_TIME *, date_mode_t fuzzydate)
+ const override;
virtual ~Type_handler_numeric() { }
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) const;
- bool Item_func_between_fix_length_and_dec(Item_func_between *func) const;
- bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const;
+ Item *source_expr, Item *source_const)
+ const override;
+ bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override;
+ bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const override;
};
@@ -4294,187 +4677,252 @@ public:
class Type_handler_real_result: public Type_handler_numeric
{
public:
- Item_result result_type() const { return REAL_RESULT; }
- Item_result cmp_type() const { return REAL_RESULT; }
+ Item_result result_type() const override{ return REAL_RESULT; }
+ Item_result cmp_type() const override { return REAL_RESULT; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ return DYN_COL_DOUBLE;
+ }
virtual ~Type_handler_real_result() {}
- const Type_handler *type_handler_for_comparison() const;
+ const Type_handler *type_handler_for_comparison() const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE_SHARE *share) const override;
void Column_definition_reuse_fix_attributes(THD *thd,
Column_definition *c,
- const Field *field) const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
+ const Field *field)
+ const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const override;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
+ const override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const;
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const;
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const;
+ const Item *outer)
+ const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ void sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override;
bool Item_const_eq(const Item_const *a, const Item_const *b,
- bool binary_cmp) const;
+ bool binary_cmp) const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
+ Item *a, Item *b) const override;
+ uint Item_decimal_precision(const Item *item) const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
- const st_value *value) const;
- void Item_update_null_value(Item *item) const;
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
+ const st_value *value) const override;
+ void Item_update_null_value(Item *item) const override;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions)
+ const override;
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp)
+ const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
+ Item **items, uint nitems)
+ const override;
bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
- Item **items, uint nitems) const;
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
- bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const;
- bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const;
- bool Item_val_bool(Item *item) const;
+ Item **items, uint nitems)
+ const override;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override;
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override;
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override;
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override;
+ bool Item_func_signed_fix_length_and_dec(Item_func_signed *item)
+ const override;
+ bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item)
+ const override;
+ bool Item_val_bool(Item *item) const override;
void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
- MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
- longlong Item_val_int_signed_typecast(Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const;
- String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const override;
+ longlong Item_val_int_signed_typecast(Item *item) const override;
+ longlong Item_val_int_unsigned_typecast(Item *item) const override;
+ String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str)
+ const override;
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const;
+ const override;
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const;
+ const override;
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const;
+ my_decimal *) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *,
- date_mode_t fuzzydate) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
- bool Item_func_in_fix_comparator_compatible_types(THD *thd,
- Item_func_in *) const;
-
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
- bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
- bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
- bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
- bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
- bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
+ date_mode_t fuzzydate)
+ const override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs)
+ const override;
+ bool Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *)
+ const override;
+
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
+ bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
+ bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
+ bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
+ bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
+ bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
};
class Type_handler_decimal_result: public Type_handler_numeric
{
public:
- protocol_send_type_t protocol_send_type() const
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_STRING;
}
- Item_result result_type() const { return DECIMAL_RESULT; }
- Item_result cmp_type() const { return DECIMAL_RESULT; }
+ Item_result result_type() const override { return DECIMAL_RESULT; }
+ Item_result cmp_type() const override { return DECIMAL_RESULT; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *) const
+ override
+ {
+ return DYN_COL_DECIMAL;
+ }
virtual ~Type_handler_decimal_result() {};
- const Type_handler *type_handler_for_comparison() const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const
+ const Type_handler *type_handler_for_comparison() const override;
+ int stored_field_cmp_to_item(THD *, Field *field, Item *item) const override
{
VDec item_val(item);
return item_val.is_null() ? 0 : my_decimal(field).cmp(item_val.ptr());
}
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const;
- Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const;
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const;
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const;
- uint32 max_display_length(const Item *item) const;
- uint32 Item_decimal_notation_int_digits(const Item *item) const;
+ const Item *outer) const override;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
+ Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *)
+ const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const override;
+ void sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override;
+ uint32 max_display_length(const Item *item) const override;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override;
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
+ const Type_cast_attributes &attr) const override;
bool Item_const_eq(const Item_const *a, const Item_const *b,
- bool binary_cmp) const;
+ bool binary_cmp) const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const
+ Item *a, Item *b) const override
{
VDec va(a), vb(b);
return va.ptr() && vb.ptr() && !va.cmp(vb);
}
- uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
+ uint Item_decimal_precision(const Item *item) const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
- const st_value *value) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const st_value *value) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_str(item, protocol, buf);
}
- void Item_update_null_value(Item *item) const;
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
+ void Item_update_null_value(Item *item) const override;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions) const
+ override;
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const
+ override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
- bool Item_val_bool(Item *item) const
+ Item **items, uint nitems)
+ const override;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const override;
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override;
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override;
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance*) const override;
+ bool Item_val_bool(Item *item) const override
{
return VDec(item).to_bool();
}
void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
- MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
- longlong Item_val_int_signed_typecast(Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const override;
+ longlong Item_val_int_signed_typecast(Item *item) const override;
+ longlong Item_val_int_unsigned_typecast(Item *item) const override
{
return VDec(item).to_longlong(true);
}
- String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
+ String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str)
+ const override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
+ String *) const override;
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const;
+ const override;
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const;
+ const override;
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const;
+ my_decimal *) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *,
- date_mode_t fuzzydate) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
- bool Item_func_in_fix_comparator_compatible_types(THD *thd,
- Item_func_in *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
- bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
- bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
- bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
- bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
- bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
+ date_mode_t fuzzydate)
+ const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *)
+ const override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs)
+ const override;
+ bool Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *)
+ const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
+ bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
+ bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
+ bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
+ bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
+ bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
};
@@ -4606,93 +5054,121 @@ public:
class Type_handler_int_result: public Type_handler_numeric
{
public:
- Item_result result_type() const { return INT_RESULT; }
- Item_result cmp_type() const { return INT_RESULT; }
- bool is_order_clause_position_type() const { return true; }
- bool is_limit_clause_valid_type() const { return true; }
+ Item_result result_type() const override { return INT_RESULT; }
+ Item_result cmp_type() const override { return INT_RESULT; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) const override
+ {
+ return attr->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT;
+ }
+ bool is_order_clause_position_type() const override { return true; }
+ bool is_limit_clause_valid_type() const override { return true; }
virtual ~Type_handler_int_result() {}
- const Type_handler *type_handler_for_comparison() const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
+ const Type_handler *type_handler_for_comparison() const override;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const;
- Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const;
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const;
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const;
+ const Item *outer) const override;
+ Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE_SHARE *share) const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ void sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override;
bool Item_const_eq(const Item_const *a, const Item_const *b,
- bool binary_cmp) const;
+ bool binary_cmp) const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- uint Item_decimal_precision(const Item *item) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
+ Item *a, Item *b) const override;
+ uint Item_decimal_precision(const Item *item) const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
- const st_value *value) const;
- void Item_update_null_value(Item *item) const;
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
+ const st_value *value) const override;
+ void Item_update_null_value(Item *item) const override;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions) const override;
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
- bool Item_val_bool(Item *item) const;
+ Item **items, uint nitems) const override;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override;
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override;
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override;
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override;
+ bool Item_val_bool(Item *item) const override;
void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
- MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
- longlong Item_val_int_signed_typecast(Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const;
- String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const override;
+ longlong Item_val_int_signed_typecast(Item *item) const override;
+ longlong Item_val_int_unsigned_typecast(Item *item) const override;
+ String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
+ String *) const override;
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const;
+ const override;
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const;
+ const override;
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const;
+ my_decimal *) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *,
- date_mode_t fuzzydate) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
+ date_mode_t fuzzydate) const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const override;
bool Item_func_in_fix_comparator_compatible_types(THD *thd,
- Item_func_in *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
- bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
- bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
- bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
- bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
- bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
-
+ Item_func_in *) const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
+ bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
+ bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
+ bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
+ bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
+ bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
+ const Vers_type_handler *vers() const override { return &vers_type_trx; }
};
class Type_handler_general_purpose_int: public Type_handler_int_result
{
public:
- bool type_can_have_auto_increment_attribute() const { return true; }
- virtual const Type_limits_int *
- type_limits_int_by_unsigned_flag(bool unsigned_flag) const= 0;
- uint32 max_display_length(const Item *item) const;
- uint32 Item_decimal_notation_int_digits(const Item *item) const;
- bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
+ bool type_can_have_auto_increment_attribute() const override { return true; }
+ virtual const Type_limits_int *type_limits_int() const= 0;
+ uint32 max_display_length(const Item *item) const override
+ {
+ return type_limits_int()->char_length();
+ }
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override;
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ return partition_field_check_result_type(item_expr, INT_RESULT);
+ }
+ bool partition_field_append_value(String *str,
+ Item *item_expr,
+ CHARSET_INFO *field_cs,
+ partition_value_print_mode_t)
+ const override;
+ const Vers_type_handler *vers() const override { return &vers_type_trx; }
};
@@ -4702,67 +5178,76 @@ protected:
uint Item_decimal_scale_with_seconds(const Item *item) const;
uint Item_divisor_precision_increment_with_seconds(const Item *) const;
public:
- Item_result result_type() const { return STRING_RESULT; }
- Item_result cmp_type() const { return TIME_RESULT; }
+ Item_result result_type() const override { return STRING_RESULT; }
+ Item_result cmp_type() const override { return TIME_RESULT; }
virtual ~Type_handler_temporal_result() {}
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const;
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ void sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override;
bool Item_const_eq(const Item_const *a, const Item_const *b,
- bool binary_cmp) const;
+ bool binary_cmp) const override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
- const st_value *value) const;
- uint32 max_display_length(const Item *item) const;
- uint32 Item_decimal_notation_int_digits(const Item *item) const;
+ const st_value *value) const override;
+ uint32 max_display_length(const Item *item) const override;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override;
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) const;
+ Item *source_expr, Item *source_const)
+ const override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const;
+ const Item *outer) const override;
bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
- Item **items, uint nitems) const;
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
- bool Item_val_bool(Item *item) const;
+ Item **items, uint nitems)
+ const override;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const override;
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override;
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override;
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *)const override;
+ bool Item_val_bool(Item *item) const override;
void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
- MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
- longlong Item_val_int_signed_typecast(Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const;
- String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const override;
+ longlong Item_val_int_signed_typecast(Item *item) const override;
+ longlong Item_val_int_unsigned_typecast(Item *item) const override;
+ String *Item_func_hex_val_str_ascii(Item_func_hex *, String *)const override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
+ String *) const override;
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const;
+ const override;
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const;
+ const override;
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const;
+ my_decimal *) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *,
- date_mode_t fuzzydate) const;
+ date_mode_t) const override;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
- MYSQL_TIME *, date_mode_t fuzzydate) const;
- bool Item_func_between_fix_length_and_dec(Item_func_between *func) const;
- bool Item_func_in_fix_comparator_compatible_types(THD *thd,
- Item_func_in *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
- bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
- bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
- bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
- bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
- bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
- bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
+ MYSQL_TIME *, date_mode_t) const override;
+ bool Item_func_between_fix_length_and_dec(Item_func_between *)const override;
+ bool Item_func_in_fix_comparator_compatible_types(THD *, Item_func_in *)
+ const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
+ bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
+ bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
+ bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
+ bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
+ bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
+ const Vers_type_handler *vers() const override;
};
@@ -4770,143 +5255,173 @@ class Type_handler_string_result: public Type_handler
{
uint Item_temporal_precision(THD *thd, Item *item, bool is_time) const;
public:
- protocol_send_type_t protocol_send_type() const
+ const Name &default_value() const override;
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_STRING;
}
- Item_result result_type() const { return STRING_RESULT; }
- Item_result cmp_type() const { return STRING_RESULT; }
- CHARSET_INFO *charset_for_protocol(const Item *item) const;
+ Item_result result_type() const override { return STRING_RESULT; }
+ Item_result cmp_type() const override { return STRING_RESULT; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *) const
+ override
+ {
+ return DYN_COL_STRING;
+ }
+ CHARSET_INFO *charset_for_protocol(const Item *item) const override;
virtual ~Type_handler_string_result() {}
- const Type_handler *type_handler_for_comparison() const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
+ const Type_handler *type_handler_for_comparison() const override;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const
+ override;
const Type_handler *
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
- CHARSET_INFO *cs) const;
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const;
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const;
- bool union_element_finalize(const Item * item) const;
+ CHARSET_INFO *cs) const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ void sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override;
+ bool is_packable()const override { return true; }
+ bool union_element_finalize(const Item * item) const override;
+ uint calc_key_length(const Column_definition &def) const override;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags) const override;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file,
const Schema_specification_st *schema)
- const;
- uint32 max_display_length(const Item *item) const;
-/*
+ const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ uint32 max_display_length(const Item *item) const override;
+ /*
The next method returns 309 for long stringified doubles in scientific
notation, e.g. FORMAT('1e308', 2).
-*/
- uint32 Item_decimal_notation_int_digits(const Item *item) const { return 309; }
+ */
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override
+ { return 309; }
bool Item_const_eq(const Item_const *a, const Item_const *b,
- bool binary_cmp) const;
+ bool binary_cmp) const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- uint Item_time_precision(THD *thd, Item *item) const
+ Item *a, Item *b) const override;
+ uint Item_time_precision(THD *thd, Item *item) const override
{
return Item_temporal_precision(thd, item, true);
}
- uint Item_datetime_precision(THD *thd, Item *item) const
+ uint Item_datetime_precision(THD *thd, Item *item) const override
{
return Item_temporal_precision(thd, item, false);
}
- uint Item_decimal_precision(const Item *item) const;
- void Item_update_null_value(Item *item) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
- void Item_param_setup_conversion(THD *thd, Item_param *) const;
+ uint Item_decimal_precision(const Item *item) const override;
+ void Item_update_null_value(Item *item) const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
+ void Item_param_setup_conversion(THD *thd, Item_param *) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
bool Item_param_set_from_value(THD *thd,
Item_param *param,
const Type_all_attributes *attr,
- const st_value *value) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const st_value *value) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_str(item, protocol, buf);
}
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- String *print_item_value(THD *thd, Item *item, String *str) const
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions) const
+ override;
+ String *print_item_value(THD *thd, Item *item, String *str) const override
{
return print_item_value_csstr(thd, item, str);
}
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) const;
+ Item *source_expr, Item *source_const) const
+ override;
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const;
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
+ const Item *outer) const override;
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const
+ override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const;
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
- bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const;
- bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const;
- bool Item_val_bool(Item *item) const;
+ Item **items, uint nitems) const
+ override;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override;
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override;
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override;
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override;
+ bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const
+ override;
+ bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const
+ override;
+ bool Item_val_bool(Item *item) const override;
void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
- MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
- longlong Item_val_int_signed_typecast(Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const;
- String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const;
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const override;
+ longlong Item_val_int_signed_typecast(Item *item) const override;
+ longlong Item_val_int_unsigned_typecast(Item *item) const override;
+ String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const
+ override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
+ String *) const override;
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const;
+ const override;
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const;
+ const override;
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const;
+ my_decimal *) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *,
- date_mode_t fuzzydate) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ date_mode_t fuzzydate)
+ const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const
+ override;
+ double Item_func_min_max_val_real(Item_func_min_max *) const override;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
+ my_decimal *) const override;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
- MYSQL_TIME *, date_mode_t fuzzydate) const;
- bool Item_func_between_fix_length_and_dec(Item_func_between *func) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
- bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
- bool Item_func_in_fix_comparator_compatible_types(THD *thd,
- Item_func_in *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
- bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const;
- bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const;
- bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const;
- bool Item_func_div_fix_length_and_dec(Item_func_div *) const;
- bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const;
+ MYSQL_TIME *, date_mode_t fuzzydate) const
+ override;
+ bool Item_func_between_fix_length_and_dec(Item_func_between *func) const
+ override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const
+ override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const
+ override;
+ bool Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *)
+ const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
+ bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const override;
+ bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const override;
+ bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const override;
+ bool Item_func_div_fix_length_and_dec(Item_func_div *) const override;
+ bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const override;
+ const Vers_type_handler *vers() const override;
};
class Type_handler_general_purpose_string: public Type_handler_string_result
{
public:
- bool is_general_purpose_string_type() const { return true; }
- bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
+ bool is_general_purpose_string_type() const override { return true; }
};
@@ -4932,545 +5447,622 @@ public:
class Type_handler_tiny: public Type_handler_general_purpose_int
{
- static const Name m_name_tiny;
- static const Type_limits_int m_limits_sint8;
- static const Type_limits_int m_limits_uint8;
public:
virtual ~Type_handler_tiny() {}
- const Name name() const { return m_name_tiny; }
- enum_field_types field_type() const { return MYSQL_TYPE_TINY; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_TINY; }
+ const Type_handler *type_handler_unsigned() const override;
+ const Type_handler *type_handler_signed() const override;
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_TINY;
}
- const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
- {
- return unsigned_fl ? &m_limits_uint8 : &m_limits_sint8;
- }
- uint32 calc_pack_length(uint32 length) const { return 1; }
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const Type_limits_int *type_limits_int() const override;
+ uint32 calc_pack_length(uint32 length) const override { return 1; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 4; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_tiny(item, protocol, buf);
}
- Field *make_conversion_table_field(TABLE *TABLE, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TINY); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
+};
+
+
+class Type_handler_utiny: public Type_handler_tiny
+{
+public:
+ uint flags() const override { return UNSIGNED_FLAG; }
+ const Type_limits_int *type_limits_int() const override;
};
class Type_handler_short: public Type_handler_general_purpose_int
{
- static const Name m_name_short;
- static const Type_limits_int m_limits_sint16;
- static const Type_limits_int m_limits_uint16;
public:
virtual ~Type_handler_short() {}
- const Name name() const { return m_name_short; }
- enum_field_types field_type() const { return MYSQL_TYPE_SHORT; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_SHORT; }
+ const Type_handler *type_handler_unsigned() const override;
+ const Type_handler *type_handler_signed() const override;
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_SHORT;
}
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_short(item, protocol, buf);
}
- const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
- {
- return unsigned_fl ? &m_limits_uint16 : &m_limits_sint16;
- }
- uint32 calc_pack_length(uint32 length) const { return 2; }
- Field *make_conversion_table_field(TABLE *TABLE, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ const Type_limits_int *type_limits_int() const override;
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 6; }
+ uint32 calc_pack_length(uint32 length) const override{ return 2; }
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_SHORT); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
+};
+
+
+class Type_handler_ushort: public Type_handler_short
+{
+public:
+ uint flags() const override { return UNSIGNED_FLAG; }
+ const Type_limits_int *type_limits_int() const override;
};
class Type_handler_long: public Type_handler_general_purpose_int
{
- static const Name m_name_int;
- static const Type_limits_int m_limits_sint32;
- static const Type_limits_int m_limits_uint32;
public:
virtual ~Type_handler_long() {}
- const Name name() const { return m_name_int; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONG; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_LONG; }
+ const Type_handler *type_handler_unsigned() const override;
+ const Type_handler *type_handler_signed() const override;
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_LONG;
}
- const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
- {
- return unsigned_fl ? &m_limits_uint32 : &m_limits_sint32;
- }
- uint32 calc_pack_length(uint32 length) const { return 4; }
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const Type_limits_int *type_limits_int() const override;
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 11; }
+ uint32 calc_pack_length(uint32 length) const override { return 4; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_long(item, protocol, buf);
}
- Field *make_conversion_table_field(TABLE *TABLE, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_LONG); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
+};
+
+
+class Type_handler_ulong: public Type_handler_long
+{
+public:
+ uint flags() const override { return UNSIGNED_FLAG; }
+ const Type_limits_int *type_limits_int() const override;
};
class Type_handler_bool: public Type_handler_long
{
- static const Name m_name_bool;
public:
- const Name name() const { return m_name_bool; }
- bool is_bool_type() const { return true; }
- void Item_update_null_value(Item *item) const;
- bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const;
+ bool is_bool_type() const override { return true; }
+ const Type_handler *type_handler_unsigned() const override;
+ const Type_handler *type_handler_signed() const override;
+ void Item_update_null_value(Item *item) const override;
+ bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const override;
};
class Type_handler_longlong: public Type_handler_general_purpose_int
{
- static const Name m_name_longlong;
- static const Type_limits_int m_limits_sint64;
- static const Type_limits_int m_limits_uint64;
public:
virtual ~Type_handler_longlong() {}
- const Name name() const { return m_name_longlong; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override{ return MYSQL_TYPE_LONGLONG; }
+ const Type_handler *type_handler_unsigned() const override;
+ const Type_handler *type_handler_signed() const override;
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_LONGLONG;
}
- const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
- {
- return unsigned_fl ? &m_limits_uint64 : &m_limits_sint64;
- }
- uint32 calc_pack_length(uint32 length) const { return 8; }
+ const Type_limits_int *type_limits_int() const override;
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 20; }
+ uint32 calc_pack_length(uint32 length) const override { return 8; }
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const Type_cast_attributes &attr) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_longlong(item, protocol, buf);
}
- Field *make_conversion_table_field(TABLE *TABLE, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{
return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_LONGLONG);
}
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
};
-class Type_handler_vers_trx_id: public Type_handler_longlong
+class Type_handler_ulonglong: public Type_handler_longlong
+{
+public:
+ uint flags() const override { return UNSIGNED_FLAG; }
+ const Type_limits_int *type_limits_int() const override;
+};
+
+
+class Type_handler_vers_trx_id: public Type_handler_ulonglong
{
public:
virtual ~Type_handler_vers_trx_id() {}
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
};
class Type_handler_int24: public Type_handler_general_purpose_int
{
- static const Name m_name_mediumint;
- static const Type_limits_int m_limits_sint24;
- static const Type_limits_int m_limits_uint24;
public:
virtual ~Type_handler_int24() {}
- const Name name() const { return m_name_mediumint; }
- enum_field_types field_type() const { return MYSQL_TYPE_INT24; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_INT24; }
+ const Type_handler *type_handler_unsigned() const override;
+ const Type_handler *type_handler_signed() const override;
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_LONG;
}
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_long(item, protocol, buf);
}
- const Type_limits_int *type_limits_int_by_unsigned_flag(bool unsigned_fl) const
- {
- return unsigned_fl ? &m_limits_uint24 : &m_limits_sint24;
- }
- uint32 calc_pack_length(uint32 length) const { return 3; }
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ const Type_limits_int *type_limits_int() const override;
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 9; }
+ uint32 calc_pack_length(uint32 length) const override { return 3; }
+ Field *make_conversion_table_field(MEM_ROOT *mem_root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_INT24); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
+};
+
+
+class Type_handler_uint24: public Type_handler_int24
+{
+public:
+ uint flags() const override { return UNSIGNED_FLAG; }
+ const Type_limits_int *type_limits_int() const override;
};
class Type_handler_year: public Type_handler_int_result
{
- static const Name m_name_year;
public:
virtual ~Type_handler_year() {}
- const Name name() const { return m_name_year; }
- enum_field_types field_type() const { return MYSQL_TYPE_YEAR; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_YEAR; }
+ uint flags() const override { return UNSIGNED_FLAG; }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_SHORT;
}
- uint32 max_display_length(const Item *item) const;
- uint32 Item_decimal_notation_int_digits(const Item *item) const { return 4; };
- uint32 calc_pack_length(uint32 length) const { return 1; }
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ uint32 max_display_length(const Item *item) const override;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override
+ { return 4; };
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 4; }
+ uint32 calc_pack_length(uint32 length) const override { return 1; }
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_short(item, protocol, buf);
}
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
void Column_definition_reuse_fix_attributes(THD *thd,
Column_definition *c,
- const Field *field) const;
+ const Field *field)
+ const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_YEAR); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ uint32 flags) const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *)const override;
void Item_get_date(THD *thd, Item *item, Temporal::Warn *warn,
- MYSQL_TIME *ltime, date_mode_t fuzzydate) const;
+ MYSQL_TIME *ltime, date_mode_t fuzzydate) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *item,
Temporal::Warn *,
MYSQL_TIME *to,
- date_mode_t fuzzydate) const;
+ date_mode_t fuzzydate)
+ const override;
+ const Vers_type_handler *vers() const override { return NULL; }
};
class Type_handler_bit: public Type_handler_int_result
{
- static const Name m_name_bit;
public:
virtual ~Type_handler_bit() {}
- const Name name() const { return m_name_bit; }
- enum_field_types field_type() const { return MYSQL_TYPE_BIT; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_BIT; }
+ uint flags() const override { return UNSIGNED_FLAG; }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_STRING;
}
- uint32 max_display_length(const Item *item) const;
- uint32 Item_decimal_notation_int_digits(const Item *item) const;
+ uint32 max_display_length(const Item *item) const override;
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override;
static uint32 Bit_decimal_notation_int_digits_by_nbits(uint nbits);
- uint32 calc_pack_length(uint32 length) const { return length / 8; }
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override { return length / 8; }
+ uint calc_key_length(const Column_definition &def) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_str(item, protocol, buf);
}
- String *print_item_value(THD *thd, Item *item, String *str) const
+ String *print_item_value(THD *thd, Item *item, String *str) const override
{
return print_item_value_csstr(thd, item, str);
}
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ void show_binlog_type(const Conv_source &src, const Field &, String *str)
+ const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags) const override;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file,
const Schema_specification_st *schema)
- const;
+ const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ ulonglong table_flags) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
- bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
+ uint32 flags) const override;
};
class Type_handler_float: public Type_handler_real_result
{
- static const Name m_name_float;
public:
virtual ~Type_handler_float() {}
- const Name name() const { return m_name_float; }
- enum_field_types field_type() const { return MYSQL_TYPE_FLOAT; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_FLOAT; }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_FLOAT;
}
- bool type_can_have_auto_increment_attribute() const { return true; }
- uint32 max_display_length(const Item *item) const { return 25; }
- uint32 Item_decimal_notation_int_digits(const Item *item) const { return 39; }
- uint32 calc_pack_length(uint32 length) const { return sizeof(float); }
+ bool type_can_have_auto_increment_attribute() const override { return true; }
+ uint32 max_display_length(const Item *item) const override { return 25; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 12; }
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override
+ { return 39; }
+ uint32 calc_pack_length(uint32 length) const override { return sizeof(float); }
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const Type_cast_attributes &attr) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_float(item, protocol, buf);
}
- Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_real(c, MYSQL_TYPE_FLOAT); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ String *) const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *)
+ const override;
};
class Type_handler_double: public Type_handler_real_result
{
- static const Name m_name_double;
public:
virtual ~Type_handler_double() {}
- const Name name() const { return m_name_double; }
- enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- protocol_send_type_t protocol_send_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_DOUBLE; }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_DOUBLE;
}
- bool type_can_have_auto_increment_attribute() const { return true; }
- uint32 max_display_length(const Item *item) const { return 53; }
- uint32 Item_decimal_notation_int_digits(const Item *item) const { return 309; }
- uint32 calc_pack_length(uint32 length) const { return sizeof(double); }
+ bool type_can_have_auto_increment_attribute() const override { return true; }
+ uint32 max_display_length(const Item *item) const override { return 53; }
+ uint32 Item_decimal_notation_int_digits(const Item *item) const override
+ { return 309; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 22; }
+ uint32 calc_pack_length(uint32 length) const override
+ {
+ return sizeof(double);
+ }
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const Type_cast_attributes &attr) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_double(item, protocol, buf);
}
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_real(c, MYSQL_TYPE_DOUBLE); }
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
+ String *) const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *)
+ const override;
};
class Type_handler_time_common: public Type_handler_temporal_result
{
- static const Name m_name_time;
public:
virtual ~Type_handler_time_common() { }
- const Name name() const { return m_name_time; }
- enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
- protocol_send_type_t protocol_send_type() const
+ const Name &default_value() const override;
+ enum_field_types field_type() const override { return MYSQL_TYPE_TIME; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ return DYN_COL_TIME;
+ }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_TIME;
}
- enum_mysql_timestamp_type mysql_timestamp_type() const
+ enum_mysql_timestamp_type mysql_timestamp_type() const override
{
return MYSQL_TIMESTAMP_TIME;
}
- bool is_val_native_ready() const { return true; }
- const Type_handler *type_handler_for_native_format() const;
- int cmp_native(const Native &a, const Native &b) const;
- bool Item_val_native_with_conversion(THD *thd, Item *, Native *to) const;
- bool Item_val_native_with_conversion_result(THD *thd, Item *, Native *to) const;
- bool Item_param_val_native(THD *thd, Item_param *item, Native *to) const;
+ bool is_val_native_ready() const override { return true; }
+ const Type_handler *type_handler_for_native_format() const override;
+ int cmp_native(const Native &a, const Native &b) const override;
+ bool Item_val_native_with_conversion(THD *thd, Item *, Native *to)
+ const override;
+ bool Item_val_native_with_conversion_result(THD *thd, Item *, Native *to)
+ const override;
+ bool Item_param_val_native(THD *thd, Item_param *item, Native *to)
+ const override;
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ return partition_field_check_result_type(item_expr, STRING_RESULT);
+ }
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
- CHARSET_INFO *cs, bool send_error) const;
+ CHARSET_INFO *cs, bool send_error)
+ const override;
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
+ const Type_cast_attributes &attr)
+ const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- uint Item_decimal_scale(const Item *item) const
+ Item *a, Item *b) const override;
+ uint Item_decimal_scale(const Item *item) const override
{
return Item_decimal_scale_with_seconds(item);
}
- uint Item_decimal_precision(const Item *item) const;
- uint Item_divisor_precision_increment(const Item *item) const
+ uint Item_decimal_precision(const Item *item) const override;
+ uint Item_divisor_precision_increment(const Item *item) const override
{
return Item_divisor_precision_increment_with_seconds(item);
}
- const Type_handler *type_handler_for_comparison() const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
- void Column_definition_implicit_upgrade(Column_definition *c) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ const Type_handler *type_handler_for_comparison() const override;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
+ const override;
+ void Column_definition_implicit_upgrade(Column_definition *c) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_time(item, protocol, buf);
}
- void Item_update_null_value(Item *item) const;
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- String *print_item_value(THD *thd, Item *item, String *str) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- longlong Item_val_int_unsigned_typecast(Item *item) const;
+ void Item_update_null_value(Item *item) const override;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions)
+ const override;
+ String *print_item_value(THD *thd, Item *item, String *str) const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ longlong Item_val_int_unsigned_typecast(Item *item) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
+ Item **items, uint nitems)
+ const override;
String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *,
- String *) const;
+ String *) const override;
double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *)
- const;
+ const override;
longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *)
- const;
+ const override;
my_decimal *Item_func_hybrid_field_type_val_decimal(
Item_func_hybrid_field_type *,
- my_decimal *) const;
+ my_decimal *) const override;
void Item_func_hybrid_field_type_get_date(THD *,
Item_func_hybrid_field_type *,
Temporal::Warn *,
MYSQL_TIME *,
- date_mode_t fuzzydate) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ date_mode_t fuzzydate)
+ const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override;
+ double Item_func_min_max_val_real(Item_func_min_max *) const override;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
+ my_decimal *) const override;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
- MYSQL_TIME *, date_mode_t fuzzydate) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
- void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ MYSQL_TIME *, date_mode_t fuzzydate)
+ const override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp)
+ const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs)
+ const override;
+ void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len)
+ const override;
};
@@ -5481,25 +6073,29 @@ class Type_handler_time: public Type_handler_time_common
public:
static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; }
virtual ~Type_handler_time() {}
- const Name version() const { return m_version_mariadb53; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ const Name version() const override { return version_mariadb53(); }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return MIN_TIME_WIDTH; }
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_TIME); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
@@ -5507,26 +6103,29 @@ class Type_handler_time2: public Type_handler_time_common
{
public:
virtual ~Type_handler_time2() {}
- const Name version() const { return m_version_mysql56; }
- enum_field_types real_field_type() const { return MYSQL_TYPE_TIME2; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ const Name version() const override { return version_mysql56(); }
+ enum_field_types real_field_type() const override { return MYSQL_TYPE_TIME2; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_TIME2); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
@@ -5535,93 +6134,126 @@ class Type_handler_temporal_with_date: public Type_handler_temporal_result
public:
virtual ~Type_handler_temporal_with_date() {}
Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
- CHARSET_INFO *cs, bool send_error) const;
+ CHARSET_INFO *cs, bool send_error)
+ const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ Item *a, Item *b) const override;
+ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
+ const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value)
+ const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_date(item, protocol, buf);
}
- void Item_update_null_value(Item *item) const;
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
+ void Item_update_null_value(Item *item) const override;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions)
+ const override;
+ Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp)
+ const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs)
+ const override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
};
class Type_handler_date_common: public Type_handler_temporal_with_date
{
- static const Name m_name_date;
public:
virtual ~Type_handler_date_common() {}
- const Name name() const { return m_name_date; }
- const Type_handler *type_handler_for_comparison() const;
- enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
- protocol_send_type_t protocol_send_type() const
+ const Name &default_value() const override;
+ const Type_handler *type_handler_for_comparison() const override;
+ enum_field_types field_type() const override { return MYSQL_TYPE_DATE; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return 3; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ return DYN_COL_DATE;
+ }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_DATE;
}
- enum_mysql_timestamp_type mysql_timestamp_type() const
+ enum_mysql_timestamp_type mysql_timestamp_type() const override
{
return MYSQL_TIMESTAMP_DATE;
}
- bool cond_notnull_field_isnull_to_field_eq_zero() const
+ bool cond_notnull_field_isnull_to_field_eq_zero() const override
{
return true;
}
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ return partition_field_check_result_type(item_expr, STRING_RESULT);
+ }
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Item_literal *create_literal_item(THD *thd, const char *str, size_t length,
- CHARSET_INFO *cs, bool send_error) const;
+ CHARSET_INFO *cs, bool send_error)
+ const override;
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
- uint Item_decimal_precision(const Item *item) const;
- String *print_item_value(THD *thd, Item *item, String *str) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ const Type_cast_attributes &attr)
+ const override;
+ bool validate_implicit_default_value(THD *thd,
+ const Column_definition &def)
+ const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ uint Item_decimal_precision(const Item *item) const override;
+ String *print_item_value(THD *thd, Item *item, String *str) const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override;
+ double Item_func_min_max_val_real(Item_func_min_max *) const override;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ my_decimal *) const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
+ Item **items, uint nitems) const
+ override;
bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func,
- Item **items, uint nitems) const;
+ Item **items, uint nitems) const
+ override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
};
class Type_handler_date: public Type_handler_date_common
{
public:
virtual ~Type_handler_date() {}
- uint32 calc_pack_length(uint32 length) const { return 4; }
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ uint32 calc_pack_length(uint32 length) const override { return 4; }
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATE); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
@@ -5629,81 +6261,111 @@ class Type_handler_newdate: public Type_handler_date_common
{
public:
virtual ~Type_handler_newdate() {}
- enum_field_types real_field_type() const { return MYSQL_TYPE_NEWDATE; }
- uint32 calc_pack_length(uint32 length) const { return 3; }
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ enum_field_types real_field_type() const override
+ {
+ return MYSQL_TYPE_NEWDATE;
+ }
+ uint32 calc_pack_length(uint32 length) const override { return 3; }
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_NEWDATE); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
class Type_handler_datetime_common: public Type_handler_temporal_with_date
{
- static const Name m_name_datetime;
public:
virtual ~Type_handler_datetime_common() {}
- const Name name() const { return m_name_datetime; }
- const Type_handler *type_handler_for_comparison() const;
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
- protocol_send_type_t protocol_send_type() const
+ const Name &default_value() const override;
+ const Type_handler *type_handler_for_comparison() const override;
+ enum_field_types field_type() const override
+ {
+ return MYSQL_TYPE_DATETIME;
+ }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ return DYN_COL_DATETIME;
+ }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_DATETIME;
}
- enum_mysql_timestamp_type mysql_timestamp_type() const
+ enum_mysql_timestamp_type mysql_timestamp_type() const override
{
return MYSQL_TIMESTAMP_DATETIME;
}
- bool cond_notnull_field_isnull_to_field_eq_zero() const
+ bool cond_notnull_field_isnull_to_field_eq_zero() const override
{
return true;
}
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ return partition_field_check_result_type(item_expr, STRING_RESULT);
+ }
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
- void Column_definition_implicit_upgrade(Column_definition *c) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
- uint Item_decimal_scale(const Item *item) const
+ const Type_cast_attributes &attr) const override;
+ bool validate_implicit_default_value(THD *thd, const Column_definition &def)
+ const override;
+ void Column_definition_implicit_upgrade(Column_definition *c) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const override;
+ uint Item_decimal_scale(const Item *item) const override
{
return Item_decimal_scale_with_seconds(item);
}
- uint Item_decimal_precision(const Item *item) const;
- uint Item_divisor_precision_increment(const Item *item) const
+ uint Item_decimal_precision(const Item *item) const override;
+ uint Item_divisor_precision_increment(const Item *item) const override
{
return Item_divisor_precision_increment_with_seconds(item);
}
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_datetime(item, protocol, buf);
}
- String *print_item_value(THD *thd, Item *item, String *str) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
- my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ String *print_item_value(THD *thd, Item *item, String *str) const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override;
+ double Item_func_min_max_val_real(Item_func_min_max *) const override;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
+ my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, my_decimal *)
+ const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
- void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ Item **items, uint nitems)
+ const override;
+ void Item_param_set_param_func(Item_param *param, uchar **pos, ulong len)
+ const override;
};
@@ -5714,25 +6376,29 @@ class Type_handler_datetime: public Type_handler_datetime_common
public:
static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; }
virtual ~Type_handler_datetime() {}
- const Name version() const { return m_version_mariadb53; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ const Name version() const override { return version_mariadb53(); }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return MAX_DATETIME_WIDTH; }
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATETIME); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
@@ -5740,106 +6406,139 @@ class Type_handler_datetime2: public Type_handler_datetime_common
{
public:
virtual ~Type_handler_datetime2() {}
- const Name version() const { return m_version_mysql56; }
- enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ const Name version() const override { return version_mysql56(); }
+ enum_field_types real_field_type() const override
+ {
+ return MYSQL_TYPE_DATETIME2;
+ }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATETIME2); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
class Type_handler_timestamp_common: public Type_handler_temporal_with_date
{
- static const Name m_name_timestamp;
protected:
bool TIME_to_native(THD *, const MYSQL_TIME *from, Native *to, uint dec) const;
public:
virtual ~Type_handler_timestamp_common() {}
- const Name name() const { return m_name_timestamp; }
- const Type_handler *type_handler_for_comparison() const;
- const Type_handler *type_handler_for_native_format() const;
- enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; }
- protocol_send_type_t protocol_send_type() const
+ const Name &default_value() const override;
+ const Type_handler *type_handler_for_comparison() const override;
+ const Type_handler *type_handler_for_native_format() const override;
+ enum_field_types field_type() const override { return MYSQL_TYPE_TIMESTAMP; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ return DYN_COL_DATETIME;
+ }
+ protocol_send_type_t protocol_send_type() const override
{
return PROTOCOL_SEND_DATETIME;
}
- enum_mysql_timestamp_type mysql_timestamp_type() const
+ enum_mysql_timestamp_type mysql_timestamp_type() const override
{
return MYSQL_TIMESTAMP_DATETIME;
}
- bool is_val_native_ready() const
+ bool is_val_native_ready() const override
{
return true;
}
- bool is_timestamp_type() const
+ bool is_timestamp_type() const override
{
return true;
}
- void Column_definition_implicit_upgrade(Column_definition *c) const;
+ void Column_definition_implicit_upgrade(Column_definition *c) const override;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const override;
bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr,
- Item *a, Item *b) const;
- bool Item_val_native_with_conversion(THD *thd, Item *, Native *to) const;
- bool Item_val_native_with_conversion_result(THD *thd, Item *, Native *to) const;
- bool Item_param_val_native(THD *thd, Item_param *item, Native *to) const;
- int cmp_native(const Native &a, const Native &b) const;
- longlong Item_func_between_val_int(Item_func_between *func) const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const;
- in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const;
- void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field,
- Sort_param *param) const;
- void sortlength(THD *thd,
- const Type_std_attributes *item,
- SORT_FIELD_ATTR *attr) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
- uint Item_decimal_scale(const Item *item) const
+ Item *a, Item *b) const override;
+ bool Item_val_native_with_conversion(THD *thd, Item *, Native *to)
+ const override;
+ bool Item_val_native_with_conversion_result(THD *thd, Item *, Native *to)
+ const override;
+ bool Item_param_val_native(THD *thd, Item_param *item, Native *to)
+ const override;
+ int cmp_native(const Native &a, const Native &b) const override;
+ longlong Item_func_between_val_int(Item_func_between *func) const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
+ cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override;
+ in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs)
+ const override;
+ void make_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ uint make_packed_sort_key_part(uchar *to, Item *item,
+ const SORT_FIELD_ATTR *sort_field,
+ Sort_param *param) const override;
+ void sort_length(THD *thd,
+ const Type_std_attributes *item,
+ SORT_FIELD_ATTR *attr) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
+ uint Item_decimal_scale(const Item *item) const override
{
return Item_decimal_scale_with_seconds(item);
}
- uint Item_decimal_precision(const Item *item) const;
- uint Item_divisor_precision_increment(const Item *item) const
+ uint Item_decimal_precision(const Item *item) const override;
+ uint Item_divisor_precision_increment(const Item *item) const override
{
return Item_divisor_precision_increment_with_seconds(item);
}
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override
{
return Item_send_timestamp(item, protocol, buf);
}
- int Item_save_in_field(Item *item, Field *field, bool no_conversions) const;
- String *print_item_value(THD *thd, Item *item, String *str) const;
- Item_cache *Item_get_cache(THD *thd, const Item *item) const;
- Item_copy *create_item_copy(THD *thd, Item *item) const;
- String *Item_func_min_max_val_str(Item_func_min_max *, String *) const;
- double Item_func_min_max_val_real(Item_func_min_max *) const;
- longlong Item_func_min_max_val_int(Item_func_min_max *) const;
+ int Item_save_in_field(Item *item, Field *field, bool no_conversions)
+ const override;
+ String *print_item_value(THD *thd, Item *item, String *str) const override;
+ Item_cache *Item_get_cache(THD *thd, const Item *item) const override;
+ Item_copy *create_item_copy(THD *thd, Item *item) const override;
+ String *Item_func_min_max_val_str(Item_func_min_max *, String *) const override;
+ double Item_func_min_max_val_real(Item_func_min_max *) const override;
+ longlong Item_func_min_max_val_int(Item_func_min_max *) const override;
my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *,
- my_decimal *) const;
- bool set_comparator_func(Arg_comparator *cmp) const;
+ my_decimal *) const override;
+ bool set_comparator_func(Arg_comparator *cmp) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
+ Item **items, uint nitems)
+ const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
+ uchar **pos, ulong len) const override;
bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*,
- MYSQL_TIME *, date_mode_t fuzzydate) const;
+ MYSQL_TIME *, date_mode_t fuzzydate)
+ const override;
+ bool Column_definition_set_attributes(THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const override;
};
@@ -5850,25 +6549,29 @@ class Type_handler_timestamp: public Type_handler_timestamp_common
public:
static uint sec_part_bytes(uint dec) { return m_sec_part_bytes[dec]; }
virtual ~Type_handler_timestamp() {}
- const Name version() const { return m_version_mariadb53; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ const Name version() const override { return version_mariadb53(); }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ { return MAX_DATETIME_WIDTH; }
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TIMESTAMP); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
@@ -5876,151 +6579,176 @@ class Type_handler_timestamp2: public Type_handler_timestamp_common
{
public:
virtual ~Type_handler_timestamp2() {}
- const Name version() const { return m_version_mysql56; }
- enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ const Name version() const override { return version_mysql56(); }
+ enum_field_types real_field_type() const override
+ {
+ return MYSQL_TYPE_TIMESTAMP2;
+ }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{
return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TIMESTAMP2);
}
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
class Type_handler_olddecimal: public Type_handler_decimal_result
{
- static const Name m_name_decimal;
public:
virtual ~Type_handler_olddecimal() {}
- const Name name() const { return m_name_decimal; }
- enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; }
- uint32 calc_pack_length(uint32 length) const { return length; }
- const Type_handler *type_handler_for_tmp_table(const Item *item) const;
- const Type_handler *type_handler_for_union(const Item *item) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ enum_field_types field_type() const override { return MYSQL_TYPE_DECIMAL; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override { return length; }
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const override;
+ const Type_handler *type_handler_for_union(const Item *item) const override;
+ void show_binlog_type(const Conv_source &src, const Field &, String *str)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_DECIMAL); }
- Field *make_table_field(const LEX_CSTRING *name,
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
class Type_handler_newdecimal: public Type_handler_decimal_result
{
- static const Name m_name_decimal;
public:
virtual ~Type_handler_newdecimal() {}
- const Name name() const { return m_name_decimal; }
- enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ enum_field_types field_type() const override { return MYSQL_TYPE_NEWDECIMAL; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ uint calc_key_length(const Column_definition &def) const override;
+ void show_binlog_type(const Conv_source &src, const Field &, String *str)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags) const override;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file,
const Schema_specification_st *schema)
- const;
+ const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ ulonglong table_flags) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
class Type_handler_null: public Type_handler_general_purpose_string
{
- static const Name m_name_null;
public:
virtual ~Type_handler_null() {}
- const Name name() const { return m_name_null; }
- enum_field_types field_type() const { return MYSQL_TYPE_NULL; }
- const Type_handler *type_handler_for_comparison() const;
- const Type_handler *type_handler_for_tmp_table(const Item *item) const;
- const Type_handler *type_handler_for_union(const Item *) const;
- uint32 max_display_length(const Item *item) const { return 0; }
- uint32 calc_pack_length(uint32 length) const { return 0; }
+ enum_field_types field_type() const override { return MYSQL_TYPE_NULL; }
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ return DYN_COL_NULL;
+ }
+ const Type_handler *type_handler_for_comparison() const override;
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const override;
+ const Type_handler *type_handler_for_union(const Item *) const override;
+ uint32 max_display_length(const Item *item) const override { return 0; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override
+ {
+ return 0;
+ }
+ uint32 calc_pack_length(uint32 length) const override { return 0; }
bool Item_const_eq(const Item_const *a, const Item_const *b,
- bool binary_cmp) const;
- bool Item_save_in_value(THD *thd, Item *item, st_value *value) const;
- bool Item_send(Item *item, Protocol *protocol, st_value *buf) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ bool binary_cmp) const override;
+ bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override;
+ bool Item_send(Item *item, Protocol *protocol, st_value *buf) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags) const override;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file,
const Schema_specification_st *schema)
- const;
+ const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_NULL); }
- Field *make_table_field(const LEX_CSTRING *name,
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
class Type_handler_longstr: public Type_handler_general_purpose_string
{
public:
- bool type_can_have_key_part() const
+ bool type_can_have_key_part() const override
{
return true;
}
@@ -6029,61 +6757,82 @@ public:
class Type_handler_string: public Type_handler_longstr
{
- static const Name m_name_char;
public:
virtual ~Type_handler_string() {}
- const Name name() const { return m_name_char; }
- enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
- bool is_param_long_data_type() const { return true; }
- uint32 calc_pack_length(uint32 length) const { return length; }
- const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ enum_field_types field_type() const override { return MYSQL_TYPE_STRING; }
+ ulong KEY_pack_flags(uint column_nr) const override
+ {
+ return HA_PACK_KEY;
+ }
+ bool is_param_long_data_type() const override { return true; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override { return length; }
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const override
{
return varstring_type_handler(item);
}
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ return partition_field_check_result_type(item_expr, STRING_RESULT);
+ }
+ void show_binlog_type(const Conv_source &src, const Field &dst, String *str)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_set_attributes(THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ ulonglong table_flags) const override;
+ bool Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
/* Old varchar */
class Type_handler_var_string: public Type_handler_string
{
- static const Name m_name_var_string;
public:
virtual ~Type_handler_var_string() {}
- const Name name() const { return m_name_var_string; }
- enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; }
- enum_field_types real_field_type() const { return MYSQL_TYPE_STRING; }
- enum_field_types traditional_merge_field_type() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_VAR_STRING; }
+ enum_field_types real_field_type() const override { return MYSQL_TYPE_STRING; }
+ enum_field_types traditional_merge_field_type() const override
{
return MYSQL_TYPE_VARCHAR;
}
- const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const override
{
return varstring_type_handler(item);
}
- void Column_definition_implicit_upgrade(Column_definition *c) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ void show_binlog_type(const Conv_source &src, const Field &dst, String *str)
+ const override;
+ void Column_definition_implicit_upgrade(Column_definition *c) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const
+ ulonglong table_flags) const override
{ return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_STRING); }
- const Type_handler *type_handler_for_union(const Item *item) const
+ const Type_handler *type_handler_for_union(const Item *item) const override
{
return varstring_type_handler(item);
}
@@ -6092,67 +6841,115 @@ public:
class Type_handler_varchar: public Type_handler_longstr
{
- static const Name m_name_varchar;
public:
virtual ~Type_handler_varchar() {}
- const Name name() const { return m_name_varchar; }
- enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- enum_field_types type_code_for_protocol() const
+ enum_field_types field_type() const override { return MYSQL_TYPE_VARCHAR; }
+ ulong KEY_pack_flags(uint column_nr) const override
+ {
+ if (column_nr == 0)
+ return HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
+ return HA_PACK_KEY;
+ }
+ enum_field_types type_code_for_protocol() const override
{
return MYSQL_TYPE_VAR_STRING; // Keep things compatible for old clients
}
- uint32 calc_pack_length(uint32 length) const
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override
{
return (length + (length < 256 ? 1: 2));
}
- const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ const Type_handler *type_handler_for_tmp_table(const Item *item) const override
{
return varstring_type_handler(item);
}
- const Type_handler *type_handler_for_union(const Item *item) const
+ const Type_handler *type_handler_for_union(const Item *item) const override
{
return varstring_type_handler(item);
}
- bool is_param_long_data_type() const { return true; }
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ bool is_param_long_data_type() const override { return true; }
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ return partition_field_check_result_type(item_expr, STRING_RESULT);
+ }
+ void show_binlog_type(const Conv_source &src, const Field &dst, String *str)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ bool Column_definition_set_attributes(THD *thd,
+ Column_definition *def,
+ const Lex_field_type_st &attr,
+ CHARSET_INFO *cs,
+ column_definition_type_t type)
+ const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ ulonglong table_flags) const override;
+ bool Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
- bool adjust_spparam_type(Spvar_definition *def, Item *from) const;
+ uint32 flags) const override;
+ bool adjust_spparam_type(Spvar_definition *def, Item *from) const override;
};
class Type_handler_hex_hybrid: public Type_handler_varchar
{
- static const Name m_name_hex_hybrid;
public:
virtual ~Type_handler_hex_hybrid() {}
- const Name name() const { return m_name_hex_hybrid; }
- const Type_handler *cast_to_int_type_handler() const;
- const Type_handler *type_handler_for_system_time() const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ const Type_handler *cast_to_int_type_handler() const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
};
class Type_handler_varchar_compressed: public Type_handler_varchar
{
public:
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ enum_field_types real_field_type() const override
+ {
+ return MYSQL_TYPE_VARCHAR_COMPRESSED;
+ }
+ ulong KEY_pack_flags(uint column_nr) const override
+ {
+ DBUG_ASSERT(0);
+ return 0;
+ }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override
+ {
+ partition_field_type_not_allowed(field_name);
+ return true;
+ }
+ void show_binlog_type(const Conv_source &src, const Field &dst, String *str)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
+ {
+ DBUG_ASSERT(0);
+ return DYN_COL_STRING;
+ }
};
@@ -6160,313 +6957,288 @@ class Type_handler_blob_common: public Type_handler_longstr
{
public:
virtual ~Type_handler_blob_common() { }
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
+ virtual uint length_bytes() const= 0;
+ ulong KEY_pack_flags(uint column_nr) const override
+ {
+ if (column_nr == 0)
+ return HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY;
+ return HA_PACK_KEY;
+ }
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
const Type_handler *type_handler_for_tmp_table(const Item *item) const
+ override
{
return blob_type_handler(item);
}
- const Type_handler *type_handler_for_union(const Item *item) const
+ const Type_handler *type_handler_for_union(const Item *item) const override
{
return blob_type_handler(item);
}
bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ const Item *outer) const override
{
return false; // Materialization does not work with BLOB columns
}
- bool is_param_long_data_type() const { return true; }
- bool Column_definition_fix_attributes(Column_definition *c) const;
- void Column_definition_reuse_fix_attributes(THD *thd,
- Column_definition *c,
- const Field *field) const;
+ bool is_param_long_data_type() const override { return true; }
+ uint calc_key_length(const Column_definition &def) const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ bool Key_part_spec_init_ft(Key_part_spec *part,
+ const Column_definition &def) const override;
+ bool Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *has_key_needed) const override;
+ bool Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
- void Item_param_setup_conversion(THD *thd, Item_param *) const;
-
+ Item **items, uint nitems) const
+ override;
+ void Item_param_setup_conversion(THD *thd, Item_param *) const override;
+
+ bool partition_field_check(const LEX_CSTRING &field_name,
+ Item *item_expr) const override;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
+ const Vers_type_handler *vers() const override;
};
class Type_handler_tiny_blob: public Type_handler_blob_common
{
- static const Name m_name_tinyblob;
public:
virtual ~Type_handler_tiny_blob() {}
- const Name name() const { return m_name_tinyblob; }
- enum_field_types field_type() const { return MYSQL_TYPE_TINY_BLOB; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ uint length_bytes() const override { return 1; }
+ enum_field_types field_type() const override { return MYSQL_TYPE_TINY_BLOB; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
- uint max_octet_length() const { return UINT_MAX8; }
+ TABLE_SHARE *share) const override;
+ uint max_octet_length() const override { return UINT_MAX8; }
};
class Type_handler_medium_blob: public Type_handler_blob_common
{
- static const Name m_name_mediumblob;
public:
virtual ~Type_handler_medium_blob() {}
- const Name name() const { return m_name_mediumblob; }
- enum_field_types field_type() const { return MYSQL_TYPE_MEDIUM_BLOB; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ uint length_bytes() const override { return 3; }
+ enum_field_types field_type() const override { return MYSQL_TYPE_MEDIUM_BLOB; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
- uint max_octet_length() const { return UINT_MAX24; }
+ TABLE_SHARE *share) const override;
+ uint max_octet_length() const override { return UINT_MAX24; }
};
class Type_handler_long_blob: public Type_handler_blob_common
{
- static const Name m_name_longblob;
public:
virtual ~Type_handler_long_blob() {}
- const Name name() const { return m_name_longblob; }
- enum_field_types field_type() const { return MYSQL_TYPE_LONG_BLOB; }
- uint32 calc_pack_length(uint32 length) const;
+ uint length_bytes() const override { return 4; }
+ enum_field_types field_type() const override { return MYSQL_TYPE_LONG_BLOB; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ const Type_cast_attributes &attr) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
- uint max_octet_length() const { return UINT_MAX32; }
+ TABLE_SHARE *share) const override;
+ uint max_octet_length() const override { return UINT_MAX32; }
};
class Type_handler_blob: public Type_handler_blob_common
{
- static const Name m_name_blob;
public:
virtual ~Type_handler_blob() {}
- const Name name() const { return m_name_blob; }
- enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
- uint32 calc_pack_length(uint32 length) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ uint length_bytes() const override { return 2; }
+ enum_field_types field_type() const override { return MYSQL_TYPE_BLOB; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
- uint max_octet_length() const { return UINT_MAX16; }
+ TABLE_SHARE *share) const override;
+ uint max_octet_length() const override { return UINT_MAX16; }
};
class Type_handler_blob_compressed: public Type_handler_blob
{
public:
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
-};
-
-
-#ifdef HAVE_SPATIAL
-class Type_handler_geometry: public Type_handler_string_result
-{
- static const Name m_name_geometry;
-public:
- virtual ~Type_handler_geometry() {}
- const Name name() const { return m_name_geometry; }
- enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; }
- bool is_param_long_data_type() const { return true; }
- uint32 calc_pack_length(uint32 length) const;
- const Type_handler *type_handler_for_comparison() const;
- bool type_can_have_key_part() const
+ enum_field_types real_field_type() const override
{
- return true;
+ return MYSQL_TYPE_BLOB_COMPRESSED;
}
- bool subquery_type_allows_materialization(const Item *inner,
- const Item *outer) const
+ ulong KEY_pack_flags(uint column_nr) const override
{
- return false; // Materialization does not work with GEOMETRY columns
+ DBUG_ASSERT(0);
+ return 0;
}
- void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
- bool Item_param_set_from_value(THD *thd,
- Item_param *param,
- const Type_all_attributes *attr,
- const st_value *value) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- void
- Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
- uchar *buff) const;
- bool
- Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
- TABLE_SHARE *share,
- const uchar *buffer,
- LEX_CUSTRING *gis_options) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
- void Column_definition_reuse_fix_attributes(THD *thd,
- Column_definition *c,
- const Field *field) const;
- bool Column_definition_prepare_stage1(THD *thd,
- MEM_ROOT *mem_root,
- Column_definition *c,
- handler *file,
- ulonglong table_flags) const;
- bool Column_definition_prepare_stage2(Column_definition *c,
- handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
- const Record_addr &addr,
- const Type_all_attributes &attr,
- TABLE *table) const;
-
- Field *make_table_field_from_def(TABLE_SHARE *share,
- MEM_ROOT *mem_root,
- const LEX_CSTRING *name,
- const Record_addr &addr,
- const Bit_addr &bit,
- const Column_definition_attributes *attr,
- uint32 flags) const;
-
- bool can_return_int() const { return false; }
- bool can_return_decimal() const { return false; }
- bool can_return_real() const { return false; }
- bool can_return_text() const { return false; }
- bool can_return_date() const { return false; }
- bool can_return_time() const { return false; }
- bool is_traditional_type() const
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ void show_binlog_type(const Conv_source &src, const Field &, String *str)
+ const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr)
+ const override
{
- return false;
+ DBUG_ASSERT(0);
+ return DYN_COL_STRING;
}
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
- bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const;
- bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const;
- bool Item_hybrid_func_fix_attributes(THD *thd,
- const char *name,
- Type_handler_hybrid_field_type *h,
- Type_all_attributes *attr,
- Item **items, uint nitems) const;
- bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const;
- bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const;
- bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const;
-
- bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const;
- bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const;
- bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const;
- bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *) const;
- bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const;
- bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const;
- bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const;
- bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const;
- bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *) const;
-};
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_geometry type_handler_geometry;
-#endif
+};
class Type_handler_typelib: public Type_handler_general_purpose_string
{
public:
virtual ~Type_handler_typelib() { }
- enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
- const Type_handler *type_handler_for_item_field() const;
- const Type_handler *cast_to_int_type_handler() const;
- bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
- bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
+ enum_field_types field_type() const override { return MYSQL_TYPE_STRING; }
+ const Type_handler *type_handler_for_item_field() const override;
+ const Type_handler *cast_to_int_type_handler() const override;
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val*) const override;
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name,
Type_handler_hybrid_field_type *,
Type_all_attributes *atrr,
- Item **items, uint nitems) const;
+ Item **items, uint nitems)
+ const override;
void Column_definition_reuse_fix_attributes(THD *thd,
Column_definition *c,
- const Field *field) const;
+ const Field *field)
+ const override;
bool Column_definition_prepare_stage1(THD *thd,
MEM_ROOT *mem_root,
Column_definition *c,
handler *file,
- ulonglong table_flags) const;
+ ulonglong table_flags)
+ const override;
bool Column_definition_redefine_stage1(Column_definition *def,
const Column_definition *dup,
const handler *file,
const Schema_specification_st *schema)
- const;
+ const override;
void Item_param_set_param_func(Item_param *param,
- uchar **pos, ulong len) const;
- bool Vers_history_point_resolve_unit(THD *thd, Vers_history_point *p) const;
+ uchar **pos, ulong len) const override;
+ const Vers_type_handler *vers() const override { return NULL; }
};
class Type_handler_enum: public Type_handler_typelib
{
- static const Name m_name_enum;
public:
virtual ~Type_handler_enum() {}
- const Name name() const { return m_name_enum; }
- enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; }
- enum_field_types traditional_merge_field_type() const
+ enum_field_types real_field_type() const override { return MYSQL_TYPE_ENUM; }
+ enum_field_types traditional_merge_field_type() const override
{
return MYSQL_TYPE_ENUM;
}
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ uint32 calc_pack_length(uint32 length) const override;
+ uint calc_key_length(const Column_definition &def) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target)
+ const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ ulonglong table_flags) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
+ Field *make_schema_field(MEM_ROOT *root,
+ TABLE *table,
+ const Record_addr &addr,
+ const ST_FIELD_INFO &def) const override;
};
class Type_handler_set: public Type_handler_typelib
{
- static const Name m_name_set;
public:
virtual ~Type_handler_set() {}
- const Name name() const { return m_name_set; }
- enum_field_types real_field_type() const { return MYSQL_TYPE_SET; }
- enum_field_types traditional_merge_field_type() const
+ enum_field_types real_field_type() const override { return MYSQL_TYPE_SET; }
+ enum_field_types traditional_merge_field_type() const override
{
return MYSQL_TYPE_SET;
}
- uint32 calc_pack_length(uint32 length) const;
- Field *make_conversion_table_field(TABLE *, uint metadata,
- const Field *target) const;
- bool Column_definition_fix_attributes(Column_definition *c) const;
+ uint32 calc_pack_length(uint32 length) const override;
+ uint calc_key_length(const Column_definition &def) const override;
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target)
+ const override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
bool Column_definition_prepare_stage2(Column_definition *c,
handler *file,
- ulonglong table_flags) const;
- Field *make_table_field(const LEX_CSTRING *name,
+ ulonglong table_flags) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
const Record_addr &addr,
const Type_all_attributes &attr,
- TABLE *table) const;
+ TABLE_SHARE *share) const override;
Field *make_table_field_from_def(TABLE_SHARE *share,
MEM_ROOT *mem_root,
const LEX_CSTRING *name,
const Record_addr &addr,
const Bit_addr &bit,
const Column_definition_attributes *attr,
- uint32 flags) const;
+ uint32 flags) const override;
};
@@ -6475,10 +7247,45 @@ class Type_handler_interval_DDhhmmssff: public Type_handler_long_blob
{
public:
Item *create_typecast_item(THD *thd, Item *item,
- const Type_cast_attributes &attr) const;
+ const Type_cast_attributes &attr) const override;
};
+class Function_collection
+{
+public:
+ virtual ~Function_collection() {}
+ virtual bool init()= 0;
+ virtual void cleanup()= 0;
+ virtual Create_func *find_native_function_builder(THD *thd,
+ const LEX_CSTRING &name)
+ const= 0;
+};
+
+
+class Type_collection
+{
+public:
+ virtual ~Type_collection() {}
+ virtual bool init(Type_handler_data *data)
+ {
+ return false;
+ }
+ virtual const Type_handler *handler_by_name(const LEX_CSTRING &name) const= 0;
+ virtual const Type_handler *aggregate_for_result(const Type_handler *h1,
+ const Type_handler *h2)
+ const= 0;
+ virtual const Type_handler *aggregate_for_comparison(const Type_handler *h1,
+ const Type_handler *h2)
+ const= 0;
+ virtual const Type_handler *aggregate_for_min_max(const Type_handler *h1,
+ const Type_handler *h2)
+ const= 0;
+ virtual const Type_handler *aggregate_for_num_op(const Type_handler *h1,
+ const Type_handler *h2)
+ const= 0;
+};
+
/**
A handler for hybrid type functions, e.g.
@@ -6524,19 +7331,6 @@ public:
{
m_type_handler= other;
}
- const Type_handler *set_handler_by_result_type(Item_result type)
- {
- return (m_type_handler= Type_handler::get_handler_by_result_type(type));
- }
- const Type_handler *set_handler_by_result_type(Item_result type,
- uint max_octet_length,
- CHARSET_INFO *cs)
- {
- m_type_handler= Type_handler::get_handler_by_result_type(type);
- return m_type_handler=
- m_type_handler->type_handler_adjusted_to_max_octet_length(max_octet_length,
- cs);
- }
const Type_handler *set_handler_by_field_type(enum_field_types type)
{
return (m_type_handler= Type_handler::get_handler_by_field_type(type));
@@ -6558,57 +7352,76 @@ public:
const Type_handler *h0, const Type_handler *h1);
};
+/*
+ Helper template to simplify creating builtin types with names.
+ Plugin types inherit from Type_handler_xxx types that do not set the name in
+ the constructor, as sql_plugin.cc sets the type name from the plugin name.
+*/
+template <typename TypeHandler>
+class Named_type_handler : public TypeHandler
+{
+ public:
+ Named_type_handler(const char *n) : TypeHandler()
+ { Type_handler::set_name(Name(n, static_cast<uint>(strlen(n)))); }
+};
-extern MYSQL_PLUGIN_IMPORT Type_handler_row type_handler_row;
-extern MYSQL_PLUGIN_IMPORT Type_handler_null type_handler_null;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_float type_handler_float;
-extern MYSQL_PLUGIN_IMPORT Type_handler_double type_handler_double;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_bit type_handler_bit;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_enum type_handler_enum;
-extern MYSQL_PLUGIN_IMPORT Type_handler_set type_handler_set;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_string type_handler_string;
-extern MYSQL_PLUGIN_IMPORT Type_handler_var_string type_handler_var_string;
-extern MYSQL_PLUGIN_IMPORT Type_handler_varchar type_handler_varchar;
-extern MYSQL_PLUGIN_IMPORT Type_handler_hex_hybrid type_handler_hex_hybrid;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob;
-extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob;
-extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob;
-extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_bool type_handler_bool;
-extern MYSQL_PLUGIN_IMPORT Type_handler_tiny type_handler_tiny;
-extern MYSQL_PLUGIN_IMPORT Type_handler_short type_handler_short;
-extern MYSQL_PLUGIN_IMPORT Type_handler_int24 type_handler_int24;
-extern MYSQL_PLUGIN_IMPORT Type_handler_long type_handler_long;
-extern MYSQL_PLUGIN_IMPORT Type_handler_longlong type_handler_longlong;
-extern MYSQL_PLUGIN_IMPORT Type_handler_longlong type_handler_ulonglong;
-extern MYSQL_PLUGIN_IMPORT Type_handler_vers_trx_id type_handler_vers_trx_id;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_newdecimal type_handler_newdecimal;
-extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year;
-extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year2;
-extern MYSQL_PLUGIN_IMPORT Type_handler_newdate type_handler_newdate;
-extern MYSQL_PLUGIN_IMPORT Type_handler_date type_handler_date;
-extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time;
-extern MYSQL_PLUGIN_IMPORT Type_handler_time2 type_handler_time2;
-extern MYSQL_PLUGIN_IMPORT Type_handler_datetime type_handler_datetime;
-extern MYSQL_PLUGIN_IMPORT Type_handler_datetime2 type_handler_datetime2;
-extern MYSQL_PLUGIN_IMPORT Type_handler_timestamp type_handler_timestamp;
-extern MYSQL_PLUGIN_IMPORT Type_handler_timestamp2 type_handler_timestamp2;
-
-extern MYSQL_PLUGIN_IMPORT Type_handler_interval_DDhhmmssff
- type_handler_interval_DDhhmmssff;
+extern Named_type_handler<Type_handler_row> type_handler_row;
+extern Named_type_handler<Type_handler_null> type_handler_null;
+
+extern Named_type_handler<Type_handler_float> type_handler_float;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_double> type_handler_double;
+
+extern Named_type_handler<Type_handler_bit> type_handler_bit;
+
+extern Named_type_handler<Type_handler_enum> type_handler_enum;
+extern Named_type_handler<Type_handler_set> type_handler_set;
+
+extern Named_type_handler<Type_handler_string> type_handler_string;
+extern Named_type_handler<Type_handler_var_string> type_handler_var_string;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_varchar> type_handler_varchar;
+extern Named_type_handler<Type_handler_varchar_compressed> type_handler_varchar_compressed;
+extern Named_type_handler<Type_handler_hex_hybrid> type_handler_hex_hybrid;
+
+extern Named_type_handler<Type_handler_tiny_blob> type_handler_tiny_blob;
+extern Named_type_handler<Type_handler_medium_blob> type_handler_medium_blob;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_long_blob> type_handler_long_blob;
+extern Named_type_handler<Type_handler_blob> type_handler_blob;
+extern Named_type_handler<Type_handler_blob_compressed> type_handler_blob_compressed;
+
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_bool> type_handler_bool;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_tiny> type_handler_stiny;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_short> type_handler_sshort;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_int24> type_handler_sint24;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_long> type_handler_slong;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_longlong> type_handler_slonglong;
+
+extern Named_type_handler<Type_handler_utiny> type_handler_utiny;
+extern Named_type_handler<Type_handler_ushort> type_handler_ushort;
+extern Named_type_handler<Type_handler_uint24> type_handler_uint24;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_ulong> type_handler_ulong;
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_ulonglong> type_handler_ulonglong;
+extern Named_type_handler<Type_handler_vers_trx_id> type_handler_vers_trx_id;
+
+extern MYSQL_PLUGIN_IMPORT Named_type_handler<Type_handler_newdecimal> type_handler_newdecimal;
+extern Named_type_handler<Type_handler_olddecimal> type_handler_olddecimal;
+
+extern Named_type_handler<Type_handler_year> type_handler_year;
+extern Named_type_handler<Type_handler_year> type_handler_year2;
+extern Named_type_handler<Type_handler_newdate> type_handler_newdate;
+extern Named_type_handler<Type_handler_date> type_handler_date;
+extern Named_type_handler<Type_handler_time> type_handler_time;
+extern Named_type_handler<Type_handler_time2> type_handler_time2;
+extern Named_type_handler<Type_handler_datetime> type_handler_datetime;
+extern Named_type_handler<Type_handler_datetime2> type_handler_datetime2;
+extern Named_type_handler<Type_handler_timestamp> type_handler_timestamp;
+extern Named_type_handler<Type_handler_timestamp2> type_handler_timestamp2;
+
+extern Type_handler_interval_DDhhmmssff type_handler_interval_DDhhmmssff;
class Type_aggregator
{
bool m_is_commutative;
+public:
class Pair
{
public:
@@ -6626,12 +7439,29 @@ class Type_aggregator
return m_handler1 == handler1 && m_handler2 == handler2;
}
};
+ static const Type_handler *
+ find_handler_in_array(const Type_aggregator::Pair *pairs,
+ const Type_handler *h1,
+ const Type_handler *h2,
+ bool commutative)
+ {
+ for (const Type_aggregator::Pair *p= pairs; p->m_result; p++)
+ {
+ if (p->eq(h1, h2))
+ return p->m_result;
+ if (commutative && p->eq(h2, h1))
+ return p->m_result;
+ }
+ return NULL;
+ }
+
+private:
Dynamic_array<Pair> m_array;
const Pair* find_pair(const Type_handler *handler1,
const Type_handler *handler2) const;
public:
Type_aggregator(bool is_commutative= false)
- :m_is_commutative(is_commutative)
+ :m_is_commutative(is_commutative), m_array(PSI_INSTRUMENT_MEM)
{ }
bool add(const Type_handler *handler1,
const Type_handler *handler2,
@@ -6677,7 +7507,6 @@ public:
bool init();
};
-
extern Type_handler_data *type_handler_data;
#endif /* SQL_TYPE_H_INCLUDED */
diff --git a/sql/sql_type_geom.cc b/sql/sql_type_geom.cc
new file mode 100644
index 00000000000..d6fb06f34a5
--- /dev/null
+++ b/sql/sql_type_geom.cc
@@ -0,0 +1,966 @@
+/*
+ Copyright (c) 2015, 2020, 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
+ 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 02111-1301 USA */
+
+#include "mariadb.h"
+
+#ifdef HAVE_SPATIAL
+
+#include "sql_class.h"
+#include "sql_type_geom.h"
+#include "item_geofunc.h"
+
+Named_type_handler<Type_handler_geometry> type_handler_geometry("geometry");
+Named_type_handler<Type_handler_point> type_handler_point("point");
+Named_type_handler<Type_handler_linestring> type_handler_linestring("linestring");
+Named_type_handler<Type_handler_polygon> type_handler_polygon("polygon");
+Named_type_handler<Type_handler_multipoint> type_handler_multipoint("multipoint");
+Named_type_handler<Type_handler_multilinestring> type_handler_multilinestring("multilinestring");
+Named_type_handler<Type_handler_multipolygon> type_handler_multipolygon("multipolygon");
+Named_type_handler<Type_handler_geometrycollection> type_handler_geometrycollection("geometrycollection");
+
+Type_collection_geometry type_collection_geometry;
+
+
+LEX_CSTRING Type_handler_geometry::extended_metadata_data_type_name() const
+{
+ return geometry_type() == GEOM_GEOMETRY ? null_clex_str :
+ name().lex_cstring();
+}
+
+
+const Type_handler_geometry *
+Type_handler_geometry::type_handler_geom_by_type(uint type)
+{
+ switch (type) {
+ case Type_handler_geometry::GEOM_POINT:
+ return &type_handler_point;
+ case Type_handler_geometry::GEOM_LINESTRING:
+ return &type_handler_linestring;
+ case Type_handler_geometry::GEOM_POLYGON:
+ return &type_handler_polygon;
+ case Type_handler_geometry::GEOM_MULTIPOINT:
+ return &type_handler_multipoint;
+ case Type_handler_geometry::GEOM_MULTILINESTRING:
+ return &type_handler_multilinestring;
+ case Type_handler_geometry::GEOM_MULTIPOLYGON:
+ return &type_handler_multipolygon;
+ case Type_handler_geometry::GEOM_GEOMETRYCOLLECTION:
+ return &type_handler_geometrycollection;
+ case Type_handler_geometry::GEOM_GEOMETRY:
+ break;
+ }
+ return &type_handler_geometry;
+}
+
+
+const Type_handler *
+Type_collection_geometry::handler_by_name(const LEX_CSTRING &name) const
+{
+ if (type_handler_point.name().eq(name))
+ return &type_handler_point;
+ if (type_handler_linestring.name().eq(name))
+ return &type_handler_linestring;
+ if (type_handler_polygon.name().eq(name))
+ return &type_handler_polygon;
+ if (type_handler_multipoint.name().eq(name))
+ return &type_handler_multipoint;
+ if (type_handler_multilinestring.name().eq(name))
+ return &type_handler_multilinestring;
+ if (type_handler_multipolygon.name().eq(name))
+ return &type_handler_multipolygon;
+ if (type_handler_geometry.name().eq(name))
+ return &type_handler_geometry;
+ if (type_handler_geometrycollection.name().eq(name))
+ return &type_handler_geometrycollection;
+ return NULL;
+}
+
+
+const Type_collection *Type_handler_geometry::type_collection() const
+{
+ return &type_collection_geometry;
+}
+
+
+const Type_handler *
+Type_handler_geometry::type_handler_frm_unpack(const uchar *buffer) const
+{
+ // charset and geometry_type share the same byte in frm
+ return type_handler_geom_by_type((uint) buffer[14]);
+}
+
+
+const Type_handler *
+Type_collection_geometry::aggregate_for_comparison(const Type_handler *a,
+ const Type_handler *b)
+ const
+{
+ const Type_handler *h;
+ if ((h= aggregate_common(a, b)) ||
+ (h= aggregate_if_null(a, b)) ||
+ (h= aggregate_if_long_blob(a, b)))
+ return h;
+ return NULL;
+}
+
+
+const Type_handler *
+Type_collection_geometry::aggregate_for_result(const Type_handler *a,
+ const Type_handler *b)
+ const
+{
+ const Type_handler *h;
+ if ((h= aggregate_common(a, b)) ||
+ (h= aggregate_if_null(a, b)) ||
+ (h= aggregate_if_long_blob(a, b)) ||
+ (h= aggregate_if_string(a, b)))
+ return h;
+ return NULL;
+}
+
+
+const Type_handler *
+Type_collection_geometry::aggregate_for_min_max(const Type_handler *a,
+ const Type_handler *b)
+ const
+{
+ const Type_handler *h;
+ if ((h= aggregate_common(a, b)) ||
+ (h= aggregate_if_null(a, b)) ||
+ (h= aggregate_if_long_blob(a, b)) ||
+ (h= aggregate_if_string(a, b)))
+ return h;
+ return NULL;
+}
+
+
+const Type_handler *
+Type_collection_geometry::aggregate_if_string(const Type_handler *a,
+ const Type_handler *b) const
+{
+ if (a->type_collection() == this)
+ {
+ DBUG_ASSERT(b->type_collection() != this);
+ swap_variables(const Type_handler *, a, b);
+ }
+ if (a == &type_handler_hex_hybrid ||
+ a == &type_handler_tiny_blob ||
+ a == &type_handler_blob ||
+ a == &type_handler_medium_blob ||
+ a == &type_handler_varchar ||
+ a == &type_handler_string)
+ return &type_handler_long_blob;
+ return NULL;
+}
+
+
+#ifndef DBUG_OFF
+bool Type_collection_geometry::init_aggregators(Type_handler_data *data,
+ const Type_handler *geom) const
+{
+ Type_aggregator *r= &data->m_type_aggregator_for_result;
+ return
+ r->add(geom, &type_handler_hex_hybrid, &type_handler_long_blob) ||
+ r->add(geom, &type_handler_tiny_blob, &type_handler_long_blob) ||
+ r->add(geom, &type_handler_blob, &type_handler_long_blob) ||
+ r->add(geom, &type_handler_medium_blob, &type_handler_long_blob) ||
+ r->add(geom, &type_handler_varchar, &type_handler_long_blob) ||
+ r->add(geom, &type_handler_string, &type_handler_long_blob);
+}
+#endif
+
+
+bool Type_collection_geometry::init(Type_handler_data *data)
+{
+#ifndef DBUG_OFF
+ Type_aggregator *nct= &data->m_type_aggregator_non_commutative_test;
+ if (nct->add(&type_handler_point,
+ &type_handler_varchar,
+ &type_handler_long_blob))
+ return true;
+ return
+ init_aggregators(data, &type_handler_geometry) ||
+ init_aggregators(data, &type_handler_geometrycollection) ||
+ init_aggregators(data, &type_handler_point) ||
+ init_aggregators(data, &type_handler_linestring) ||
+ init_aggregators(data, &type_handler_polygon) ||
+ init_aggregators(data, &type_handler_multipoint) ||
+ init_aggregators(data, &type_handler_multilinestring) ||
+ init_aggregators(data, &type_handler_multipolygon);
+#endif // DBUG_OFF
+ return false;
+}
+
+
+bool Type_handler_geometry::check_type_geom_or_binary(const char *opname,
+ const Item *item)
+{
+ const Type_handler *handler= item->type_handler();
+ if (handler->type_handler_for_comparison() == &type_handler_geometry ||
+ (handler->is_general_purpose_string_type() &&
+ item->collation.collation == &my_charset_bin))
+ return false;
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ handler->name().ptr(), opname);
+ return true;
+}
+
+
+bool Type_handler_geometry::check_types_geom_or_binary(const char *opname,
+ Item* const *args,
+ uint start, uint end)
+{
+ for (uint i= start; i < end ; i++)
+ {
+ if (check_type_geom_or_binary(opname, args[i]))
+ return true;
+ }
+ return false;
+}
+
+
+const Type_handler *Type_handler_geometry::type_handler_for_comparison() const
+{
+ return &type_handler_geometry;
+}
+
+
+Field *Type_handler_geometry::make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table,
+ uint metadata,
+ const Field *target)
+ const
+{
+ DBUG_ASSERT(target->type() == MYSQL_TYPE_GEOMETRY);
+ /*
+ We do not do not update feature_gis statistics here:
+ status_var_increment(target->table->in_use->status_var.feature_gis);
+ as this is only a temporary field.
+ The statistics was already incremented when "target" was created.
+ */
+ const Field_geom *fg= static_cast<const Field_geom*>(target);
+ return new (root)
+ Field_geom(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str,
+ table->s, 4, fg->type_handler_geom(), fg->srid);
+}
+
+
+bool Type_handler_geometry::
+ Column_definition_fix_attributes(Column_definition *def) const
+{
+ def->flags|= BLOB_FLAG;
+ return false;
+}
+
+void Type_handler_geometry::
+ Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *def,
+ const Field *field) const
+{
+ def->srid= ((Field_geom*) field)->srid;
+}
+
+
+bool Type_handler_geometry::
+ Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ def->create_length_to_internal_length_string();
+ return def->prepare_blob_field(thd);
+}
+
+
+bool Type_handler_geometry::
+ Column_definition_prepare_stage2(Column_definition *def,
+ handler *file,
+ ulonglong table_flags) const
+{
+ if (!(table_flags & HA_CAN_GEOMETRY))
+ {
+ my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY");
+ return true;
+ }
+ return def->prepare_stage2_blob(file, table_flags, FIELDFLAG_GEOM);
+}
+
+bool Type_handler_geometry::Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ return part->check_primary_key_for_blob(file);
+}
+
+
+bool Type_handler_geometry::Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *hash_field_needed) const
+{
+ if (!part->length)
+ *hash_field_needed= true;
+ return part->check_key_for_blob(file);
+}
+
+
+bool Type_handler_geometry::Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ return part->init_multiple_key_for_blob(file);
+}
+
+
+bool Type_handler_geometry::Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ return part->check_foreign_key_for_blob(file);
+}
+
+
+bool Type_handler_geometry::Key_part_spec_init_spatial(Key_part_spec *part,
+ const Column_definition &def)
+ const
+{
+ if (part->length)
+ {
+ my_error(ER_WRONG_SUB_KEY, MYF(0));
+ return true;
+ }
+ /*
+ 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case
+ Lately we'll extend this code to support more dimensions
+ */
+ part->length= 4 * sizeof(double);
+ return false;
+}
+
+
+Item *
+Type_handler_geometry::create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr)
+ const
+{
+ DBUG_EXECUTE_IF("emulate_geometry_create_typecast_item",
+ return new (thd->mem_root) Item_func_geometry_from_text(thd, item);
+ );
+
+ return NULL;
+}
+
+bool Type_handler_point::Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ /*
+ QQ:
+ The below assignment (here and in all other Key_part_spec_init_xxx methods)
+ overrides the explicitly given key part length, so in this query:
+ CREATE OR REPLACE TABLE t1 (a POINT, KEY(a(10)));
+ the key becomes KEY(a(25)).
+ This might be a bug.
+ */
+ part->length= octet_length();
+ return part->check_key_for_blob(file);
+}
+
+
+bool Type_handler_point::Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *hash_field_needed) const
+{
+ part->length= octet_length();
+ return part->check_key_for_blob(file);
+}
+
+
+bool Type_handler_point::Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length= octet_length();
+ return part->check_key_for_blob(file);
+}
+
+
+bool Type_handler_point::Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const
+{
+ part->length= octet_length();
+ return part->check_key_for_blob(file);
+}
+
+
+Item *
+Type_handler_point::make_constructor_item(THD *thd, List<Item> *args) const
+{
+ if (!args || args->elements != 2)
+ return NULL;
+ Item_args tmp(thd, *args);
+ return new (thd->mem_root) Item_func_point(thd,
+ tmp.arguments()[0],
+ tmp.arguments()[1]);
+}
+
+
+Item *
+Type_handler_linestring::make_constructor_item(THD *thd, List<Item> *args) const
+{
+ return args ? new (thd->mem_root) Item_func_linestring(thd, *args) : NULL;
+}
+
+
+Item *
+Type_handler_polygon::make_constructor_item(THD *thd, List<Item> *args) const
+{
+ return args ? new (thd->mem_root) Item_func_polygon(thd, *args) : NULL;
+}
+
+
+Item *
+Type_handler_multipoint::make_constructor_item(THD *thd, List<Item> *args) const
+{
+ return args ? new (thd->mem_root) Item_func_multipoint(thd, *args) : NULL;
+}
+
+
+Item *
+Type_handler_multilinestring::make_constructor_item(THD *thd,
+ List<Item> *args) const
+{
+ return args ? new (thd->mem_root) Item_func_multilinestring(thd, *args) :
+ NULL;
+}
+
+
+Item *
+Type_handler_multipolygon::make_constructor_item(THD *thd,
+ List<Item> *args) const
+{
+ return args ? new (thd->mem_root) Item_func_multipolygon(thd, *args) : NULL;
+}
+
+
+Item *
+Type_handler_geometrycollection::make_constructor_item(THD *thd,
+ List<Item> *args) const
+{
+ return args ? new (thd->mem_root) Item_func_geometrycollection(thd, *args) :
+ NULL;
+}
+
+
+uint32 Type_handler_geometry::calc_pack_length(uint32 length) const
+{
+ return 4 + portable_sizeof_char_ptr;
+}
+
+
+Field *Type_handler_geometry::make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE_SHARE *share) const
+{
+ return new (root)
+ Field_geom(addr.ptr(), addr.null_ptr(), addr.null_bit(),
+ Field::NONE, name, share, 4, this, 0);
+}
+
+
+bool Type_handler_geometry::
+ Item_hybrid_func_fix_attributes(THD *thd,
+ const char *func_name,
+ Type_handler_hybrid_field_type *handler,
+ Type_all_attributes *func,
+ Item **items, uint nitems) const
+{
+ DBUG_ASSERT(nitems > 0);
+ func->collation.set(&my_charset_bin);
+ func->unsigned_flag= false;
+ func->decimals= 0;
+ func->max_length= (uint32) UINT_MAX32;
+ func->set_maybe_null(true);
+ return false;
+}
+
+
+bool Type_handler_geometry::
+ Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const
+{
+ return Item_func_or_sum_illegal_param("sum");
+}
+
+
+bool Type_handler_geometry::
+ Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const
+{
+ return Item_func_or_sum_illegal_param("avg");
+}
+
+
+bool Type_handler_geometry::
+ Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_func_round_fix_length_and_dec(Item_func_round *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_func_abs_fix_length_and_dec(Item_func_abs *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_func_neg_fix_length_and_dec(Item_func_neg *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+
+bool Type_handler_geometry::
+ Item_func_signed_fix_length_and_dec(Item_func_signed *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const
+{
+ if (item->cast_charset() != &my_charset_bin)
+ return Item_func_or_sum_illegal_param(item); // CAST(geom AS CHAR)
+ item->fix_length_and_dec_str();
+ return false; // CAST(geom AS BINARY)
+}
+
+
+bool Type_handler_geometry::
+ Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+
+bool Type_handler_geometry::
+ Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const
+{
+ return Item_func_or_sum_illegal_param(item);
+}
+
+
+bool Type_handler_geometry::
+ Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item)
+ const
+{
+ return Item_func_or_sum_illegal_param(item);
+
+}
+
+
+bool Type_handler_geometry::
+ Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *val) const
+{
+ param->unsigned_flag= false;
+ param->setup_conversion_blob(thd);
+ return param->set_str(val->m_string.ptr(), val->m_string.length(),
+ &my_charset_bin, &my_charset_bin);
+}
+
+
+void Type_handler_geometry::Item_param_set_param_func(Item_param *param,
+ uchar **pos,
+ ulong len) const
+{
+ param->set_null(); // Not possible type code in the client-server protocol
+}
+
+
+Field *Type_handler_geometry::
+ make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &rec, const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const
+{
+ status_var_increment(current_thd->status_var.feature_gis);
+ return new (root)
+ Field_geom(rec.ptr(), rec.null_ptr(), rec.null_bit(),
+ attr->unireg_check, name, share,
+ attr->pack_flag_to_pack_length(), this, attr->srid);
+}
+
+
+void Type_handler_geometry::
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *def,
+ uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(def->pack_flag & ~FIELDFLAG_GEOM) == 0);
+ def->frm_pack_basic(buff);
+ buff[11]= 0;
+ buff[14]= (uchar) geometry_type();
+}
+
+
+
+/* Values 1-40 reserved for 1-byte options,
+ 41-80 for 2-byte options,
+ 81-120 for 4-byte options,
+ 121-160 for 8-byte options,
+ other - varied length in next 1-3 bytes.
+*/
+enum extra2_gis_field_options {
+ FIELDGEOM_END=0,
+ FIELDGEOM_STORAGE_MODEL=1,
+ FIELDGEOM_PRECISION=2,
+ FIELDGEOM_SCALE=3,
+ FIELDGEOM_SRID=81,
+};
+
+
+uint
+Type_handler_geometry::
+ Column_definition_gis_options_image(uchar *cbuf,
+ const Column_definition &def) const
+{
+ if (cbuf)
+ {
+ cbuf[0]= FIELDGEOM_STORAGE_MODEL;
+ cbuf[1]= (uchar) Field_geom::GEOM_STORAGE_WKB;
+
+ cbuf[2]= FIELDGEOM_PRECISION;
+ cbuf[3]= (uchar) def.length;
+
+ cbuf[4]= FIELDGEOM_SCALE;
+ cbuf[5]= (uchar) def.decimals;
+
+ cbuf[6]= FIELDGEOM_SRID;
+ int4store(cbuf + 7, ((uint32) def.srid));
+
+ cbuf[11]= FIELDGEOM_END;
+ }
+ return 12;
+}
+
+
+static uint gis_field_options_read(const uchar *buf, size_t buf_len,
+ Field_geom::storage_type *st_type,
+ uint *precision, uint *scale, uint *srid)
+{
+ const uchar *buf_end= buf + buf_len;
+ const uchar *cbuf= buf;
+ int option_id;
+
+ *precision= *scale= *srid= 0;
+ *st_type= Field_geom::GEOM_STORAGE_WKB;
+
+ if (!buf) /* can only happen with the old FRM file */
+ goto end_of_record;
+
+ while (cbuf < buf_end)
+ {
+ switch ((option_id= *(cbuf++)))
+ {
+ case FIELDGEOM_STORAGE_MODEL:
+ *st_type= (Field_geom::storage_type) cbuf[0];
+ break;
+ case FIELDGEOM_PRECISION:
+ *precision= cbuf[0];
+ break;
+ case FIELDGEOM_SCALE:
+ *scale= cbuf[0];
+ break;
+ case FIELDGEOM_SRID:
+ *srid= uint4korr(cbuf);
+ break;
+ case FIELDGEOM_END:
+ goto end_of_record;
+ }
+ if (option_id > 0 && option_id <= 40)
+ cbuf+= 1;
+ else if (option_id > 40 && option_id <= 80)
+ cbuf+= 2;
+ else if (option_id > 80 && option_id <= 120)
+ cbuf+= 4;
+ else if (option_id > 120 && option_id <= 160)
+ cbuf+= 8;
+ else /* > 160 and <=255 */
+ cbuf+= cbuf[0] ? 1 + cbuf[0] : 3 + uint2korr(cbuf+1);
+ }
+
+end_of_record:
+ return (uint)(cbuf - buf);
+}
+
+
+bool Type_handler_geometry::
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options)
+ const
+{
+ uint gis_opt_read, gis_length, gis_decimals;
+ Field_geom::storage_type st_type;
+ attr->frm_unpack_basic(buffer);
+ gis_opt_read= gis_field_options_read(gis_options->str,
+ gis_options->length,
+ &st_type, &gis_length,
+ &gis_decimals, &attr->srid);
+ gis_options->str+= gis_opt_read;
+ gis_options->length-= gis_opt_read;
+ return false;
+}
+
+
+uint32
+Type_handler_geometry::max_display_length_for_field(const Conv_source &src)
+ const
+{
+ return (uint32) my_set_bits(4 * 8);
+}
+
+
+enum_conv_type
+Field_geom::rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const
+{
+ return binlog_type() == source.real_field_type() ?
+ rpl_conv_type_from_same_data_type(source.metadata(), rli, param) :
+ CONV_TYPE_IMPOSSIBLE;
+}
+
+
+/*****************************************************************/
+void Field_geom::sql_type(String &res) const
+{
+ CHARSET_INFO *cs= &my_charset_latin1;
+ const Name tmp= m_type_handler->name();
+ res.set(tmp.ptr(), tmp.length(), cs);
+}
+
+
+int Field_geom::store(double nr)
+{
+ my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
+ ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
+ return -1;
+}
+
+
+int Field_geom::store(longlong nr, bool unsigned_val)
+{
+ my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
+ ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
+ return -1;
+}
+
+
+int Field_geom::store_decimal(const my_decimal *)
+{
+ my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
+ ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
+ return -1;
+}
+
+
+int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs)
+{
+ if (!length)
+ bzero(ptr, Field_blob::pack_length());
+ else
+ {
+ if (from == Geometry::bad_geometry_data.ptr())
+ goto err;
+ // Check given WKB
+ uint32 wkb_type;
+ if (length < SRID_SIZE + WKB_HEADER_SIZE + 4)
+ goto err;
+ wkb_type= uint4korr(from + SRID_SIZE + 1);
+ if (wkb_type < (uint32) Geometry::wkb_point ||
+ wkb_type > (uint32) Geometry::wkb_last)
+ goto err;
+
+ if (m_type_handler->geometry_type() != Type_handler_geometry::GEOM_GEOMETRY &&
+ m_type_handler->geometry_type() != Type_handler_geometry::GEOM_GEOMETRYCOLLECTION &&
+ (uint32) m_type_handler->geometry_type() != wkb_type)
+ {
+ const char *db= table->s->db.str;
+ const char *tab_name= table->s->table_name.str;
+
+ if (!db)
+ db= "";
+ if (!tab_name)
+ tab_name= "";
+
+ Geometry_buffer buffer;
+ Geometry *geom= NULL;
+ String wkt;
+ const char *dummy;
+ wkt.set_charset(&my_charset_latin1);
+ if (!(geom= Geometry::construct(&buffer, from, uint32(length))) ||
+ geom->as_wkt(&wkt, &dummy))
+ goto err;
+
+ my_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, MYF(0),
+ Geometry::ci_collection[m_type_handler->geometry_type()]->m_name.str,
+ wkt.c_ptr_safe(),
+ db, tab_name, field_name.str,
+ (ulong) table->in_use->get_stmt_da()->
+ current_row_for_warning());
+ goto err_exit;
+ }
+
+ Field_blob::store_length(length);
+ if ((table->copy_blobs || length <= MAX_FIELD_WIDTH) &&
+ from != value.ptr())
+ { // Must make a copy
+ value.copy(from, length, cs);
+ from= value.ptr();
+ }
+ bmove(ptr + packlength, &from, sizeof(char*));
+ }
+ return 0;
+
+err:
+ my_message(ER_CANT_CREATE_GEOMETRY_OBJECT,
+ ER_THD(get_thd(), ER_CANT_CREATE_GEOMETRY_OBJECT), MYF(0));
+err_exit:
+ bzero(ptr, Field_blob::pack_length());
+ return -1;
+}
+
+
+bool Field_geom::is_equal(const Column_definition &new_field) const
+{
+ /*
+ - Allow ALTER..INPLACE to supertype (GEOMETRY),
+ e.g. POINT to GEOMETRY or POLYGON to GEOMETRY.
+ - Allow ALTER..INPLACE to the same geometry type: POINT -> POINT
+ */
+ if (new_field.type_handler() == m_type_handler)
+ return true;
+ const Type_handler_geometry *gth=
+ dynamic_cast<const Type_handler_geometry*>(new_field.type_handler());
+ return gth && gth->is_binary_compatible_geom_super_type_for(m_type_handler);
+}
+
+
+bool Field_geom::can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const
+{
+ return item->cmp_type() == STRING_RESULT;
+}
+
+
+bool Field_geom::load_data_set_no_data(THD *thd, bool fixed_format)
+{
+ return Field_geom::load_data_set_null(thd);
+}
+
+
+bool Field_geom::load_data_set_null(THD *thd)
+{
+ Field_blob::reset();
+ if (!maybe_null())
+ {
+ my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field_name.str,
+ thd->get_stmt_da()->current_row_for_warning());
+ return true;
+ }
+ set_null();
+ set_has_explicit_value(); // Do not auto-update this field
+ return false;
+}
+
+
+uint Field_geom::get_key_image(uchar *buff,uint length, const uchar *ptr_arg,
+ imagetype type_arg) const
+{
+ if (type_arg == itMBR)
+ {
+ LEX_CSTRING tmp;
+ tmp.str= (const char *) get_ptr(ptr_arg);
+ tmp.length= get_length(ptr_arg);
+ return Geometry::get_key_image_itMBR(tmp, buff, length);
+ }
+ return Field_blob::get_key_image_itRAW(ptr_arg, buff, length);
+}
+
+Binlog_type_info Field_geom::binlog_type_info() const
+{
+ DBUG_ASSERT(Field_geom::type() == binlog_type());
+ return Binlog_type_info(Field_geom::type(), pack_length_no_ptr(), 1,
+ field_charset(), type_handler_geom()->geometry_type());
+}
+
+#endif // HAVE_SPATIAL
diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h
new file mode 100644
index 00000000000..74290e47afe
--- /dev/null
+++ b/sql/sql_type_geom.h
@@ -0,0 +1,436 @@
+#ifndef SQL_TYPE_GEOM_H_INCLUDED
+#define SQL_TYPE_GEOM_H_INCLUDED
+/*
+ Copyright (c) 2015 MariaDB Foundation
+ Copyright (c) 2019 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
+ 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 02111-1301 USA */
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mariadb.h"
+#include "sql_type.h"
+
+#ifdef HAVE_SPATIAL
+class Type_handler_geometry: public Type_handler_string_result
+{
+public:
+ enum geometry_types
+ {
+ GEOM_GEOMETRY = 0, GEOM_POINT = 1, GEOM_LINESTRING = 2, GEOM_POLYGON = 3,
+ GEOM_MULTIPOINT = 4, GEOM_MULTILINESTRING = 5, GEOM_MULTIPOLYGON = 6,
+ GEOM_GEOMETRYCOLLECTION = 7
+ };
+ static bool check_type_geom_or_binary(const char *opname, const Item *item);
+ static bool check_types_geom_or_binary(const char *opname,
+ Item * const *args,
+ uint start, uint end);
+ static const Type_handler_geometry *type_handler_geom_by_type(uint type);
+ LEX_CSTRING extended_metadata_data_type_name() const;
+public:
+ virtual ~Type_handler_geometry() {}
+ enum_field_types field_type() const override { return MYSQL_TYPE_GEOMETRY; }
+ bool Item_append_extended_type_info(Send_field_extended_metadata *to,
+ const Item *item) const override
+ {
+ LEX_CSTRING tmp= extended_metadata_data_type_name();
+ return tmp.length ? to->set_data_type_name(tmp) : false;
+ }
+ bool is_param_long_data_type() const override { return true; }
+ uint32 max_display_length_for_field(const Conv_source &src) const override;
+ uint32 calc_pack_length(uint32 length) const override;
+ const Type_collection *type_collection() const override;
+ const Type_handler *type_handler_for_comparison() const override;
+ virtual geometry_types geometry_type() const { return GEOM_GEOMETRY; }
+ virtual Item *create_typecast_item(THD *thd, Item *item,
+ const Type_cast_attributes &attr)
+ const override;
+ const Type_handler *type_handler_frm_unpack(const uchar *buffer)
+ const override;
+ bool is_binary_compatible_geom_super_type_for(const Type_handler_geometry *th)
+ const
+ {
+ return geometry_type() == GEOM_GEOMETRY ||
+ geometry_type() == th->geometry_type();
+ }
+ bool type_can_have_key_part() const override { return true; }
+ bool subquery_type_allows_materialization(const Item *inner,
+ const Item *outer) const override
+ {
+ return false; // Materialization does not work with GEOMETRY columns
+ }
+ void Item_param_set_param_func(Item_param *param,
+ uchar **pos, ulong len) const override;
+ bool Item_param_set_from_value(THD *thd,
+ Item_param *param,
+ const Type_all_attributes *attr,
+ const st_value *value) const override;
+ Field *make_conversion_table_field(MEM_ROOT *root,
+ TABLE *table, uint metadata,
+ const Field *target) const override;
+ uint Column_definition_gis_options_image(uchar *buff,
+ const Column_definition &def)
+ const override;
+ bool Column_definition_data_type_info_image(Binary_string *to,
+ const Column_definition &def)
+ const override
+ {
+ return false;
+ }
+ void
+ Column_definition_attributes_frm_pack(const Column_definition_attributes *at,
+ uchar *buff) const override;
+ bool
+ Column_definition_attributes_frm_unpack(Column_definition_attributes *attr,
+ TABLE_SHARE *share,
+ const uchar *buffer,
+ LEX_CUSTRING *gis_options) const
+ override;
+ bool Column_definition_fix_attributes(Column_definition *c) const override;
+ void Column_definition_reuse_fix_attributes(THD *thd,
+ Column_definition *c,
+ const Field *field) const
+ override;
+ bool Column_definition_prepare_stage1(THD *thd,
+ MEM_ROOT *mem_root,
+ Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const override;
+ bool Column_definition_prepare_stage2(Column_definition *c,
+ handler *file,
+ ulonglong table_flags) const override;
+ bool Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *has_key_needed) const override;
+ bool Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_spatial(Key_part_spec *part,
+ const Column_definition &def) const override;
+ Field *make_table_field(MEM_ROOT *root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Type_all_attributes &attr,
+ TABLE_SHARE *share) const override;
+
+ Field *make_table_field_from_def(TABLE_SHARE *share,
+ MEM_ROOT *mem_root,
+ const LEX_CSTRING *name,
+ const Record_addr &addr,
+ const Bit_addr &bit,
+ const Column_definition_attributes *attr,
+ uint32 flags) const override;
+
+ bool can_return_int() const override { return false; }
+ bool can_return_decimal() const override { return false; }
+ bool can_return_real() const override { return false; }
+ bool can_return_text() const override { return false; }
+ bool can_return_date() const override { return false; }
+ bool can_return_time() const override { return false; }
+ bool Item_func_round_fix_length_and_dec(Item_func_round *) const override;
+ bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const override;
+ bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const override;
+ bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const override;
+ bool Item_hybrid_func_fix_attributes(THD *thd,
+ const char *name,
+ Type_handler_hybrid_field_type *h,
+ Type_all_attributes *attr,
+ Item **items, uint nitems) const
+ override;
+ bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const override;
+ bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const override;
+ bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const override;
+
+ bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const override;
+ bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const
+ override;
+ bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const
+ override;
+ bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *) const
+ override;
+ bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const
+ override;
+ bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const
+ override;
+ bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const
+ override;
+ bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const
+ override;
+ bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *) const
+ override;
+};
+
+
+class Type_handler_point: public Type_handler_geometry
+{
+ // Binary length of a POINT value: 4 byte SRID + 21 byte WKB POINT
+ static uint octet_length() { return 25; }
+public:
+ geometry_types geometry_type() const override { return GEOM_POINT; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+ bool Key_part_spec_init_primary(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_unique(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file,
+ bool *has_key_needed) const override;
+ bool Key_part_spec_init_multiple(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+ bool Key_part_spec_init_foreign(Key_part_spec *part,
+ const Column_definition &def,
+ const handler *file) const override;
+};
+
+
+class Type_handler_linestring: public Type_handler_geometry
+{
+public:
+ geometry_types geometry_type() const override { return GEOM_LINESTRING; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+};
+
+
+class Type_handler_polygon: public Type_handler_geometry
+{
+public:
+ geometry_types geometry_type() const override { return GEOM_POLYGON; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+};
+
+
+class Type_handler_multipoint: public Type_handler_geometry
+{
+public:
+ geometry_types geometry_type() const override { return GEOM_MULTIPOINT; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+};
+
+
+class Type_handler_multilinestring: public Type_handler_geometry
+{
+public:
+ geometry_types geometry_type() const override { return GEOM_MULTILINESTRING; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+};
+
+
+class Type_handler_multipolygon: public Type_handler_geometry
+{
+public:
+ geometry_types geometry_type() const override { return GEOM_MULTIPOLYGON; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+};
+
+
+class Type_handler_geometrycollection: public Type_handler_geometry
+{
+public:
+ geometry_types geometry_type() const override { return GEOM_GEOMETRYCOLLECTION; }
+ Item *make_constructor_item(THD *thd, List<Item> *args) const override;
+};
+
+extern Named_type_handler<Type_handler_geometry> type_handler_geometry;
+extern Named_type_handler<Type_handler_point> type_handler_point;
+extern Named_type_handler<Type_handler_linestring> type_handler_linestring;
+extern Named_type_handler<Type_handler_polygon> type_handler_polygon;
+extern Named_type_handler<Type_handler_multipoint> type_handler_multipoint;
+extern Named_type_handler<Type_handler_multilinestring> type_handler_multilinestring;
+extern Named_type_handler<Type_handler_multipolygon> type_handler_multipolygon;
+extern Named_type_handler<Type_handler_geometrycollection> type_handler_geometrycollection;
+
+class Type_collection_geometry: public Type_collection
+{
+ const Type_handler *aggregate_common(const Type_handler *a,
+ const Type_handler *b) const
+ {
+ if (a == b)
+ return a;
+ if (dynamic_cast<const Type_handler_geometry*>(a) &&
+ dynamic_cast<const Type_handler_geometry*>(b))
+ return &type_handler_geometry;
+ return NULL;
+ }
+ const Type_handler *aggregate_if_null(const Type_handler *a,
+ const Type_handler *b) const
+ {
+ return a == &type_handler_null ? b :
+ b == &type_handler_null ? a :
+ NULL;
+ }
+ const Type_handler *aggregate_if_long_blob(const Type_handler *a,
+ const Type_handler *b) const
+ {
+ return a == &type_handler_long_blob ? &type_handler_long_blob :
+ b == &type_handler_long_blob ? &type_handler_long_blob :
+ NULL;
+ }
+ const Type_handler *aggregate_if_string(const Type_handler *a,
+ const Type_handler *b) const;
+#ifndef DBUG_OFF
+ bool init_aggregators(Type_handler_data *data, const Type_handler *geom) const;
+#endif
+public:
+ bool init(Type_handler_data *data) override;
+ const Type_handler *handler_by_name(const LEX_CSTRING &name) const override;
+ const Type_handler *aggregate_for_result(const Type_handler *a,
+ const Type_handler *b)
+ const override;
+ const Type_handler *aggregate_for_comparison(const Type_handler *a,
+ const Type_handler *b)
+ const override;
+ const Type_handler *aggregate_for_min_max(const Type_handler *a,
+ const Type_handler *b)
+ const override;
+ const Type_handler *aggregate_for_num_op(const Type_handler *a,
+ const Type_handler *b)
+ const override
+ {
+ return NULL;
+ }
+};
+
+extern Type_collection_geometry type_collection_geometry;
+
+#include "field.h"
+
+class Field_geom :public Field_blob
+{
+ const Type_handler_geometry *m_type_handler;
+public:
+ uint srid;
+ uint precision;
+ enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1};
+ enum storage_type storage;
+
+ Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg,
+ TABLE_SHARE *share, uint blob_pack_length,
+ const Type_handler_geometry *gth,
+ uint field_srid)
+ :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+ field_name_arg, share, blob_pack_length, &my_charset_bin),
+ m_type_handler(gth)
+ { srid= field_srid; }
+ enum_conv_type rpl_conv_type_from(const Conv_source &source,
+ const Relay_log_info *rli,
+ const Conv_param &param) const override;
+ enum ha_base_keytype key_type() const override
+ {
+ return HA_KEYTYPE_VARBINARY2;
+ }
+ const Type_handler *type_handler() const override
+ {
+ return m_type_handler;
+ }
+ const Type_handler_geometry *type_handler_geom() const
+ {
+ return m_type_handler;
+ }
+ void set_type_handler(const Type_handler_geometry *th)
+ {
+ m_type_handler= th;
+ }
+ enum_field_types type() const override
+ {
+ return MYSQL_TYPE_GEOMETRY;
+ }
+ enum_field_types real_type() const override
+ {
+ return MYSQL_TYPE_GEOMETRY;
+ }
+ Information_schema_character_attributes
+ information_schema_character_attributes() const override
+ {
+ return Information_schema_character_attributes();
+ }
+ void make_send_field(Send_field *to) override
+ {
+ Field_longstr::make_send_field(to);
+ LEX_CSTRING tmp= m_type_handler->extended_metadata_data_type_name();
+ if (tmp.length)
+ to->set_data_type_name(tmp);
+ }
+ bool can_optimize_range(const Item_bool_func *cond,
+ const Item *item,
+ bool is_eq_func) const override;
+ void sql_type(String &str) const override;
+ Copy_func *get_copy_func(const Field *from) const override
+ {
+ const Type_handler_geometry *fth=
+ dynamic_cast<const Type_handler_geometry*>(from->type_handler());
+ if (fth && m_type_handler->is_binary_compatible_geom_super_type_for(fth))
+ return get_identical_copy_func();
+ return do_conv_blob;
+ }
+ bool memcpy_field_possible(const Field *from) const override
+ {
+ const Type_handler_geometry *fth=
+ dynamic_cast<const Type_handler_geometry*>(from->type_handler());
+ return fth &&
+ m_type_handler->is_binary_compatible_geom_super_type_for(fth) &&
+ !table->copy_blobs;
+ }
+ bool is_equal(const Column_definition &new_field) const override;
+ bool can_be_converted_by_engine(const Column_definition &new_type)
+ const override
+ {
+ return false; // Override the Field_blob behavior
+ }
+
+ int store(const char *to, size_t length, CHARSET_INFO *charset) override;
+ int store(double nr) override;
+ int store(longlong nr, bool unsigned_val) override;
+ int store_decimal(const my_decimal *) override;
+ uint size_of() const override{ return sizeof(*this); }
+ /**
+ Key length is provided only to support hash joins. (compared byte for byte)
+ Ex: SELECT .. FROM t1,t2 WHERE t1.field_geom1=t2.field_geom2.
+
+ The comparison is not very relevant, as identical geometry might be
+ represented differently, but we need to support it either way.
+ */
+ uint32 key_length() const override{ return packlength; }
+ uint get_key_image(uchar *buff,uint length,
+ const uchar *ptr_arg, imagetype type_arg) const override;
+
+ /**
+ Non-nullable GEOMETRY types cannot have defaults,
+ but the underlying blob must still be reset.
+ */
+ int reset(void) override{ return Field_blob::reset() || !maybe_null(); }
+ bool load_data_set_null(THD *thd) override;
+ bool load_data_set_no_data(THD *thd, bool fixed_format) override;
+
+ uint get_srid() const { return srid; }
+ void print_key_value(String *out, uint32 length) override
+ {
+ out->append(STRING_WITH_LEN("unprintable_geometry_value"));
+ }
+ Binlog_type_info binlog_type_info() const override;
+};
+
+#endif // HAVE_SPATIAL
+
+#endif // SQL_TYPE_GEOM_H_INCLUDED
diff --git a/sql/sql_type_int.h b/sql/sql_type_int.h
index d3e9d0318cf..9fd4f0c46a0 100644
--- a/sql/sql_type_int.h
+++ b/sql/sql_type_int.h
@@ -16,6 +16,8 @@
#ifndef SQL_TYPE_INT_INCLUDED
#define SQL_TYPE_INT_INCLUDED
+#include "my_bit.h" // my_count_bits()
+
class Null_flag
{
@@ -43,6 +45,58 @@ public:
Longlong_null(longlong nr, bool is_null)
:Longlong(nr), Null_flag(is_null)
{ }
+ explicit Longlong_null()
+ :Longlong(0), Null_flag(true)
+ { }
+ explicit Longlong_null(longlong nr)
+ :Longlong(nr), Null_flag(false)
+ { }
+ Longlong_null operator|(const Longlong_null &other) const
+ {
+ if (is_null() || other.is_null())
+ return Longlong_null();
+ return Longlong_null(value() | other.value());
+ }
+ Longlong_null operator&(const Longlong_null &other) const
+ {
+ if (is_null() || other.is_null())
+ return Longlong_null();
+ return Longlong_null(value() & other.value());
+ }
+ Longlong_null operator^(const Longlong_null &other) const
+ {
+ if (is_null() || other.is_null())
+ return Longlong_null();
+ return Longlong_null((longlong) (value() ^ other.value()));
+ }
+ Longlong_null operator~() const
+ {
+ if (is_null())
+ return *this;
+ return Longlong_null((longlong) ~ (ulonglong) value());
+ }
+ Longlong_null operator<<(const Longlong_null &llshift) const
+ {
+ if (is_null() || llshift.is_null())
+ return Longlong_null();
+ uint shift= (uint) llshift.value();
+ ulonglong res= ((ulonglong) value()) << shift;
+ return Longlong_null(shift < sizeof(longlong) * 8 ? (longlong) res : 0);
+ }
+ Longlong_null operator>>(const Longlong_null &llshift) const
+ {
+ if (is_null() || llshift.is_null())
+ return Longlong_null();
+ uint shift= (uint) llshift.value();
+ ulonglong res= ((ulonglong) value()) >> shift;
+ return Longlong_null(shift < sizeof(longlong) * 8 ? (longlong) res : 0);
+ }
+ Longlong_null bit_count() const
+ {
+ if (is_null())
+ return *this;
+ return Longlong_null((longlong) my_count_bits((ulonglong) value()));
+ }
};
diff --git a/sql/sql_type_json.cc b/sql/sql_type_json.cc
index f53a247d816..a804366ec03 100644
--- a/sql/sql_type_json.cc
+++ b/sql/sql_type_json.cc
@@ -35,8 +35,10 @@ Type_handler_json_longtext::make_json_valid_expr(THD *thd,
Lex_ident_sys_st str;
Item *field, *expr;
str.set_valid_utf8(field_name);
- if (unlikely(!(field= thd->lex->create_item_ident_field(thd, NullS, NullS,
- &str))))
+ if (unlikely(!(field= thd->lex->create_item_ident_field(thd,
+ Lex_ident_sys(),
+ Lex_ident_sys(),
+ str))))
return 0;
if (unlikely(!(expr= new (thd->mem_root) Item_func_json_valid(thd, field))))
return 0;
diff --git a/sql/sql_type_string.cc b/sql/sql_type_string.cc
new file mode 100644
index 00000000000..df46ef744f0
--- /dev/null
+++ b/sql/sql_type_string.cc
@@ -0,0 +1,104 @@
+/*
+ Copyright (c) 2019, 2020 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
+ 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 02111-1301 USA */
+
+#include "mariadb.h"
+
+#include "sql_class.h"
+#include "sql_type_string.h"
+
+
+uchar *
+StringPack::pack(uchar *to, const uchar *from, uint max_length) const
+{
+ size_t length= MY_MIN(m_octet_length, max_length);
+ size_t local_char_length= char_length();
+ DBUG_PRINT("debug", ("length: %zu ", length));
+
+ if (length > local_char_length)
+ local_char_length= charset()->charpos(from, from + length,
+ local_char_length);
+ set_if_smaller(length, local_char_length);
+
+ /*
+ TODO: change charset interface to add a new function that does
+ the following or add a flag to lengthsp to do it itself
+ (this is for not packing padding adding bytes in BINARY
+ fields).
+ */
+ if (mbmaxlen() == 1)
+ {
+ while (length && from[length-1] == charset()->pad_char)
+ length --;
+ }
+ else
+ length= charset()->lengthsp((const char*) from, length);
+
+ // Length always stored little-endian
+ *to++= (uchar) length;
+ if (m_octet_length > 255)
+ *to++= (uchar) (length >> 8);
+
+ // Store the actual bytes of the string
+ memcpy(to, from, length);
+ return to+length;
+}
+
+
+const uchar *
+StringPack::unpack(uchar *to, const uchar *from, const uchar *from_end,
+ uint param_data) const
+{
+ uint from_length, length;
+
+ /*
+ Compute the declared length of the field on the master. This is
+ used to decide if one or two bytes should be read as length.
+ */
+ if (param_data)
+ from_length= (((param_data >> 4) & 0x300) ^ 0x300) + (param_data & 0x00ff);
+ else
+ from_length= m_octet_length;
+
+ DBUG_PRINT("debug",
+ ("param_data: 0x%x, field_length: %u, from_length: %u",
+ param_data, m_octet_length, from_length));
+ /*
+ Compute the actual length of the data by reading one or two bits
+ (depending on the declared field length on the master).
+ */
+ if (from_length > 255)
+ {
+ if (from + 2 > from_end)
+ return 0;
+ length= uint2korr(from);
+ from+= 2;
+ }
+ else
+ {
+ if (from + 1 > from_end)
+ return 0;
+ length= (uint) *from++;
+ }
+ if (from + length > from_end || length > m_octet_length)
+ return 0;
+
+ memcpy(to, from, length);
+ // Pad the string with the pad character of the fields charset
+ charset()->fill((char*) to + length,
+ m_octet_length - length,
+ charset()->pad_char);
+ return from+length;
+}
diff --git a/sql/sql_type_string.h b/sql/sql_type_string.h
new file mode 100644
index 00000000000..fca46e91394
--- /dev/null
+++ b/sql/sql_type_string.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2019 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
+ 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_TYPE_STRING_INCLUDED
+#define SQL_TYPE_STRING_INCLUDED
+
+class StringPack
+{
+ CHARSET_INFO *m_cs;
+ uint32 m_octet_length;
+ CHARSET_INFO *charset() const { return m_cs; }
+ uint mbmaxlen() const { return m_cs->mbmaxlen; };
+ uint32 char_length() const { return m_octet_length / mbmaxlen(); }
+public:
+ StringPack(CHARSET_INFO *cs, uint32 octet_length)
+ :m_cs(cs),
+ m_octet_length(octet_length)
+ { }
+ uchar *pack(uchar *to, const uchar *from, uint max_length) const;
+ const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end,
+ uint param_data) const;
+public:
+ static uint max_packed_col_length(uint max_length)
+ {
+ return (max_length > 255 ? 2 : 1) + max_length;
+ }
+ static uint packed_col_length(const uchar *data_ptr, uint length)
+ {
+ if (length > 255)
+ return uint2korr(data_ptr)+2;
+ return (uint) *data_ptr + 1;
+ }
+};
+
+
+#endif // SQL_TYPE_STRING_INCLUDED
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 3f7289fdca2..07dd3b1f6ca 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -112,6 +112,8 @@ extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
return (uchar*) udf->name.str;
}
+static PSI_memory_key key_memory_udf_mem;
+
#ifdef HAVE_PSI_INTERFACE
static PSI_rwlock_key key_rwlock_THR_LOCK_udf;
@@ -120,6 +122,11 @@ static PSI_rwlock_info all_udf_rwlocks[]=
{ &key_rwlock_THR_LOCK_udf, "THR_LOCK_udf", PSI_FLAG_GLOBAL}
};
+static PSI_memory_info all_udf_memory[]=
+{
+ { &key_memory_udf_mem, "udf_mem", PSI_FLAG_GLOBAL}
+};
+
static void init_udf_psi_keys(void)
{
const char* category= "sql";
@@ -130,6 +137,9 @@ static void init_udf_psi_keys(void)
count= array_elements(all_udf_rwlocks);
PSI_server->register_rwlock(category, all_udf_rwlocks, count);
+
+ count= array_elements(all_udf_memory);
+ mysql_memory_register(category, all_udf_memory, count);
}
#endif
@@ -156,10 +166,11 @@ void udf_init()
mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
- init_sql_alloc(&mem, "udf", UDF_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(key_memory_udf_mem, &mem, UDF_ALLOC_BLOCK_SIZE, 0, MYF(0));
THD *new_thd = new THD(0);
if (!new_thd ||
- my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
+ my_hash_init(key_memory_udf_mem,
+ &udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
{
sql_print_error("Can't allocate memory for udf structures");
my_hash_free(&udf_hash);
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index a487f4d4d73..99a199886ce 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -72,7 +72,7 @@ void select_unit::change_select()
switch (step)
{
case INTERSECT_TYPE:
- intersect_mark->value= prev_step= curr_step;
+ prev_step= curr_step;
curr_step= current_select_number;
break;
case EXCEPT_TYPE:
@@ -83,6 +83,7 @@ void select_unit::change_select()
}
DBUG_VOID_RETURN;
}
+
/**
Fill temporary tables for UNION/EXCEPT/INTERSECT
@@ -93,7 +94,7 @@ UNION:
EXCEPT:
looks for the record in the table (with 'counter' field first if
INTERSECT present in the sequence) and delete it if found
-INTESECT:
+INTERSECT:
looks for the same record with 'counter' field of previous operation,
put as a 'counter' number of the current SELECT.
We scan the table and remove all records which marked with not last
@@ -108,28 +109,29 @@ INTESECT:
*/
int select_unit::send_data(List<Item> &values)
{
- int rc;
+ int rc= 0;
int not_reported_error= 0;
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- return 0;
- }
- if (thd->killed == ABORT_QUERY)
- return 0;
+
if (table->no_rows_with_nulls)
table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
- if (intersect_mark)
+
+ fill_record(thd, table, table->field + addon_cnt, values, true, false);
+ /* set up initial values for records to be written */
+ if (addon_cnt && step == UNION_TYPE)
{
- fill_record(thd, table, table->field + 1, values, TRUE, FALSE);
- table->field[0]->store((ulonglong) curr_step, 1);
+ DBUG_ASSERT(addon_cnt == 1);
+ table->field[0]->store((longlong) curr_step, 1);
}
- else
- fill_record(thd, table, table->field, values, TRUE, FALSE);
+
if (unlikely(thd->is_error()))
{
rc= 1;
- goto end;
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
}
if (table->no_rows_with_nulls)
{
@@ -137,105 +139,58 @@ int select_unit::send_data(List<Item> &values)
if (table->null_catch_flags)
{
rc= 0;
- goto end;
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
}
}
- // select_unit::change_select() change step & Co correctly for each SELECT
+ /* select_unit::change_select() change step & Co correctly for each SELECT */
+ int find_res;
switch (step)
{
- case UNION_TYPE:
- {
- if (unlikely((write_err=
- table->file->ha_write_tmp_row(table->record[0]))))
- {
- if (write_err == HA_ERR_FOUND_DUPP_KEY)
- {
- /*
- Inform upper level that we found a duplicate key, that should not
- be counted as part of limit
- */
- rc= -1;
- goto end;
- }
- bool is_duplicate= FALSE;
- /* create_internal_tmp_table_from_heap will generate error if needed */
- if (table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
- create_internal_tmp_table_from_heap(thd, table,
- tmp_table_param.start_recinfo,
- &tmp_table_param.recinfo,
- write_err, 1, &is_duplicate))
- {
- rc= 1;
- goto end;
- }
+ case UNION_TYPE:
+ rc= write_record();
+ /* no reaction with conversion */
+ if (rc == -2)
+ rc= 0;
+ break;
- if (is_duplicate)
- {
- rc= -1;
- goto end;
- }
- }
- break;
- }
- case EXCEPT_TYPE:
- {
- int find_res;
- /*
- The temporary table uses very first index or constrain for
- checking unique constrain.
- */
- if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
- {
- DBUG_ASSERT(!table->triggers);
- table->status|= STATUS_DELETED;
- not_reported_error= table->file->ha_delete_tmp_row(table->record[0]);
- rc= MY_TEST(not_reported_error);
- goto end;
- }
- else
- {
- if ((rc= not_reported_error= (find_res != 1)))
- goto end;
- }
- break;
- }
- case INTERSECT_TYPE:
+ case EXCEPT_TYPE:
+ /*
+ The temporary table uses very first index or constrain for
+ checking unique constrain.
+ */
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ rc= delete_record();
+ else
+ rc= not_reported_error= (find_res != 1);
+ break;
+ case INTERSECT_TYPE:
+ /*
+ The temporary table uses very first index or constrain for
+ checking unique constrain.
+ */
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
{
- int find_res;
- /*
- The temporary table uses very first index or constrain for
- checking unique constrain.
- */
- if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ DBUG_ASSERT(!table->triggers);
+ if (table->field[0]->val_int() == prev_step)
{
- DBUG_ASSERT(!table->triggers);
- if (table->field[0]->val_int() != prev_step)
- {
- rc= 0;
- goto end;
- }
- store_record(table, record[1]);
- table->field[0]->store(curr_step, 0);
- not_reported_error= table->file->ha_update_tmp_row(table->record[1],
- table->record[0]);
+ not_reported_error= update_counter(table->field[0], curr_step);
rc= MY_TEST(not_reported_error);
DBUG_ASSERT(rc != HA_ERR_RECORD_IS_THE_SAME);
- goto end;
}
- else
- {
- if ((rc= not_reported_error= (find_res != 1)))
- goto end;
- }
- break;
}
- default:
- DBUG_ASSERT(0);
+ else
+ rc= not_reported_error= (find_res != 1);
+ break;
+ default:
+ DBUG_ASSERT(0);
}
- rc= 0;
-end:
if (unlikely(not_reported_error))
{
DBUG_ASSERT(rc);
@@ -251,7 +206,7 @@ bool select_unit::send_eof()
thd->lex->current_select->next_select()->get_linkage() == INTERSECT_TYPE))
{
/*
- it is not INTESECT or next SELECT in the sequence is INTERSECT so no
+ it is not INTERSECT or next SELECT in the sequence is INTERSECT so no
need filtering (the last INTERSECT in this sequence of intersects will
filter).
*/
@@ -265,15 +220,14 @@ bool select_unit::send_eof()
TODO: as optimization for simple case this could be moved to
'fake_select' WHERE condition
*/
- handler *file= table->file;
int error;
- if (unlikely(file->ha_rnd_init_with_error(1)))
+ if (table->file->ha_rnd_init_with_error(1))
return 1;
-
do
{
- if (unlikely(error= file->ha_rnd_next(table->record[0])))
+ error= table->file->ha_rnd_next(table->record[0]);
+ if (unlikely(error))
{
if (error == HA_ERR_END_OF_FILE)
{
@@ -283,9 +237,9 @@ bool select_unit::send_eof()
break;
}
if (table->field[0]->val_int() != curr_step)
- error= file->ha_delete_tmp_row(table->record[0]);
- } while (likely(!error));
- file->ha_rnd_end();
+ error= delete_record();
+ } while (!error);
+ table->file->ha_rnd_end();
if (unlikely(error))
table->file->print_error(error, MYF(0));
@@ -303,6 +257,8 @@ int select_union_recursive::send_data(List<Item> &values)
write_err != HA_ERR_FOUND_DUPP_UNIQUE)
{
int err;
+ DBUG_ASSERT(incr_table->s->reclength == table->s->reclength ||
+ incr_table->s->reclength == table->s->reclength - MARIA_UNIQUE_HASH_LENGTH);
if ((err= incr_table->file->ha_write_tmp_row(table->record[0])))
{
bool is_duplicate;
@@ -345,6 +301,7 @@ bool select_unit::flush()
create_table whether to physically create result table
keep_row_order keep rows in order as they were inserted
hidden number of hidden fields (for INTERSECT)
+ plus one for `ALL`
DESCRIPTION
Create a temporary table that is used to store the result of a UNION,
@@ -433,6 +390,143 @@ select_union_recursive::create_result_table(THD *thd_arg,
}
+/*
+ @brief
+ Write a record
+
+ @retval
+ -2 conversion happened
+ -1 found a duplicate key
+ 0 no error
+ 1 if an error is reported
+*/
+
+int select_unit::write_record()
+{
+ if (unlikely((write_err= table->file->ha_write_tmp_row(table->record[0]))))
+ {
+ if (write_err == HA_ERR_FOUND_DUPP_KEY)
+ {
+ /*
+ Inform upper level that we found a duplicate key, that should not
+ be counted as part of limit
+ */
+ return -1;
+ }
+ bool is_duplicate= false;
+ /* create_internal_tmp_table_from_heap will generate error if needed */
+ if (table->file->is_fatal_error(write_err, HA_CHECK_DUP))
+ {
+ if (!create_internal_tmp_table_from_heap(thd, table,
+ tmp_table_param.start_recinfo,
+ &tmp_table_param.recinfo,
+ write_err, 1, &is_duplicate))
+ {
+ return -2;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ if (is_duplicate)
+ {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ @brief
+ Update counter for a record
+
+ @retval
+ 0 no error
+ -1 error occurred
+*/
+
+int select_unit::update_counter(Field* counter, longlong value)
+{
+ store_record(table, record[1]);
+ counter->store(value, 0);
+ int error= table->file->ha_update_tmp_row(table->record[1],
+ table->record[0]);
+ return error;
+}
+
+
+/*
+ @brief
+ Try to disable index
+
+ @retval
+ true index is disabled this time
+ false this time did not disable the index
+*/
+
+bool select_unit_ext::disable_index_if_needed(SELECT_LEX *curr_sl)
+{
+ if (is_index_enabled &&
+ (curr_sl == curr_sl->master_unit()->union_distinct ||
+ !curr_sl->next_select()) )
+ {
+ is_index_enabled= false;
+ if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
+ return false;
+ table->no_keyread=1;
+ return true;
+ }
+ return false;
+}
+
+/*
+ @brief
+ Unfold a record
+
+ @retval
+ 0 no error
+ -1 conversion happened
+*/
+
+int select_unit_ext::unfold_record(ha_rows cnt)
+{
+
+ DBUG_ASSERT(cnt > 0);
+ int error= 0;
+ bool is_convertion_happened= false;
+ while (--cnt)
+ {
+ error= write_record();
+ if (error == -2)
+ {
+ is_convertion_happened= true;
+ error= -1;
+ }
+ }
+ if (is_convertion_happened)
+ return -1;
+ return error;
+}
+
+/*
+ @brief
+ Delete a record
+
+ @retval
+ 0 no error
+ 1 if an error is reported
+*/
+
+int select_unit::delete_record()
+{
+ DBUG_ASSERT(!table->triggers);
+ table->status|= STATUS_DELETED;
+ int not_reported_error= table->file->ha_delete_tmp_row(table->record[0]);
+ return MY_TEST(not_reported_error);
+}
+
/**
Reset and empty the temporary table that stores the materialized query
result.
@@ -448,6 +542,350 @@ void select_unit::cleanup()
}
+/*
+ @brief
+ Set up value needed by send_data() and send_eof()
+
+ @detail
+ - For EXCEPT we will decrease the counter by one
+ and INTERSECT / UNION we increase the counter.
+
+ - For INTERSECT we will modify the second extra field (intersect counter)
+ and for EXCEPT / UNION we modify the first (duplicate counter)
+*/
+
+void select_unit_ext::change_select()
+{
+ select_unit::change_select();
+ switch(step){
+ case UNION_TYPE:
+ increment= 1;
+ curr_op_type= UNION_DISTINCT;
+ break;
+ case EXCEPT_TYPE:
+ increment= -1;
+ curr_op_type= EXCEPT_DISTINCT;
+ break;
+ case INTERSECT_TYPE:
+ increment= 1;
+ curr_op_type= INTERSECT_DISTINCT;
+ break;
+ default: DBUG_ASSERT(0);
+ }
+ if (!thd->lex->current_select->distinct)
+ /* change type from DISTINCT to ALL */
+ curr_op_type= (set_op_type)(curr_op_type + 1);
+
+ duplicate_cnt= table->field[addon_cnt - 1];
+ if (addon_cnt == 2)
+ additional_cnt= table->field[addon_cnt - 2];
+ else
+ additional_cnt= NULL;
+}
+
+
+/*
+ @brief
+ Fill temporary tables for operations need extra fields
+
+ @detail
+ - If this operation is not distinct, we try to find it and increase the
+ counter by "increment" setted in select_unit_ext::change_select().
+
+ - If it is distinct, for UNION we write this record; for INTERSECT we
+ try to find it and increase the intersect counter if found; for EXCEPT
+ we try to find it and delete that record if found.
+
+*/
+
+int select_unit_ext::send_data(List<Item> &values)
+{
+ int rc= 0;
+ int not_reported_error= 0;
+ int find_res;
+
+ if (table->no_rows_with_nulls)
+ table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
+
+ fill_record(thd, table, table->field + addon_cnt, values, true, false);
+ /* set up initial values for records to be written */
+ if ( step == UNION_TYPE )
+ {
+ /* set duplicate counter to 1 */
+ duplicate_cnt->store((longlong) 1, 1);
+ /* set the other counter to 0 */
+ if (curr_op_type == INTERSECT_ALL)
+ additional_cnt->store((longlong) 0, 1);
+ }
+
+ if (unlikely(thd->is_error()))
+ {
+ rc= 1;
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
+ }
+ if (table->no_rows_with_nulls)
+ {
+ table->null_catch_flags&= ~CHECK_ROW_FOR_NULLS_TO_REJECT;
+ if (table->null_catch_flags)
+ {
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
+ }
+ }
+
+ switch(curr_op_type)
+ {
+ case UNION_ALL:
+ if (!is_index_enabled ||
+ (find_res= table->file->find_unique_row(table->record[0], 0)))
+ {
+ rc= write_record();
+ /* no reaction with conversion */
+ if (rc == -2)
+ rc= 0;
+ }
+ else
+ {
+ longlong cnt= duplicate_cnt->val_int() + increment;
+ not_reported_error= update_counter(duplicate_cnt, cnt);
+ DBUG_ASSERT(!table->triggers);
+ rc= MY_TEST(not_reported_error);
+ }
+ break;
+
+ case EXCEPT_ALL:
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ {
+ longlong cnt= duplicate_cnt->val_int() + increment;
+ if (cnt == 0)
+ rc= delete_record();
+ else
+ {
+ not_reported_error= update_counter(duplicate_cnt, cnt);
+ DBUG_ASSERT(!table->triggers);
+ rc= MY_TEST(not_reported_error);
+ }
+ }
+ break;
+
+ case INTERSECT_ALL:
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ {
+ longlong cnt= duplicate_cnt->val_int() + increment;
+ if (cnt <= additional_cnt->val_int())
+ {
+ not_reported_error= update_counter(duplicate_cnt, cnt);
+ DBUG_ASSERT(!table->triggers);
+ rc= MY_TEST(not_reported_error);
+ }
+ }
+ break;
+
+ case UNION_DISTINCT:
+ rc= write_record();
+ /* no reaction with conversion */
+ if (rc == -2)
+ rc= 0;
+ break;
+
+ case EXCEPT_DISTINCT:
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ rc= delete_record();
+ else
+ rc= not_reported_error= (find_res != 1);
+ break;
+
+ case INTERSECT_DISTINCT:
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ {
+ if (additional_cnt->val_int() == prev_step)
+ {
+ not_reported_error= update_counter(additional_cnt, curr_step);
+ rc= MY_TEST(not_reported_error);
+ DBUG_ASSERT(rc != HA_ERR_RECORD_IS_THE_SAME);
+ }
+ else if (additional_cnt->val_int() != curr_step)
+ rc= delete_record();
+ }
+ else
+ rc= not_reported_error= (find_res != 1);
+ break;
+
+ default:
+ DBUG_ASSERT(0);
+ }
+
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
+}
+
+
+/*
+ @brief
+ Do post-operation after a operator
+
+ @detail
+ We need to scan in these cases:
+ - If this operation is DISTINCT and next is ALL,
+ duplicate counter needs to be set to 1.
+ - If this operation is INTERSECT ALL and counter needs to be updated.
+ - If next operation is INTERSECT ALL,
+ set up the second extra field (called "intersect_counter") to 0.
+ this extra field counts records in the second operand.
+
+ If this operation is equal to "union_distinct" or is the last operation,
+ we'll disable index. Then if this operation is ALL we'll unfold records.
+*/
+
+bool select_unit_ext::send_eof()
+{
+ int error= 0;
+ SELECT_LEX *curr_sl= thd->lex->current_select;
+ SELECT_LEX *next_sl= curr_sl->next_select();
+ bool is_next_distinct= next_sl && next_sl->distinct;
+ bool is_next_intersect_all=
+ next_sl &&
+ next_sl->get_linkage() == INTERSECT_TYPE &&
+ !next_sl->distinct;
+ bool need_unfold= (disable_index_if_needed(curr_sl) &&
+ !curr_sl->distinct);
+
+ if (((curr_sl->distinct && !is_next_distinct) ||
+ curr_op_type == INTERSECT_ALL ||
+ is_next_intersect_all) &&
+ !need_unfold)
+ {
+ if (!next_sl)
+ DBUG_ASSERT(curr_op_type != INTERSECT_ALL);
+ bool need_update_row;
+ if (unlikely(table->file->ha_rnd_init_with_error(1)))
+ return 1;
+ do
+ {
+ need_update_row= false;
+ if (unlikely(error= table->file->ha_rnd_next(table->record[0])))
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ error= 0;
+ break;
+ }
+ break;
+ }
+ store_record(table, record[1]);
+
+ if (curr_sl->distinct && !is_next_distinct)
+ {
+ /* set duplicate counter to 1 if next operation is ALL */
+ duplicate_cnt->store(1, 0);
+ need_update_row= true;
+ }
+
+ if (is_next_intersect_all)
+ {
+ longlong d_cnt_val= duplicate_cnt->val_int();
+ if (d_cnt_val == 0)
+ error= delete_record();
+ else
+ {
+ if (curr_op_type == INTERSECT_ALL)
+ {
+ longlong a_cnt_val= additional_cnt->val_int();
+ if (a_cnt_val < d_cnt_val)
+ d_cnt_val= a_cnt_val;
+ }
+ additional_cnt->store(d_cnt_val, 0);
+ duplicate_cnt->store((longlong)0, 0);
+ need_update_row= true;
+ }
+ }
+
+ if (need_update_row)
+ error= table->file->ha_update_tmp_row(table->record[1],
+ table->record[0]);
+ } while (likely(!error));
+ table->file->ha_rnd_end();
+ }
+
+ /* unfold */
+ else if (need_unfold)
+ {
+ /* unfold if is ALL operation */
+ ha_rows dup_cnt;
+ if (unlikely(table->file->ha_rnd_init_with_error(1)))
+ return 1;
+ do
+ {
+ if (unlikely(error= table->file->ha_rnd_next(table->record[0])))
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ error= 0;
+ break;
+ }
+ break;
+ }
+ dup_cnt= (ha_rows)duplicate_cnt->val_int();
+ /* delete record if not exist in the second operand */
+ if (dup_cnt == 0)
+ {
+ error= delete_record();
+ continue;
+ }
+ if (curr_op_type == INTERSECT_ALL)
+ {
+ ha_rows add_cnt= (ha_rows)additional_cnt->val_int();
+ if (dup_cnt > add_cnt && add_cnt > 0)
+ dup_cnt= (ha_rows)add_cnt;
+ }
+
+ if (dup_cnt == 1)
+ continue;
+
+ duplicate_cnt->store((longlong)1, 0);
+ if (additional_cnt)
+ additional_cnt->store((longlong)0, 0);
+ error= table->file->ha_update_tmp_row(table->record[1],
+ table->record[0]);
+ if (unlikely(error))
+ break;
+
+ if (unfold_record(dup_cnt) == -1)
+ {
+ /* restart the scan */
+ if (unlikely(table->file->ha_rnd_init_with_error(1)))
+ return 1;
+
+ duplicate_cnt= table->field[addon_cnt - 1];
+ if (addon_cnt == 2)
+ additional_cnt= table->field[addon_cnt - 2];
+ else
+ additional_cnt= NULL;
+ continue;
+ }
+ } while (likely(!error));
+ table->file->ha_rnd_end();
+ }
+
+ if (unlikely(error))
+ table->file->print_error(error, MYF(0));
+
+ return (MY_TEST(error));
+}
+
void select_union_recursive::cleanup()
{
if (table)
@@ -634,7 +1072,7 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl,
select_result *tmp_result,
- ulong additional_options,
+ ulonglong additional_options,
bool is_union_select)
{
DBUG_ENTER("st_select_lex_unit::prepare_join");
@@ -653,7 +1091,6 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl,
can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
saved_error= join->prepare(sl->table_list.first,
- sl->with_wild,
(derived && derived->merged ? NULL : sl->where),
(can_skip_order_by ? 0 :
sl->order_list.elements) +
@@ -667,8 +1104,6 @@ bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl,
thd_arg->lex->proc_list.first),
sl, this);
- /* There are no * in the statement anymore (for PS) */
- sl->with_wild= 0;
last_procedure= join->procedure;
if (unlikely(saved_error || (saved_error= thd_arg->is_fatal_error)))
@@ -818,9 +1253,32 @@ bool st_select_lex_unit::join_union_item_types(THD *thd_arg,
}
+bool init_item_int(THD* thd, Item_int* &item)
+{
+ if (!item)
+ {
+ Query_arena *arena, backup_arena;
+ arena= thd->activate_stmt_arena_if_needed(&backup_arena);
+
+ item= new (thd->mem_root) Item_int(thd, 0);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup_arena);
+
+ if (!item)
+ return false;
+ }
+ else
+ {
+ item->value= 0;
+ }
+ return true;
+}
+
+
bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
select_result *sel_result,
- ulong additional_options)
+ ulonglong additional_options)
{
SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select();
@@ -829,7 +1287,8 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
uint union_part_count= 0;
select_result *tmp_result;
bool is_union_select;
- bool have_except= FALSE, have_intersect= FALSE;
+ bool have_except= false, have_intersect= false,
+ have_except_all_or_intersect_all= false;
bool instantiate_tmp_table= false;
bool single_tvc= !first_sl->next_select() && first_sl->tvc;
bool single_tvc_wo_order= single_tvc && !first_sl->order_list.elements;
@@ -867,7 +1326,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
max/min subquery (ALL/ANY optimization)
*/
result= sel_result;
-
+
if (prepared)
{
if (describe)
@@ -885,8 +1344,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
else
{
sl->join->result= result;
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
+ lim.set_unlimited();
if (!sl->join->procedure &&
result->prepare(sl->join->fields_list, this))
{
@@ -925,15 +1383,27 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
global_parameters()->order_list.elements= 0;
}
+ /* will only optimize once */
+ if (!bag_set_op_optimized && !is_recursive)
+ {
+ optimize_bag_operation(false);
+ }
+
for (SELECT_LEX *s= first_sl; s; s= s->next_select())
{
switch (s->linkage)
{
case INTERSECT_TYPE:
have_intersect= TRUE;
+ if (!s->distinct){
+ have_except_all_or_intersect_all= true;
+ }
break;
case EXCEPT_TYPE:
have_except= TRUE;
+ if (!s->distinct){
+ have_except_all_or_intersect_all= TRUE;
+ }
break;
default:
break;
@@ -961,7 +1431,21 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
else
{
if (!is_recursive)
- union_result= new (thd->mem_root) select_unit(thd);
+ {
+ /*
+ class "select_unit_ext" handles query contains EXCEPT ALL and / or
+ INTERSECT ALL. Others are handled by class "select_unit"
+ If have EXCEPT ALL or INTERSECT ALL in the query. First operand
+ should be UNION ALL
+ */
+ if (have_except_all_or_intersect_all)
+ {
+ union_result= new (thd->mem_root) select_unit_ext(thd);
+ first_sl->distinct= false;
+ }
+ else
+ union_result= new (thd->mem_root) select_unit(thd);
+ }
else
{
with_element->rec_result=
@@ -1099,7 +1583,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
{
if (with_element)
{
- if (with_element->rename_columns_of_derived_unit(thd, this))
+ if (with_element->process_columns_of_derived_unit(thd, this))
goto err;
if (check_duplicate_names(thd, sl->item_list, 0))
goto err;
@@ -1136,6 +1620,10 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
instantiate_tmp_table, false,
0))
goto err;
+ if (have_except_all_or_intersect_all)
+ {
+ union_result->init();
+ }
if (!derived_arg->table)
{
derived_arg->table= with_element->rec_result->rec_tables.head();
@@ -1147,6 +1635,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
}
}
}
+
// In case of a non-recursive UNION, join data types for all UNION parts.
if (!is_recursive && join_union_item_types(thd, types, union_part_count))
goto err;
@@ -1222,48 +1711,42 @@ cont:
if (global_parameters()->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
+ /* extra field counter */
+ uint hidden= 0;
+ Item_int *addon_fields[2]= {0};
if (!is_recursive)
{
- uint hidden= 0;
- if (have_intersect)
+ if (have_except_all_or_intersect_all)
{
- hidden= 1;
- if (!intersect_mark)
- {
- /*
- For intersect we add a hidden column first that contains
- the current select number of the time when the row was
- added to the temporary table
- */
-
- Query_arena *arena, backup_arena;
- arena= thd->activate_stmt_arena_if_needed(&backup_arena);
-
- intersect_mark= new (thd->mem_root) Item_int(thd, 0);
-
- if (arena)
- thd->restore_active_arena(arena, &backup_arena);
+ /* add duplicate_count */
+ ++hidden;
+ }
+ /* add intersect_count */
+ if (have_intersect)
+ ++hidden;
- if (!intersect_mark)
- goto err;
- }
- else
- intersect_mark->value= 0; //reset
- types.push_front(union_result->intersect_mark= intersect_mark);
- union_result->intersect_mark->name.str= "___";
- union_result->intersect_mark->name.length= 3;
+ for(uint i= 0; i< hidden; i++)
+ {
+ init_item_int(thd, addon_fields[i]);
+ types.push_front(addon_fields[i]);
+ addon_fields[i]->name.str= i ? "__CNT_1" : "__CNT_2";
+ addon_fields[i]->name.length= 7;
}
bool error=
union_result->create_result_table(thd, &types,
- MY_TEST(union_distinct),
+ MY_TEST(union_distinct) ||
+ have_except_all_or_intersect_all ||
+ have_intersect,
create_options, &empty_clex_str, false,
instantiate_tmp_table, false,
hidden);
- if (intersect_mark)
+ union_result->addon_cnt= hidden;
+ for (uint i= 0; i < hidden; i++)
types.pop();
if (unlikely(error))
goto err;
}
+
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
save_tablenr= result_table_list.tablenr_exec;
@@ -1291,9 +1774,8 @@ cont:
arena= thd->activate_stmt_arena_if_needed(&backup_arena);
saved_error= table->fill_item_list(&item_list);
- // Item_list is inherited from 'types', so there could be the counter
- if (intersect_mark)
- item_list.pop(); // remove intersect counter
+ for (uint i= 0; i < hidden; i++)
+ item_list.pop();
if (arena)
thd->restore_active_arena(arena, &backup_arena);
@@ -1338,7 +1820,7 @@ cont:
We're in execution of a prepared statement or stored procedure:
reset field items to point at fields from the created temporary table.
*/
- table->reset_item_list(&item_list, intersect_mark ? 1 : 0);
+ table->reset_item_list(&item_list, hidden);
}
if (fake_select_lex != NULL &&
(thd->stmt_arena->is_stmt_prepare() ||
@@ -1352,7 +1834,7 @@ cont:
DBUG_RETURN(TRUE);
}
saved_error= fake_select_lex->join->
- prepare(fake_select_lex->table_list.first, 0, 0,
+ prepare(fake_select_lex->table_list.first, 0,
global_parameters()->order_list.elements, // og_num
global_parameters()->order_list.first, // order
false, NULL, NULL, NULL, fake_select_lex, this);
@@ -1372,9 +1854,170 @@ err:
/**
+ @brief
+ Optimize a sequence of set operations
+
+ @param first_sl first select of the level now under processing
+
+ @details
+ The method optimizes with the following rules:
+ - (1)If a subsequence of INTERSECT contains at least one INTERSECT DISTINCT
+ or this subsequence is followed by UNION/EXCEPT DISTINCT then all
+ elements in the subsequence can changed for INTERSECT DISTINCT
+ - (2)If previous set operation is DISTINCT then EXCEPT ALL can be replaced
+ for EXCEPT DISTINCT
+ - (3)If UNION DISTINCT / EXCEPT DISTINCT follows a subsequence of UNION ALL
+ then all set operations of this subsequence can be replaced for
+ UNION DISTINCT
+
+ For derived table it will look up outer select, and do optimize based on
+ outer select.
+
+ Variable "union_distinct" will be updated in the end.
+ Not compatible with Oracle Mode.
+*/
+
+void st_select_lex_unit::optimize_bag_operation(bool is_outer_distinct)
+{
+ /*
+ skip run optimize for:
+ ORACLE MODE
+ CREATE VIEW
+ PREPARE ... FROM
+ recursive
+ */
+ if ((thd->variables.sql_mode & MODE_ORACLE) ||
+ (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) ||
+ (fake_select_lex != NULL && thd->stmt_arena->is_stmt_prepare()) ||
+ (with_element && with_element->is_recursive ))
+ return;
+ DBUG_ASSERT(!bag_set_op_optimized);
+
+ SELECT_LEX *sl;
+ /* INTERSECT subsequence can occur only at the very beginning */
+ /* The first select with linkage == INTERSECT_TYPE */
+ SELECT_LEX *intersect_start= NULL;
+ /* The first select after the INTERSECT subsequence */
+ SELECT_LEX *intersect_end= NULL;
+ /*
+ Will point to the last node before UNION ALL subsequence.
+ Index can be disable there.
+ */
+ SELECT_LEX *disable_index= NULL;
+ /*
+ True if there is a select with:
+ linkage == INTERSECT_TYPE && distinct==true
+ */
+ bool any_intersect_distinct= false;
+ SELECT_LEX *prev_sl= first_select();
+
+ /* process INTERSECT subsequence in the begining */
+ for (sl= prev_sl->next_select(); sl; prev_sl= sl, sl= sl->next_select())
+ {
+ if (sl->linkage != INTERSECT_TYPE)
+ {
+ intersect_end= sl;
+ break;
+ }
+ else
+ {
+ if (!intersect_start)
+ intersect_start= sl;
+ if (sl->distinct)
+ {
+ any_intersect_distinct= true;
+ disable_index= sl;
+ }
+ }
+ }
+
+ /* if subquery only contains INTERSECT and outer is UNION DISTINCT*/
+ if (!sl && is_outer_distinct)
+ any_intersect_distinct= true;
+
+ /* The first select of the current UNION ALL subsequence */
+ SELECT_LEX *union_all_start= NULL;
+ for ( ; sl; prev_sl= sl, sl= sl->next_select())
+ {
+ DBUG_ASSERT (sl->linkage != INTERSECT_TYPE);
+ if (!sl->distinct)
+ {
+ if (sl->linkage == UNION_TYPE)
+ {
+ if (!union_all_start)
+ {
+ union_all_start= sl;
+ }
+ }
+ else
+ {
+ DBUG_ASSERT (sl->linkage == EXCEPT_TYPE);
+ union_all_start= NULL;
+ if (prev_sl->distinct && prev_sl->is_set_op())
+ {
+ sl->distinct= true;
+ disable_index= sl;
+ }
+ }
+ }
+ else
+ { /* sl->distinct == true */
+ for (SELECT_LEX *si= union_all_start; si && si != sl; si= si->next_select())
+ {
+ si->distinct= true;
+ }
+ union_all_start= NULL;
+ disable_index= sl;
+ }
+ }
+
+ if (is_outer_distinct)
+ {
+ for (SELECT_LEX *si= union_all_start; si && si != sl; si= si->next_select())
+ {
+ si->distinct= true;
+ }
+ union_all_start= NULL;
+ }
+
+ if (any_intersect_distinct ||
+ (intersect_end != NULL && intersect_end->distinct))
+ {
+ for (sl= intersect_start; sl && sl != intersect_end; sl= sl->next_select())
+ {
+ sl->distinct= true;
+ if (disable_index && disable_index->linkage == INTERSECT_TYPE)
+ disable_index= sl;
+ }
+ }
+ /*
+ if disable_index points to a INTERSECT, based on rule 1 we can set it
+ to the last INTERSECT node.
+ */
+ if (disable_index && disable_index->linkage == INTERSECT_TYPE &&
+ intersect_end && intersect_end->distinct)
+ disable_index= intersect_end;
+ /* union_distinct controls when to disable index */
+ union_distinct= disable_index;
+
+ /* recursive call this function for whole lex tree */
+ for(sl= first_select(); sl; sl= sl->next_select())
+ {
+ if (sl->is_unit_nest() &&
+ sl->first_inner_unit() &&
+ !sl->first_inner_unit()->bag_set_op_optimized)
+ sl->first_inner_unit()->optimize_bag_operation(sl->distinct);
+ }
+
+ /* mark as optimized */
+ bag_set_op_optimized= true;
+}
+
+
+/**
Run optimization phase.
- @return FALSE unit successfully passed optimization phase.
+ @return false unit successfully passed optimization phase.
@return TRUE an error occur.
*/
bool st_select_lex_unit::optimize()
@@ -1384,10 +2027,10 @@ bool st_select_lex_unit::optimize()
DBUG_ENTER("st_select_lex_unit::optimize");
if (optimized && !uncacheable && !describe)
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(false);
if (with_element && with_element->is_recursive && optimize_started)
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(false);
optimize_started= true;
if (uncacheable || !item || !item->assigned() || describe)
@@ -1407,9 +2050,12 @@ bool st_select_lex_unit::optimize()
table->file->info(HA_STATUS_VARIABLE);
}
/* re-enabling indexes for next subselect iteration */
- if (union_distinct && table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL))
+ if ((union_result->force_enable_index_if_needed() || union_distinct))
{
- DBUG_ASSERT(0);
+ if(table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL))
+ DBUG_ASSERT(0);
+ else
+ table->no_keyread= 0;
}
}
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
@@ -1417,7 +2063,7 @@ bool st_select_lex_unit::optimize()
if (sl->tvc)
{
sl->tvc->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
if (sl->tvc->optimize(thd))
{
@@ -1437,13 +2083,13 @@ bool st_select_lex_unit::optimize()
set_limit(sl);
if (sl == global_parameters() || describe)
{
- offset_limit_cnt= 0;
+ lim.remove_offset();
/*
We can't use LIMIT at this stage if we are using ORDER BY for the
whole query
*/
if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_unlimited();
}
/*
@@ -1452,7 +2098,7 @@ bool st_select_lex_unit::optimize()
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
@@ -1532,13 +2178,13 @@ bool st_select_lex_unit::exec()
set_limit(sl);
if (sl == global_parameters() || describe)
{
- offset_limit_cnt= 0;
+ lim.remove_offset();
/*
We can't use LIMIT at this stage if we are using ORDER BY for the
whole query
*/
if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_unlimited();
}
/*
@@ -1549,14 +2195,14 @@ bool st_select_lex_unit::exec()
if (sl->tvc)
{
sl->tvc->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->tvc->optimize(thd);
}
else
{
- sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->join->select_options=
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
}
@@ -1568,7 +2214,8 @@ bool st_select_lex_unit::exec()
sl->tvc->exec(sl);
else
sl->join->exec();
- if (sl == union_distinct && !(with_element && with_element->is_recursive))
+ if (sl == union_distinct && !have_except_all_or_intersect_all &&
+ !(with_element && with_element->is_recursive))
{
// This is UNION DISTINCT, so there should be a fake_select_lex
DBUG_ASSERT(fake_select_lex != NULL);
@@ -1578,9 +2225,6 @@ bool st_select_lex_unit::exec()
}
if (!sl->tvc)
saved_error= sl->join->error;
- offset_limit_cnt= (ha_rows)(sl->offset_limit ?
- sl->offset_limit->val_uint() :
- 0);
if (likely(!saved_error))
{
examined_rows+= thd->get_examined_row_count();
@@ -1607,8 +2251,8 @@ bool st_select_lex_unit::exec()
DBUG_RETURN(1);
}
}
- if (found_rows_for_union && !sl->braces &&
- select_limit_cnt != HA_POS_ERROR)
+ if (found_rows_for_union && !sl->braces &&
+ !lim.is_unlimited())
{
/*
This is a union without braces. Remember the number of rows that
@@ -1694,14 +2338,13 @@ bool st_select_lex_unit::exec()
if (!was_executed)
save_union_explain_part2(thd->lex->explain);
- saved_error= mysql_select(thd,
- &result_table_list,
- 0, item_list, NULL,
+ saved_error= mysql_select(thd, &result_table_list,
+ item_list, NULL,
global_parameters()->order_list.elements,
global_parameters()->order_list.first,
- NULL, NULL, NULL,
- fake_select_lex->options | SELECT_NO_UNLOCK,
- result, this, fake_select_lex);
+ NULL, NULL, NULL,
+ fake_select_lex->options | SELECT_NO_UNLOCK,
+ result, this, fake_select_lex);
}
else
{
@@ -1717,14 +2360,12 @@ bool st_select_lex_unit::exec()
to reset them back, we re-do all of the actions (yes it is ugly):
*/ // psergey-todo: is the above really necessary anymore??
join->init(thd, item_list, fake_select_lex->options, result);
- saved_error= mysql_select(thd,
- &result_table_list,
- 0, item_list, NULL,
+ saved_error= mysql_select(thd, &result_table_list, item_list, NULL,
global_parameters()->order_list.elements,
global_parameters()->order_list.first,
- NULL, NULL, NULL,
- fake_select_lex->options | SELECT_NO_UNLOCK,
- result, this, fake_select_lex);
+ NULL, NULL, NULL,
+ fake_select_lex->options | SELECT_NO_UNLOCK,
+ result, this, fake_select_lex);
}
else
{
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 9514b193407..9e6a1cd3bb4 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -35,7 +35,6 @@
#include "probes_mysql.h"
#include "debug_sync.h"
#include "key.h" // is_key_used
-#include "sql_acl.h" // *_ACL, check_grant
#include "records.h" // init_read_record,
// end_read_record
#include "filesort.h" // filesort
@@ -380,7 +379,7 @@ int mysql_update(THD *thd,
bool need_sort= TRUE;
bool reverse= FALSE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- uint want_privilege;
+ privilege_t want_privilege(NO_ACL);
#endif
uint table_count= 0;
ha_rows updated, found;
@@ -456,11 +455,10 @@ int mysql_update(THD *thd,
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "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();
+ table->opt_range_keys.clear_all();
query_plan.select_lex= thd->lex->first_select_lex();
query_plan.table= table;
@@ -596,7 +594,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())
+ if (table->opt_range_keys.is_clear_all())
{
thd->set_status_no_index_used();
if (safe_update && !using_limit)
@@ -633,9 +631,11 @@ int mysql_update(THD *thd,
else
{
ha_rows scanned_limit= query_plan.scanned_rows;
+ table->no_keyread= 1;
query_plan.index= get_index_for_order(order, table, select, limit,
&scanned_limit, &need_sort,
&reverse);
+ table->no_keyread= 0;
if (!need_sort)
query_plan.scanned_rows= scanned_limit;
@@ -688,7 +688,7 @@ int mysql_update(THD *thd,
if (!(explain= query_plan.save_explain_update_data(query_plan.mem_root, thd)))
goto err;
- ANALYZE_START_TRACKING(&explain->command_tracker);
+ ANALYZE_START_TRACKING(thd, &explain->command_tracker);
DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
dbug_serve_apcs(thd, 1););
@@ -948,7 +948,7 @@ update_begin:
thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- transactional_table= table->file->has_transactions();
+ transactional_table= table->file->has_transactions_and_rollback();
thd->abort_on_warning= !ignore && thd->is_strict_mode();
if (do_direct_update)
@@ -988,6 +988,9 @@ update_begin:
can_compare_record= records_are_comparable(table);
explain->tracker.on_scan_init();
+ table->file->prepare_for_insert(1);
+ DBUG_ASSERT(table->file->inited != handler::NONE);
+
THD_STAGE_INFO(thd, stage_updating);
while (!(error=info.read_record()) && !thd->killed)
{
@@ -1195,7 +1198,7 @@ update_begin:
break;
}
}
- ANALYZE_STOP_TRACKING(&explain->command_tracker);
+ ANALYZE_STOP_TRACKING(thd, &explain->command_tracker);
table->auto_increment_field_not_null= FALSE;
dup_key_found= 0;
/*
@@ -1241,7 +1244,7 @@ update_end:
table->file->try_semi_consistent_read(0);
if (!transactional_table && updated > 0)
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
end_read_record(&info);
delete select;
@@ -1260,10 +1263,10 @@ update_end:
query_cache_invalidate3(thd, table_list, 1);
}
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
/*
error < 0 means really no error at all: we processed all rows until the
@@ -1274,7 +1277,7 @@ update_end:
Sometimes we want to binlog even if we updated no rows, in case user used
it to be sure master and slave are in same state.
*/
- if (likely(error < 0) || thd->transaction.stmt.modified_non_trans_table)
+ if (likely(error < 0) || thd->transaction->stmt.modified_non_trans_table)
{
if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
@@ -1295,7 +1298,7 @@ update_end:
}
}
}
- DBUG_ASSERT(transactional_table || !updated || thd->transaction.stmt.modified_non_trans_table);
+ DBUG_ASSERT(transactional_table || !updated || thd->transaction->stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
delete file_sort;
if (table->file->pushed_cond)
@@ -1516,8 +1519,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, 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() &&
- table1->s->primary_key != MAX_KEY);
+ bool primkey_clustered= (table1->file->
+ pk_is_clustering_key(table1->s->primary_key));
bool table_partitioned= false;
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -1868,9 +1871,8 @@ int mysql_multi_update_prepare(THD *thd)
/* now lock and fill tables */
if (!thd->stmt_arena->is_stmt_prepare() &&
lock_tables(thd, table_list, table_count, 0))
- {
DBUG_RETURN(TRUE);
- }
+
(void) read_statistics_for_tables_if_needed(thd, table_list);
/* @todo: downgrade the metadata locks here. */
@@ -1893,8 +1895,8 @@ int mysql_multi_update_prepare(THD *thd)
(SELECT_ACL & ~tlist->grant.privilege);
table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
}
- DBUG_PRINT("info", ("table: %s want_privilege: %u", tl->alias.str,
- (uint) table->grant.want_privilege));
+ DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str,
+ (longlong) table->grant.want_privilege));
}
/*
Set exclude_from_table_unique_test value back to FALSE. It is needed for
@@ -1936,7 +1938,7 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields,
DBUG_RETURN(1);
res= mysql_select(thd,
- table_list, select_lex->with_wild, total_list, conds,
+ table_list, total_list, conds,
select_lex->order_list.elements,
select_lex->order_list.first, NULL, NULL, NULL,
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
@@ -2046,13 +2048,14 @@ int multi_update::prepare(List<Item> &not_used_values,
{
table->read_set= &table->def_read_set;
bitmap_union(table->read_set, &table->tmp_set);
+ table->file->prepare_for_insert(1);
}
}
if (unlikely(error))
DBUG_RETURN(1);
/*
- Save tables beeing updated in update_tables
+ Save tables being updated in update_tables
update_table->shared is position for table
Don't use key read on tables that are updated
*/
@@ -2370,7 +2373,7 @@ loop_end:
tmp_param->field_count= temp_fields.elements;
tmp_param->func_count= temp_fields.elements - 1;
calc_group_buffer(tmp_param, &group);
- /* small table, ignore SQL_BIG_TABLES */
+ /* small table, ignore @@big_tables */
my_bool save_big_tables= thd->variables.big_tables;
thd->variables.big_tables= FALSE;
tmp_tables[cnt]=create_tmp_table(thd, tmp_param, temp_fields,
@@ -2466,7 +2469,7 @@ multi_update::~multi_update()
delete [] copy_field;
thd->count_cuted_fields= CHECK_FIELD_IGNORE; // Restore this setting
DBUG_ASSERT(trans_safe || !updated ||
- thd->transaction.all.modified_non_trans_table);
+ thd->transaction->all.modified_non_trans_table);
}
@@ -2586,12 +2589,12 @@ int multi_update::send_data(List<Item> &not_used_values)
}
/* non-transactional or transactional table got modified */
/* either multi_update class' flag is raised in its branch */
- if (table->file->has_transactions())
+ if (table->file->has_transactions_and_rollback())
transactional_tables= TRUE;
else
{
trans_safe= FALSE;
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
}
}
}
@@ -2646,7 +2649,7 @@ void multi_update::abort_result_set()
{
/* the error was handled or nothing deleted and no side effects return */
if (unlikely(error_handled ||
- (!thd->transaction.stmt.modified_non_trans_table && !updated)))
+ (!thd->transaction->stmt.modified_non_trans_table && !updated)))
return;
/* Something already updated so we have to invalidate cache */
@@ -2659,14 +2662,14 @@ void multi_update::abort_result_set()
if (! trans_safe)
{
- DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table);
+ DBUG_ASSERT(thd->transaction->stmt.modified_non_trans_table);
if (do_update && table_count > 1)
{
/* Add warning here */
(void) do_updates();
}
}
- if (thd->transaction.stmt.modified_non_trans_table)
+ if (thd->transaction->stmt.modified_non_trans_table)
{
/*
The query has to binlog because there's a modified non-transactional table
@@ -2685,11 +2688,11 @@ void multi_update::abort_result_set()
thd->query(), thd->query_length(),
transactional_tables, FALSE, FALSE, errcode);
}
- thd->transaction.all.modified_non_trans_table= TRUE;
+ thd->transaction->all.modified_non_trans_table= TRUE;
}
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
- DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table);
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ DBUG_ASSERT(trans_safe || !updated || thd->transaction->stmt.modified_non_trans_table);
}
@@ -2905,12 +2908,12 @@ int multi_update::do_updates()
if (updated != org_updated)
{
- if (table->file->has_transactions())
+ if (table->file->has_transactions_and_rollback())
transactional_tables= TRUE;
else
{
trans_safe= FALSE; // Can't do safe rollback
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
}
}
(void) table->file->ha_rnd_end();
@@ -2942,12 +2945,12 @@ err2:
if (updated != org_updated)
{
- if (table->file->has_transactions())
+ if (table->file->has_transactions_and_rollback())
transactional_tables= TRUE;
else
{
trans_safe= FALSE;
- thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction->stmt.modified_non_trans_table= TRUE;
}
}
DBUG_RETURN(1);
@@ -2994,13 +2997,13 @@ bool multi_update::send_eof()
either from the query's list or via a stored routine: bug#13270,23333
*/
- if (thd->transaction.stmt.modified_non_trans_table)
- thd->transaction.all.modified_non_trans_table= TRUE;
- thd->transaction.all.m_unsafe_rollback_flags|=
- (thd->transaction.stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
+ if (thd->transaction->stmt.modified_non_trans_table)
+ thd->transaction->all.modified_non_trans_table= TRUE;
+ thd->transaction->all.m_unsafe_rollback_flags|=
+ (thd->transaction->stmt.m_unsafe_rollback_flags & THD_TRANS::DID_WAIT);
if (likely(local_error == 0 ||
- thd->transaction.stmt.modified_non_trans_table))
+ thd->transaction->stmt.modified_non_trans_table))
{
if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
{
@@ -3032,7 +3035,7 @@ bool multi_update::send_eof()
}
}
DBUG_ASSERT(trans_safe || !updated ||
- thd->transaction.stmt.modified_non_trans_table);
+ thd->transaction->stmt.modified_non_trans_table);
if (likely(local_error != 0))
error_handled= TRUE; // to force early leave from ::abort_result_set()
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 0a18a852832..617254244e1 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -27,7 +27,6 @@
#include "sql_show.h" // append_identifier
#include "sql_table.h" // build_table_filename
#include "sql_db.h" // mysql_opt_change_db, mysql_change_db
-#include "sql_acl.h" // *_ACL, check_grant
#include "sql_select.h"
#include "parse_file.h"
#include "sp_head.h"
@@ -37,6 +36,7 @@
#include "sql_derived.h"
#include "sql_cte.h" // check_dependencies_in_with_clauses()
#include "opt_trace.h"
+#include "wsrep_mysqld.h"
#define MD5_BUFF_LENGTH 33
@@ -137,7 +137,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view
Item *check;
/* treat underlying fields like set by user names */
if (item->real_item()->type() == Item::FIELD_ITEM)
- item->is_autogenerated_name= FALSE;
+ item->common_flags&= ~IS_AUTO_GENERATED_NAME;
itc.rewind();
while ((check= itc++) && check != item)
{
@@ -145,9 +145,9 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view
{
if (!gen_unique_view_name)
goto err;
- if (item->is_autogenerated_name)
+ if (item->is_autogenerated_name())
make_unique_view_field_name(thd, item, item_list, item);
- else if (check->is_autogenerated_name)
+ else if (check->is_autogenerated_name())
make_unique_view_field_name(thd, check, item_list, item);
else
goto err;
@@ -179,7 +179,7 @@ void make_valid_column_names(THD *thd, List<Item> &item_list)
for (uint column_no= 1; (item= it++); column_no++)
{
- if (!item->is_autogenerated_name || !check_column_name(item->name.str))
+ if (!item->is_autogenerated_name() || !check_column_name(item->name.str))
continue;
name_len= my_snprintf(buff, NAME_LEN, "Name_exp_%u", column_no);
item->orig_name= item->name.str;
@@ -432,7 +432,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
if (check_dependencies_in_with_clauses(lex->with_clauses_list))
{
res= TRUE;
- goto err;
+ goto err_no_relink;
}
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
@@ -453,6 +453,14 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
goto err_no_relink;
}
+#ifdef WITH_WSREP
+ if(!wsrep_should_replicate_ddl_iterate(thd, static_cast<const TABLE_LIST *>(tables)))
+ {
+ res= TRUE;
+ goto err_no_relink;
+ }
+#endif
+
view= lex->unlink_first_table(&link_to_local);
if (check_db_dir_existence(view->db.str))
@@ -555,8 +563,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
while ((item= it++, name= nm++))
{
- item->set_name(thd, name->str, (uint) name->length, system_charset_info);
- item->is_autogenerated_name= FALSE;
+ item->set_name(thd, *name);
+ item->common_flags&= ~IS_AUTO_GENERATED_NAME;
}
}
@@ -591,7 +599,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
This will hold the intersection of the priviliges on all columns in the
view.
*/
- uint final_priv= VIEW_ANY_ACL;
+ privilege_t final_priv(VIEW_ANY_ACL);
for (sl= select_lex; sl; sl= sl->next_select())
{
@@ -601,8 +609,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
while ((item= it++))
{
Item_field *fld= item->field_for_view_update();
- uint priv= (get_column_grant(thd, &view->grant, view->db.str,
- view->table_name.str, item->name.str) &
+ privilege_t priv(get_column_grant(thd, &view->grant, view->db.str,
+ view->table_name.str,
+ item->name.str) &
VIEW_ANY_ACL);
if (!fld)
@@ -645,7 +654,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
*/
if (!res)
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db.str, view->table_name.str, false);
+ tdc_remove_table(thd, view->db.str, view->table_name.str);
if (!res && mysql_bin_log.is_open())
{
@@ -927,16 +936,13 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view_query.length(0);
is_query.length(0);
{
- sql_mode_t sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
- thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
+ Sql_mode_instant_remove sms(thd, MODE_ANSI_QUOTES);
lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET |
QT_WITHOUT_INTRODUCERS |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
-
- thd->variables.sql_mode|= sql_mode;
}
DBUG_PRINT("info", ("View: %.*s", view_query.length(), view_query.ptr()));
@@ -1209,7 +1215,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
if (table->index_hints && table->index_hints->elements)
{
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0),
+ my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0),
table->index_hints->head()->key_name.str, table->table_name.str);
DBUG_RETURN(TRUE);
}
@@ -1804,10 +1810,10 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
char path[FN_REFLEN + 1];
TABLE_LIST *view;
String non_existant_views;
- const char *wrong_object_db= NULL, *wrong_object_name= NULL;
- bool error= FALSE;
+ bool delete_error= FALSE, wrong_object_name= FALSE;
bool some_views_deleted= FALSE;
bool something_wrong= FALSE;
+ uint not_exists_count= 0;
DBUG_ENTER("mysql_drop_view");
/*
@@ -1837,32 +1843,22 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
char name[FN_REFLEN];
my_snprintf(name, sizeof(name), "%s.%s", view->db.str,
view->table_name.str);
- if (thd->lex->if_exists())
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_UNKNOWN_VIEW,
- ER_THD(thd, ER_UNKNOWN_VIEW),
- name);
- continue;
- }
- if (not_exist)
+ if (non_existant_views.length())
+ non_existant_views.append(',');
+ non_existant_views.append(name);
+
+ if (!not_exist)
{
- if (non_existant_views.length())
- non_existant_views.append(',');
- non_existant_views.append(name);
+ wrong_object_name= 1;
+ my_error(ER_WRONG_OBJECT, MYF(ME_WARNING), view->db.str,
+ view->table_name.str, "VIEW");
}
else
- {
- if (!wrong_object_name)
- {
- wrong_object_db= view->db.str;
- wrong_object_name= view->table_name.str;
- }
- }
+ not_exists_count++;
continue;
}
if (unlikely(mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
- error= TRUE;
+ delete_error= TRUE;
some_views_deleted= TRUE;
@@ -1870,23 +1866,21 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
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.str, view->table_name.str,
- FALSE);
+ tdc_remove_table(thd, view->db.str, view->table_name.str);
query_cache_invalidate3(thd, view, 0);
sp_cache_invalidate();
}
- if (unlikely(wrong_object_name))
- {
- my_error(ER_WRONG_OBJECT, MYF(0), wrong_object_db, wrong_object_name,
- "VIEW");
- }
+ something_wrong= (delete_error ||
+ (!thd->lex->if_exists() && (not_exists_count ||
+ wrong_object_name)));
+
if (unlikely(non_existant_views.length()))
{
- my_error(ER_UNKNOWN_VIEW, MYF(0), non_existant_views.c_ptr_safe());
+ my_error(ER_UNKNOWN_VIEW, MYF(something_wrong ? 0 : ME_NOTE),
+ non_existant_views.c_ptr_safe());
}
- something_wrong= error || wrong_object_name || non_existant_views.length();
if (some_views_deleted || !something_wrong)
{
/* if something goes wrong, bin-log with possible error code,
diff --git a/sql/sql_window.cc b/sql/sql_window.cc
index abb3937096f..af6a73006a8 100644
--- a/sql/sql_window.cc
+++ b/sql/sql_window.cc
@@ -778,10 +778,10 @@ public:
{
//DBUG_ASSERT(info->read_record == rr_from_tempfile);
rownum= 0;
- io_cache= (IO_CACHE*)my_malloc(sizeof(IO_CACHE), MYF(0));
+ io_cache= (IO_CACHE*)my_malloc(PSI_INSTRUMENT_ME, sizeof(IO_CACHE), MYF(0));
init_slave_io_cache(info->io_cache, io_cache);
- ref_buffer= (uchar*)my_malloc(ref_length, MYF(0));
+ ref_buffer= (uchar*)my_malloc(PSI_INSTRUMENT_ME, ref_length, MYF(0));
ref_buffer_valid= false;
}
}
@@ -2816,7 +2816,7 @@ bool compute_window_func(THD *thd,
List_iterator_fast<Group_bound_tracker> iter_part_trackers(partition_trackers);
ha_rows rownum= 0;
- uchar *rowid_buf= (uchar*) my_malloc(tbl->file->ref_length, MYF(0));
+ uchar *rowid_buf= (uchar*) my_malloc(PSI_INSTRUMENT_ME, tbl->file->ref_length, MYF(0));
while (true)
{
@@ -2936,6 +2936,14 @@ bool Window_func_runner::add_function_to_run(Item_window_func *win_func)
my_error(ER_NOT_SUPPORTED_YET, MYF(0),
"COUNT(DISTINCT) aggregate as window function");
return true;
+ case Item_sum::JSON_ARRAYAGG_FUNC:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "JSON_ARRAYAGG() aggregate as window function");
+ return true;
+ case Item_sum::JSON_OBJECTAGG_FUNC:
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0),
+ "JSON_OBJECTAGG() aggregate as window function");
+ return true;
default:
break;
}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 3e3674d03ea..1b37896f18b 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -38,7 +38,6 @@
#include "sql_parse.h" /* comp_*_creator */
#include "sql_table.h" /* primary_key_name */
#include "sql_partition.h" /* partition_info, HASH_PARTITION */
-#include "sql_acl.h" /* *_ACL */
#include "sql_class.h" /* Key_part_spec, enum_filetype, Diag_condition_item_name */
#include "slave.h"
#include "lex_symbol.h"
@@ -144,7 +143,7 @@ static Item* escape(THD *thd)
to abort from the parser.
*/
-void MYSQLerror(THD *thd, const char *s)
+static void yyerror(THD *thd, const char *s)
{
/*
Restore the original LEX if it was replaced when parsing
@@ -161,7 +160,9 @@ void MYSQLerror(THD *thd, const char *s)
#ifndef DBUG_OFF
-void turn_parser_debug_on()
+#define __CONCAT_UNDERSCORED(x,y) x ## _ ## y
+#define _CONCAT_UNDERSCORED(x,y) __CONCAT_UNDERSCORED(x,y)
+void _CONCAT_UNDERSCORED(turn_parser_debug_on,yyparse)()
{
/*
MYSQLdebug is in sql/sql_yacc.cc, in bison generated code.
@@ -183,495 +184,6 @@ void turn_parser_debug_on()
#endif
-/**
- Helper action for a case expression statement (the expr in 'CASE expr').
- This helper is used for 'searched' cases only.
- @param lex the parser lex context
- @param expr the parsed expression
- @return 0 on success
-*/
-
-int LEX::case_stmt_action_expr(Item* expr)
-{
- int case_expr_id= spcont->register_case_expr();
- sp_instr_set_case_expr *i;
-
- if (spcont->push_case_expr_id(case_expr_id))
- return 1;
-
- i= new (thd->mem_root)
- sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id, expr,
- this);
-
- sphead->add_cont_backpatch(i);
- return sphead->add_instr(i);
-}
-
-/**
- Helper action for a case when condition.
- This helper is used for both 'simple' and 'searched' cases.
- @param lex the parser lex context
- @param when the parsed expression for the WHEN clause
- @param simple true for simple cases, false for searched cases
-*/
-
-int LEX::case_stmt_action_when(Item *when, bool simple)
-{
- uint ip= sphead->instructions();
- sp_instr_jump_if_not *i;
- Item_case_expr *var;
- Item *expr;
-
- if (simple)
- {
- var= new (thd->mem_root)
- Item_case_expr(thd, spcont->get_current_case_expr_id());
-
-#ifdef DBUG_ASSERT_EXISTS
- if (var)
- {
- var->m_sp= sphead;
- }
-#endif
-
- expr= new (thd->mem_root) Item_func_eq(thd, var, when);
- i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, expr, this);
- }
- else
- i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, when, this);
-
- /*
- BACKPATCH: Registering forward jump from
- "case_stmt_action_when" to "case_stmt_action_then"
- (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
- */
-
- return
- !MY_TEST(i) ||
- sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0)) ||
- sphead->add_cont_backpatch(i) ||
- sphead->add_instr(i);
-}
-
-/**
- Helper action for a case then statements.
- This helper is used for both 'simple' and 'searched' cases.
- @param lex the parser lex context
-*/
-
-int LEX::case_stmt_action_then()
-{
- uint ip= sphead->instructions();
- sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont);
- if (!MY_TEST(i) || sphead->add_instr(i))
- return 1;
-
- /*
- BACKPATCH: Resolving forward jump from
- "case_stmt_action_when" to "case_stmt_action_then"
- (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
- */
-
- sphead->backpatch(spcont->pop_label());
-
- /*
- BACKPATCH: Registering forward jump from
- "case_stmt_action_then" to after END CASE
- (jump from instruction 4 to 12, 7 to 12 ... in the example)
- */
-
- return sphead->push_backpatch(thd, i, spcont->last_label());
-}
-
-
-/**
- Helper action for a SET statement.
- Used to push a system variable into the assignment list.
-
- @param tmp the system variable with base name
- @param var_type the scope of the variable
- @param val the value being assigned to the variable
-
- @return TRUE if error, FALSE otherwise.
-*/
-
-bool
-LEX::set_system_variable(enum enum_var_type var_type,
- sys_var *sysvar, const LEX_CSTRING *base_name,
- Item *val)
-{
- set_var *setvar;
-
- /* No AUTOCOMMIT from a stored function or trigger. */
- if (spcont && sysvar == Sys_autocommit_ptr)
- sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
-
- if (val && val->type() == Item::FIELD_ITEM &&
- ((Item_field*)val)->table_name)
- {
- my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str);
- return TRUE;
- }
-
- if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar,
- base_name, val)))
- return TRUE;
-
- return var_list.push_back(setvar, thd->mem_root);
-}
-
-
-/**
- Helper action for a SET statement.
- Used to SET a field of NEW row.
-
- @param name the field name
- @param val the value being assigned to the row
-
- @return TRUE if error, FALSE otherwise.
-*/
-
-bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val)
-{
- Item_trigger_field *trg_fld;
- sp_instr_set_trigger_field *sp_fld;
-
- /* QQ: Shouldn't this be field's default value ? */
- if (! val)
- val= new (thd->mem_root) Item_null(thd);
-
- DBUG_ASSERT(trg_chistics.action_time == TRG_ACTION_BEFORE &&
- (trg_chistics.event == TRG_EVENT_INSERT ||
- trg_chistics.event == TRG_EVENT_UPDATE));
-
- trg_fld= new (thd->mem_root)
- Item_trigger_field(thd, current_context(),
- Item_trigger_field::NEW_ROW,
- name, UPDATE_ACL, FALSE);
-
- if (unlikely(trg_fld == NULL))
- return TRUE;
-
- sp_fld= new (thd->mem_root)
- sp_instr_set_trigger_field(sphead->instructions(),
- spcont, trg_fld, val, this);
-
- if (unlikely(sp_fld == NULL))
- return TRUE;
-
- /*
- Let us add this item to list of all Item_trigger_field
- objects in trigger.
- */
- trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field);
-
- return sphead->add_instr(sp_fld);
-}
-
-
-/**
- Create an object to represent a SP variable in the Item-hierarchy.
-
- @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.
-*/
-Item_splocal*
-LEX::create_item_for_sp_var(const Lex_ident_cli_st *cname, sp_variable *spvar)
-{
- const Sp_rcontext_handler *rh;
- Item_splocal *item;
- const char *start_in_q= cname->pos();
- const char *end_in_q= cname->end();
- uint pos_in_q, len_in_q;
- Lex_ident_sys name(thd, cname);
-
- if (name.is_null())
- return NULL; // EOM
-
- /* If necessary, look for the variable. */
- if (spcont && !spvar)
- spvar= find_variable(&name, &rh);
-
- if (!spvar)
- {
- my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str);
- return NULL;
- }
-
- DBUG_ASSERT(spcont && spvar);
-
- /* Position and length of the SP variable name in the query. */
- pos_in_q= (uint)(start_in_q - sphead->m_tmp_query);
- len_in_q= (uint)(end_in_q - start_in_q);
-
- item= new (thd->mem_root)
- Item_splocal(thd, rh, &name, spvar->offset, spvar->type_handler(),
- pos_in_q, len_in_q);
-
-#ifdef DBUG_ASSERT_EXISTS
- if (item)
- item->m_sp= 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
- <code>left [NOT] IN ( expr )</code>
- @param thd the current thread
- @param left the in predicand
- @param equal true for IN predicates, false for NOT IN predicates
- @param expr first and only expression of the in value list
- @return an expression representing the IN predicate.
-*/
-Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal,
- Item *expr)
-{
- /*
- Relevant references for this issue:
- - SQL:2003, Part 2, section 8.4 <in predicate>, page 383,
- - SQL:2003, Part 2, section 7.2 <row value expression>, page 296,
- - SQL:2003, Part 2, section 6.3 <value expression primary>, page 174,
- - SQL:2003, Part 2, section 7.15 <subquery>, page 370,
- - SQL:2003 Feature F561, "Full value expressions".
-
- The exception in SQL:2003 Note 184 means:
- Item_singlerow_subselect, which corresponds to a <scalar subquery>,
- should be re-interpreted as an Item_in_subselect, which corresponds
- to a <table subquery> when used inside an <in predicate>.
-
- Our reading of Note 184 is reccursive, so that all:
- - IN (( <subquery> ))
- - IN ((( <subquery> )))
- - IN '('^N <subquery> ')'^N
- - etc
- should be interpreted as a <table subquery>, no matter how deep in the
- expression the <subquery> is.
- */
-
- Item *result;
-
- DBUG_ENTER("handle_sql2003_note184_exception");
-
- if (expr->type() == Item::SUBSELECT_ITEM)
- {
- Item_subselect *expr2 = (Item_subselect*) expr;
-
- if (expr2->substype() == Item_subselect::SINGLEROW_SUBS)
- {
- Item_singlerow_subselect *expr3 = (Item_singlerow_subselect*) expr2;
- st_select_lex *subselect;
-
- /*
- Implement the mandated change, by altering the semantic tree:
- left IN Item_singlerow_subselect(subselect)
- is modified to
- left IN (subselect)
- which is represented as
- Item_in_subselect(left, subselect)
- */
- subselect= expr3->invalidate_and_restore_select_lex();
- result= new (thd->mem_root) Item_in_subselect(thd, left, subselect);
-
- if (! equal)
- result = negate_expression(thd, result);
-
- DBUG_RETURN(result);
- }
- }
-
- if (equal)
- result= new (thd->mem_root) Item_func_eq(thd, left, expr);
- else
- result= new (thd->mem_root) Item_func_ne(thd, left, expr);
-
- DBUG_RETURN(result);
-}
-
-/**
- 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
-*/
-
-bool 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;
- if (lex->main_select_push())
- return true;
- mysql_init_select(lex);
- lex->var_list.empty();
- lex->autocommit= 0;
- /* get_ptr() is only correct with no lookahead. */
- if (no_lookahead)
- lex->sphead->m_tmp_query= lip->get_ptr();
- else
- lex->sphead->m_tmp_query= lip->get_tok_end();
- /* Inherit from outer lex. */
- lex->option_type= old_lex->option_type;
- }
- return false;
-}
-
-
-/**
- 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.
-*/
-
-bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
-{
- LEX *lex= thd->lex;
-
- if (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_input_stream *lip= &thd->m_parser_state->m_lip;
-
- /*
- Extract the query statement from the tokenizer. The
- end is either lip->ptr, if there was no lookahead,
- lip->tok_end otherwise.
- */
- static const LEX_CSTRING setsp= { STRING_WITH_LEN("SET ") };
- const char *qend= no_lookahead ? lip->get_ptr() : lip->get_tok_end();
- Lex_cstring qbuf(lex->sphead->m_tmp_query, qend);
- if (lex->new_sp_instr_stmt(thd, setsp, qbuf))
- return true;
- }
- lex->pop_select();
- if (lex->check_main_unit_semantics())
- {
- /*
- "lex" can be referrenced by:
- - sp_instr_set SET a= expr;
- - sp_instr_set_row_field SET r.a= expr;
- - sp_instr_stmt (just generated above) SET @a= expr;
- In this case, "lex" is fully owned by sp_instr_xxx and it will
- be deleted by the destructor ~sp_instr_xxx().
- So we should remove "lex" from the stack sp_head::m_lex,
- to avoid double free.
- Note, in case "lex" is not owned by any sp_instr_xxx,
- it's also safe to remove it from the stack right now.
- So we can remove it unconditionally, without testing lex->sp_lex_in_use.
- */
- lex->sphead->restore_lex(thd);
- 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;
-}
-
-void LEX::add_key_to_list(LEX_CSTRING *field_name,
- enum Key::Keytype type, bool check_exists)
-{
- Key *key;
- MEM_ROOT *mem_root= thd->mem_root;
- key= new (mem_root)
- Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, false,
- DDL_options(check_exists ?
- DDL_options::OPT_IF_NOT_EXISTS :
- DDL_options::OPT_NONE));
- key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0),
- mem_root);
- alter_info.key_list.push_back(key, mem_root);
-}
-
-bool LEX::add_alter_list(const char *name, Virtual_column_info *expr,
- bool exists)
-{
- MEM_ROOT *mem_root= thd->mem_root;
- Alter_column *ac= new (mem_root) Alter_column(name, expr, exists);
- if (unlikely(ac == NULL))
- return true;
- alter_info.alter_list.push_back(ac, mem_root);
- alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
- return false;
-}
-
-void LEX::init_last_field(Column_definition *field,
- const LEX_CSTRING *field_name,
- const CHARSET_INFO *cs)
-{
- last_field= field;
-
- field->field_name= *field_name;
-
- /* reset LEX fields that are used in Create_field::set_and_check() */
- charset= cs;
-}
-
-
-bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
-{
- /*
- if charset is NULL - we're parsing a field declaration.
- we cannot call find_bin_collation for a field here, because actual
- field charset is determined in get_sql_field_charset() much later.
- so we only set a flag.
- */
- if (!charset)
- {
- charset= cs;
- last_field->flags|= bin ? BINCMP_FLAG : 0;
- return false;
- }
-
- charset= bin ? find_bin_collation(cs ? cs : charset)
- : cs ? cs : charset;
- return charset == NULL;
-}
-
#define bincmp_collation(X,Y) \
do \
{ \
@@ -679,18 +191,6 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
MYSQL_YYABORT; \
} while(0)
-
-Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
-{
- Virtual_column_info *v= new (thd->mem_root) Virtual_column_info();
- if (unlikely(!v))
- return 0;
- v->expr= expr;
- v->utf8= 0; /* connection charset */
- return v;
-}
-
-
%}
%union {
int num;
@@ -704,6 +204,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
Lex_ident_cli_st kwd;
Lex_ident_cli_st ident_cli;
Lex_ident_sys_st ident_sys;
+ Lex_column_list_privilege_st column_list_privilege;
Lex_string_with_metadata_st lex_string_with_metadata;
Lex_spblock_st spblock;
Lex_spblock_handlers_st spblock_handlers;
@@ -731,10 +232,12 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
Lex_order_limit_lock *order_limit_lock;
/* pointers */
+ Lex_ident_sys *ident_sys_ptr;
Create_field *create_field;
Spvar_definition *spvar_definition;
Row_definition_list *spvar_definition_list;
const Type_handler *type_handler;
+ const class Sp_handler *sp_handler;
CHARSET_INFO *charset;
Condition_information_item *cond_info_item;
DYNCALL_CREATE_DEF *dyncol_def;
@@ -745,6 +248,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
Item_basic_constant *item_basic_constant;
Key_part_spec *key_part;
LEX *lex;
+ sp_expr_lex *expr_lex;
sp_assignment_lex *assignment_lex;
class sp_lex_cursor *sp_cursor_stmt;
LEX_CSTRING *lex_str_ptr;
@@ -756,7 +260,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
List<sp_assignment_lex> *sp_assignment_lex_list;
List<Statement_information_item> *stmt_info_list;
List<String> *string_list;
- List<LEX_CSTRING> *lex_str_list;
+ List<Lex_ident_sys> *ident_sys_list;
Statement_information_item *stmt_info_item;
String *string;
TABLE_LIST *table_list;
@@ -765,6 +269,8 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
char *simple_string;
const char *const_simple_string;
chooser_compare_func_creator boolfunc2creator;
+ class Lex_grant_privilege *lex_grant;
+ class Lex_grant_object_name *lex_grant_ident;
class my_var *myvar;
class sp_condition_value *spcondvalue;
class sp_head *sphead;
@@ -789,7 +295,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
enum Condition_information_item::Name cond_info_item_name;
enum enum_diag_condition_item_name diag_condition_item_name;
enum Diagnostics_information::Which_area diag_area;
- enum Field::geometry_type geom_type;
enum enum_fk_option m_fk_option;
enum Item_udftype udf_type;
enum Key::Keytype key_type;
@@ -812,9 +317,10 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
enum Window_frame::Frame_exclusion frame_exclusion;
enum trigger_order_type trigger_action_order_type;
DDL_options_st object_ddl_options;
- enum vers_sys_type_t vers_range_unit;
+ enum vers_kind_t vers_range_unit;
enum Column_definition::enum_column_versioning vers_column_versioning;
enum plsql_cursor_attr_t plsql_cursor_attr;
+ privilege_t privilege;
}
%{
@@ -829,7 +335,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
/*
We should not introduce any further shift/reduce conflicts.
*/
-%expect 54
+
+/* Start SQL_MODE_DEFAULT_SPECIFIC */
+%expect 53
+/* End SQL_MODE_DEFAULT_SPECIFIC */
+
+
+/* Start SQL_MODE_ORACLE_SPECIFIC
+%expect 56
+End SQL_MODE_ORACLE_SPECIFIC */
+
/*
Comments for TOKENS.
@@ -851,316 +366,340 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
*/
+%token <lex_str> '@'
+
+/*
+ Special purpose tokens
+*/
+%token <NONE> ABORT_SYM /* INTERNAL (used in lex) */
+%token <NONE> IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */
+%token <NONE> END_OF_INPUT /* INTERNAL */
+%token <kwd> COLON_ORACLE_SYM /* INTERNAL */
+%token <kwd> PARAM_MARKER /* INTERNAL */
+%token <NONE> FOR_SYSTEM_TIME_SYM /* INTERNAL */
+%token <NONE> LEFT_PAREN_ALT /* INTERNAL */
+%token <NONE> LEFT_PAREN_WITH /* INTERNAL */
+%token <NONE> LEFT_PAREN_LIKE /* INTERNAL */
+%token <NONE> ORACLE_CONCAT_SYM /* INTERNAL */
+%token <NONE> PERCENT_ORACLE_SYM /* INTERNAL */
+%token <NONE> WITH_CUBE_SYM /* INTERNAL */
+%token <NONE> WITH_ROLLUP_SYM /* INTERNAL */
+%token <NONE> WITH_SYSTEM_SYM /* INTERNAL */
+
+
/*
- Reserved keywords and operators
+ Identifiers
*/
-%token ABORT_SYM /* INTERNAL (used in lex) */
-%token ACCESSIBLE_SYM
-%token ADD /* SQL-2003-R */
-%token ALL /* SQL-2003-R */
-%token ALTER /* SQL-2003-R */
-%token ANALYZE_SYM
-%token AND_AND_SYM /* OPERATOR */
-%token AND_SYM /* SQL-2003-R */
-%token AS /* SQL-2003-R */
-%token ASC /* SQL-2003-N */
-%token ASENSITIVE_SYM /* FUTURE-USE */
-%token BEFORE_SYM /* SQL-2003-N */
-%token BETWEEN_SYM /* SQL-2003-R */
-%token BIGINT /* SQL-2003-R */
-%token BINARY /* SQL-2003-R */
-%token BIN_NUM
-%token BIT_AND /* MYSQL-FUNC */
-%token BIT_OR /* MYSQL-FUNC */
-%token BIT_XOR /* MYSQL-FUNC */
-%token BLOB_MARIADB_SYM /* SQL-2003-R */
-%token BLOB_ORACLE_SYM /* Oracle-R */
-%token BODY_ORACLE_SYM /* Oracle-R */
-%token BOTH /* SQL-2003-R */
-%token BY /* SQL-2003-R */
-%token CALL_SYM /* SQL-2003-R */
-%token CASCADE /* SQL-2003-N */
-%token CASE_SYM /* SQL-2003-R */
-%token CAST_SYM /* SQL-2003-R */
-%token CHANGE
-%token CHAR_SYM /* SQL-2003-R */
-%token CHECK_SYM /* SQL-2003-R */
-%token COLLATE_SYM /* SQL-2003-R */
-%token CONDITION_SYM /* SQL-2003-R, SQL-2008-R */
-%token CONSTRAINT /* SQL-2003-R */
-%token CONTINUE_MARIADB_SYM /* SQL-2003-R, Oracle-R */
-%token CONTINUE_ORACLE_SYM /* SQL-2003-R, Oracle-R */
-%token CONVERT_SYM /* SQL-2003-N */
-%token COUNT_SYM /* SQL-2003-N */
-%token CREATE /* SQL-2003-R */
-%token CROSS /* SQL-2003-R */
-%token CUME_DIST_SYM
-%token CURDATE /* MYSQL-FUNC */
-%token CURRENT_USER /* SQL-2003-R */
-%token CURRENT_ROLE /* SQL-2003-R */
-%token CURSOR_SYM /* SQL-2003-R */
-%token CURTIME /* MYSQL-FUNC */
-%token DATABASE
-%token DATABASES
-%token DATE_ADD_INTERVAL /* MYSQL-FUNC */
-%token DATE_SUB_INTERVAL /* MYSQL-FUNC */
-%token DAY_HOUR_SYM
-%token DAY_MICROSECOND_SYM
-%token DAY_MINUTE_SYM
-%token DAY_SECOND_SYM
-%token DECIMAL_NUM
-%token DECIMAL_SYM /* SQL-2003-R */
-%token DECLARE_MARIADB_SYM /* SQL-2003-R */
-%token DECLARE_ORACLE_SYM /* Oracle-R */
-%token DEFAULT /* SQL-2003-R */
-%token DELETE_DOMAIN_ID_SYM
-%token DELETE_SYM /* SQL-2003-R */
-%token DENSE_RANK_SYM
-%token DESC /* SQL-2003-N */
-%token DESCRIBE /* SQL-2003-R */
-%token DETERMINISTIC_SYM /* SQL-2003-R */
-%token DISTINCT /* SQL-2003-R */
-%token DIV_SYM
-%token DOUBLE_SYM /* SQL-2003-R */
-%token DO_DOMAIN_IDS_SYM
-%token DOT_DOT_SYM
-%token DROP /* SQL-2003-R */
-%token DUAL_SYM
-%token EACH_SYM /* SQL-2003-R */
-%token ELSE /* SQL-2003-R */
-%token ELSEIF_MARIADB_SYM
-%token ELSIF_ORACLE_SYM /* PLSQL-R */
-%token ENCLOSED
-%token END_OF_INPUT /* INTERNAL */
-%token EQUAL_SYM /* OPERATOR */
-%token ESCAPED
-%token EXCEPT_SYM /* SQL-2003-R */
-%token EXISTS /* SQL-2003-R */
-%token EXTRACT_SYM /* SQL-2003-N */
-%token FALSE_SYM /* SQL-2003-R */
-%token FETCH_SYM /* SQL-2003-R */
-%token FIRST_VALUE_SYM /* SQL-2011 */
-%token FLOAT_NUM
-%token FLOAT_SYM /* SQL-2003-R */
-%token FOREIGN /* SQL-2003-R */
-%token FOR_SYM /* SQL-2003-R */
-%token FOR_SYSTEM_TIME_SYM /* INTERNAL */
-%token FROM
-%token FULLTEXT_SYM
-%token GE
-%token GOTO_ORACLE_SYM /* Oracle-R */
-%token GRANT /* SQL-2003-R */
-%token GROUP_SYM /* SQL-2003-R */
-%token GROUP_CONCAT_SYM
-%token LAG_SYM /* SQL-2011 */
-%token LEAD_SYM /* SQL-2011 */
-%token HAVING /* SQL-2003-R */
-%token HEX_NUM
-%token HEX_STRING
-%token HOUR_MICROSECOND_SYM
-%token HOUR_MINUTE_SYM
-%token HOUR_SECOND_SYM
%token IDENT
%token IDENT_QUOTED
-%token IF_SYM
-%token IGNORE_DOMAIN_IDS_SYM
-%token IGNORE_SYM
-%token INDEX_SYM
-%token INFILE
-%token INNER_SYM /* SQL-2003-R */
-%token INOUT_SYM /* SQL-2003-R */
-%token INSENSITIVE_SYM /* SQL-2003-R */
-%token INSERT /* SQL-2003-R */
-%token INTERSECT_SYM /* SQL-2003-R */
-%token INTERVAL_SYM /* SQL-2003-R */
-%token INTO /* SQL-2003-R */
-%token INT_SYM /* SQL-2003-R */
-%token IN_SYM /* SQL-2003-R */
-%token IS /* SQL-2003-R */
-%token ITERATE_SYM
-%token JOIN_SYM /* SQL-2003-R */
-%token KEYS
-%token KEY_SYM /* SQL-2003-N */
-%token KILL_SYM
-%token LE /* OPERATOR */
-%token LEADING /* SQL-2003-R */
-%token LEAVE_SYM
-%token LEFT /* SQL-2003-R */
-%token LEFT_PAREN_ALT /* INTERNAL */
-%token LEFT_PAREN_WITH /* INTERNAL */
-%token LEFT_PAREN_LIKE /* INTERNAL */
%token LEX_HOSTNAME
-%token LIKE /* SQL-2003-R */
-%token LIMIT
-%token LINEAR_SYM
-%token LINES
-%token LOAD
-%token LOCATOR_SYM /* SQL-2003-N */
-%token LOCK_SYM
-%token LONGBLOB
-%token LONGTEXT
-%token LONG_NUM
-%token LONG_SYM
-%token LOOP_SYM
-%token LOW_PRIORITY
-%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
-%token MATCH /* SQL-2003-R */
-%token MAX_SYM /* SQL-2003-N */
-%token MAXVALUE_SYM /* SQL-2003-N */
-%token MEDIAN_SYM
-%token MEDIUMBLOB
-%token MEDIUMINT
-%token MEDIUMTEXT
-%token MINUTE_MICROSECOND_SYM
-%token MINUTE_SECOND_SYM
-%token MIN_SYM /* SQL-2003-N */
-%token MODIFIES_SYM /* SQL-2003-R */
-%token MOD_SYM /* SQL-2003-N */
-%token MYSQL_CONCAT_SYM /* OPERATOR */
-%token NATURAL /* SQL-2003-R */
-%token NCHAR_STRING
-%token NE /* OPERATOR */
-%token NEG
-%token NOT2_SYM
-%token NOT_SYM /* SQL-2003-R */
-%token NOW_SYM
-%token NO_WRITE_TO_BINLOG
-%token NTILE_SYM
-%token NULL_SYM /* SQL-2003-R */
-%token NUM
-%token NUMERIC_SYM /* SQL-2003-R */
-%token NTH_VALUE_SYM /* SQL-2011 */
-%token ON /* SQL-2003-R */
-%token OPTIMIZE
-%token OPTIONALLY
-%token ORACLE_CONCAT_SYM /* INTERNAL */
-%token OR2_SYM
-%token ORDER_SYM /* SQL-2003-R */
-%token OR_SYM /* SQL-2003-R */
-%token OTHERS_ORACLE_SYM /* SQL-2011-N, PLSQL-R */
-%token OUTER
-%token OUTFILE
-%token OUT_SYM /* SQL-2003-R */
-%token OVER_SYM
-%token PACKAGE_ORACLE_SYM /* Oracle-R */
-%token PAGE_CHECKSUM_SYM
-%token PARAM_MARKER
-%token PARSE_VCOL_EXPR_SYM
-%token PARTITION_SYM /* SQL-2003-R */
-%token PERCENT_ORACLE_SYM /* INTERNAL */
-%token PERCENT_RANK_SYM
-%token PERCENTILE_CONT_SYM
-%token PERCENTILE_DISC_SYM
-%token PORTION_SYM /* SQL-2016-R */
-%token POSITION_SYM /* SQL-2003-N */
-%token PRECISION /* SQL-2003-R */
-%token PRIMARY_SYM /* SQL-2003-R */
-%token PROCEDURE_SYM /* SQL-2003-R */
-%token PURGE
-%token RAISE_ORACLE_SYM /* PLSQL-R */
-%token RANGE_SYM /* SQL-2003-R */
-%token RANK_SYM
-%token READS_SYM /* SQL-2003-R */
-%token READ_SYM /* SQL-2003-N */
-%token READ_WRITE_SYM
-%token REAL /* SQL-2003-R */
-%token RECURSIVE_SYM
-%token REF_SYSTEM_ID_SYM
-%token REFERENCES /* SQL-2003-R */
-%token REGEXP
-%token RELEASE_SYM /* SQL-2003-R */
-%token RENAME
-%token REPEAT_SYM /* MYSQL-FUNC */
-%token REPLACE /* MYSQL-FUNC */
-%token REQUIRE_SYM
-%token RESIGNAL_SYM /* SQL-2003-R */
-%token RESTRICT
-%token RETURNING_SYM
-%token RETURN_MARIADB_SYM /* SQL-2003-R, PLSQL-R */
-%token RETURN_ORACLE_SYM /* SQL-2003-R, PLSQL-R */
-%token REVOKE /* SQL-2003-R */
-%token RIGHT /* SQL-2003-R */
-%token ROWS_SYM /* SQL-2003-R */
-%token ROWTYPE_ORACLE_SYM /* PLSQL-R */
-%token ROW_NUMBER_SYM
-%token SECOND_MICROSECOND_SYM
-%token SELECT_SYM /* SQL-2003-R */
-%token SENSITIVE_SYM /* FUTURE-USE */
-%token SEPARATOR_SYM
-%token SERVER_OPTIONS
-%token SET /* SQL-2003-R */
-%token SET_VAR
-%token SHIFT_LEFT /* OPERATOR */
-%token SHIFT_RIGHT /* OPERATOR */
-%token SHOW
-%token SIGNAL_SYM /* SQL-2003-R */
-%token SMALLINT /* SQL-2003-R */
-%token SPATIAL_SYM
-%token SPECIFIC_SYM /* SQL-2003-R */
-%token SQLEXCEPTION_SYM /* SQL-2003-R */
-%token SQLSTATE_SYM /* SQL-2003-R */
-%token SQLWARNING_SYM /* SQL-2003-R */
-%token SQL_BIG_RESULT
-%token SQL_SMALL_RESULT
-%token SQL_SYM /* SQL-2003-R */
-%token SSL_SYM
-%token STARTING
-%token STATS_AUTO_RECALC_SYM
-%token STATS_PERSISTENT_SYM
-%token STATS_SAMPLE_PAGES_SYM
-%token STDDEV_SAMP_SYM /* SQL-2003-N */
-%token STD_SYM
-%token STRAIGHT_JOIN
-%token SUBSTRING /* SQL-2003-N */
-%token SUM_SYM /* SQL-2003-N */
-%token SYSDATE
-%token TABLE_REF_PRIORITY
-%token TABLE_SYM /* SQL-2003-R */
-%token TERMINATED
-%token TEXT_STRING
-%token THEN_SYM /* SQL-2003-R */
-%token TINYBLOB
-%token TINYINT
-%token TINYTEXT
-%token TO_SYM /* SQL-2003-R */
-%token TRAILING /* SQL-2003-R */
-%token TRIGGER_SYM /* SQL-2003-R */
-%token TRIM /* SQL-2003-N */
-%token TRUE_SYM /* SQL-2003-R */
-%token ULONGLONG_NUM
-%token UNDERSCORE_CHARSET
-%token UNDO_SYM /* FUTURE-USE */
-%token UNION_SYM /* SQL-2003-R */
-%token UNIQUE_SYM
-%token UNLOCK_SYM
-%token UNSIGNED
-%token UPDATE_SYM /* SQL-2003-R */
-%token USAGE /* SQL-2003-N */
-%token USE_SYM
-%token USING /* SQL-2003-R */
-%token UTC_DATE_SYM
-%token UTC_TIMESTAMP_SYM
-%token UTC_TIME_SYM
-%token VALUES /* SQL-2003-R */
-%token VALUES_IN_SYM
-%token VALUES_LESS_SYM
-%token VARBINARY
-%token VARCHAR /* SQL-2003-R */
-%token VARIANCE_SYM
-%token VARYING /* SQL-2003-R */
-%token VAR_SAMP_SYM
-%token WHEN_SYM /* SQL-2003-R */
-%token WHERE /* SQL-2003-R */
-%token WHILE_SYM
-%token WITH /* SQL-2003-R */
-%token WITH_CUBE_SYM /* INTERNAL */
-%token WITH_ROLLUP_SYM /* INTERNAL */
-%token WITH_SYSTEM_SYM /* INTERNAL */
-%token XOR
-%token YEAR_MONTH_SYM
-%token ZEROFILL
-
-%token IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */
+%token UNDERSCORE_CHARSET /* _latin1 */
+
+
+/*
+ Literals
+*/
+%token BIN_NUM /* LITERAL */
+%token DECIMAL_NUM /* LITERAL */
+%token FLOAT_NUM /* LITERAL */
+%token HEX_NUM /* LITERAL */
+%token HEX_STRING /* LITERAL */
+%token LONG_NUM /* LITERAL */
+%token NCHAR_STRING /* LITERAL */
+%token NUM /* LITERAL */
+%token TEXT_STRING /* LITERAL */
+%token ULONGLONG_NUM /* LITERAL */
+
+
+/*
+ Operators
+*/
+%token <NONE> AND_AND_SYM /* OPERATOR */
+%token <NONE> DOT_DOT_SYM /* OPERATOR */
+%token <NONE> EQUAL_SYM /* OPERATOR */
+%token <NONE> GE /* OPERATOR */
+%token <NONE> LE /* OPERATOR */
+%token <NONE> MYSQL_CONCAT_SYM /* OPERATOR */
+%token <NONE> NE /* OPERATOR */
+%token <NONE> NOT2_SYM /* OPERATOR */
+%token <NONE> OR2_SYM /* OPERATOR */
+%token <NONE> SET_VAR /* OPERATOR */
+%token <NONE> SHIFT_LEFT /* OPERATOR */
+%token <NONE> SHIFT_RIGHT /* OPERATOR */
+
+
+/*
+ Reserved keywords
+*/
+%token <kwd> ACCESSIBLE_SYM
+%token <kwd> ADD /* SQL-2003-R */
+%token <kwd> ALL /* SQL-2003-R */
+%token <kwd> ALTER /* SQL-2003-R */
+%token <kwd> ANALYZE_SYM
+%token <kwd> AND_SYM /* SQL-2003-R */
+%token <kwd> ASC /* SQL-2003-N */
+%token <kwd> ASENSITIVE_SYM /* FUTURE-USE */
+%token <kwd> AS /* SQL-2003-R */
+%token <kwd> BEFORE_SYM /* SQL-2003-N */
+%token <kwd> BETWEEN_SYM /* SQL-2003-R */
+%token <kwd> BIGINT /* SQL-2003-R */
+%token <kwd> BINARY /* SQL-2003-R */
+%token <kwd> BIT_AND /* MYSQL-FUNC */
+%token <kwd> BIT_OR /* MYSQL-FUNC */
+%token <kwd> BIT_XOR /* MYSQL-FUNC */
+%token <kwd> BLOB_MARIADB_SYM /* SQL-2003-R */
+%token <kwd> BLOB_ORACLE_SYM /* Oracle-R */
+%token <kwd> BODY_ORACLE_SYM /* Oracle-R */
+%token <kwd> BOTH /* SQL-2003-R */
+%token <kwd> BY /* SQL-2003-R */
+%token <kwd> CALL_SYM /* SQL-2003-R */
+%token <kwd> CASCADE /* SQL-2003-N */
+%token <kwd> CASE_SYM /* SQL-2003-R */
+%token <kwd> CAST_SYM /* SQL-2003-R */
+%token <kwd> CHANGE
+%token <kwd> CHAR_SYM /* SQL-2003-R */
+%token <kwd> CHECK_SYM /* SQL-2003-R */
+%token <kwd> COLLATE_SYM /* SQL-2003-R */
+%token <kwd> CONDITION_SYM /* SQL-2003-R, SQL-2008-R */
+%token <kwd> CONSTRAINT /* SQL-2003-R */
+%token <kwd> CONTINUE_MARIADB_SYM /* SQL-2003-R, Oracle-R */
+%token <kwd> CONTINUE_ORACLE_SYM /* SQL-2003-R, Oracle-R */
+%token <kwd> CONVERT_SYM /* SQL-2003-N */
+%token <kwd> COUNT_SYM /* SQL-2003-N */
+%token <kwd> CREATE /* SQL-2003-R */
+%token <kwd> CROSS /* SQL-2003-R */
+%token <kwd> CUME_DIST_SYM
+%token <kwd> CURDATE /* MYSQL-FUNC */
+%token <kwd> CURRENT_ROLE /* SQL-2003-R */
+%token <kwd> CURRENT_USER /* SQL-2003-R */
+%token <kwd> CURSOR_SYM /* SQL-2003-R */
+%token <kwd> CURTIME /* MYSQL-FUNC */
+%token <kwd> DATABASE
+%token <kwd> DATABASES
+%token <kwd> DATE_ADD_INTERVAL /* MYSQL-FUNC */
+%token <kwd> DATE_SUB_INTERVAL /* MYSQL-FUNC */
+%token <kwd> DAY_HOUR_SYM
+%token <kwd> DAY_MICROSECOND_SYM
+%token <kwd> DAY_MINUTE_SYM
+%token <kwd> DAY_SECOND_SYM
+%token <kwd> DECIMAL_SYM /* SQL-2003-R */
+%token <kwd> DECLARE_MARIADB_SYM /* SQL-2003-R */
+%token <kwd> DECLARE_ORACLE_SYM /* Oracle-R */
+%token <kwd> DEFAULT /* SQL-2003-R */
+%token <kwd> DELETE_DOMAIN_ID_SYM
+%token <kwd> DELETE_SYM /* SQL-2003-R */
+%token <kwd> DENSE_RANK_SYM
+%token <kwd> DESCRIBE /* SQL-2003-R */
+%token <kwd> DESC /* SQL-2003-N */
+%token <kwd> DETERMINISTIC_SYM /* SQL-2003-R */
+%token <kwd> DISTINCT /* SQL-2003-R */
+%token <kwd> DIV_SYM
+%token <kwd> DO_DOMAIN_IDS_SYM
+%token <kwd> DOUBLE_SYM /* SQL-2003-R */
+%token <kwd> DROP /* SQL-2003-R */
+%token <kwd> DUAL_SYM
+%token <kwd> EACH_SYM /* SQL-2003-R */
+%token <kwd> ELSEIF_MARIADB_SYM
+%token <kwd> ELSE /* SQL-2003-R */
+%token <kwd> ELSIF_ORACLE_SYM /* PLSQL-R */
+%token <kwd> ENCLOSED
+%token <kwd> ESCAPED
+%token <kwd> EXCEPT_SYM /* SQL-2003-R */
+%token <kwd> EXISTS /* SQL-2003-R */
+%token <kwd> EXTRACT_SYM /* SQL-2003-N */
+%token <kwd> FALSE_SYM /* SQL-2003-R */
+%token <kwd> FETCH_SYM /* SQL-2003-R */
+%token <kwd> FIRST_VALUE_SYM /* SQL-2011 */
+%token <kwd> FLOAT_SYM /* SQL-2003-R */
+%token <kwd> FOREIGN /* SQL-2003-R */
+%token <kwd> FOR_SYM /* SQL-2003-R */
+%token <kwd> FROM
+%token <kwd> FULLTEXT_SYM
+%token <kwd> GOTO_ORACLE_SYM /* Oracle-R */
+%token <kwd> GRANT /* SQL-2003-R */
+%token <kwd> GROUP_CONCAT_SYM
+%token <rwd> JSON_ARRAYAGG_SYM
+%token <rwd> JSON_OBJECTAGG_SYM
+%token <kwd> GROUP_SYM /* SQL-2003-R */
+%token <kwd> HAVING /* SQL-2003-R */
+%token <kwd> HOUR_MICROSECOND_SYM
+%token <kwd> HOUR_MINUTE_SYM
+%token <kwd> HOUR_SECOND_SYM
+%token <kwd> IF_SYM
+%token <kwd> IGNORE_DOMAIN_IDS_SYM
+%token <kwd> IGNORE_SYM
+%token <kwd> INDEX_SYM
+%token <kwd> INFILE
+%token <kwd> INNER_SYM /* SQL-2003-R */
+%token <kwd> INOUT_SYM /* SQL-2003-R */
+%token <kwd> INSENSITIVE_SYM /* SQL-2003-R */
+%token <kwd> INSERT /* SQL-2003-R */
+%token <kwd> IN_SYM /* SQL-2003-R */
+%token <kwd> INTERSECT_SYM /* SQL-2003-R */
+%token <kwd> INTERVAL_SYM /* SQL-2003-R */
+%token <kwd> INTO /* SQL-2003-R */
+%token <kwd> INT_SYM /* SQL-2003-R */
+%token <kwd> IS /* SQL-2003-R */
+%token <kwd> ITERATE_SYM
+%token <kwd> JOIN_SYM /* SQL-2003-R */
+%token <kwd> KEYS
+%token <kwd> KEY_SYM /* SQL-2003-N */
+%token <kwd> KILL_SYM
+%token <kwd> LAG_SYM /* SQL-2011 */
+%token <kwd> LEADING /* SQL-2003-R */
+%token <kwd> LEAD_SYM /* SQL-2011 */
+%token <kwd> LEAVE_SYM
+%token <kwd> LEFT /* SQL-2003-R */
+%token <kwd> LIKE /* SQL-2003-R */
+%token <kwd> LIMIT
+%token <kwd> LINEAR_SYM
+%token <kwd> LINES
+%token <kwd> LOAD
+%token <kwd> LOCATOR_SYM /* SQL-2003-N */
+%token <kwd> LOCK_SYM
+%token <kwd> LONGBLOB
+%token <kwd> LONG_SYM
+%token <kwd> LONGTEXT
+%token <kwd> LOOP_SYM
+%token <kwd> LOW_PRIORITY
+%token <kwd> MASTER_SSL_VERIFY_SERVER_CERT_SYM
+%token <kwd> MATCH /* SQL-2003-R */
+%token <kwd> MAX_SYM /* SQL-2003-N */
+%token <kwd> MAXVALUE_SYM /* SQL-2003-N */
+%token <kwd> MEDIAN_SYM
+%token <kwd> MEDIUMBLOB
+%token <kwd> MEDIUMINT
+%token <kwd> MEDIUMTEXT
+%token <kwd> MIN_SYM /* SQL-2003-N */
+%token <kwd> MINUTE_MICROSECOND_SYM
+%token <kwd> MINUTE_SECOND_SYM
+%token <kwd> MODIFIES_SYM /* SQL-2003-R */
+%token <kwd> MOD_SYM /* SQL-2003-N */
+%token <kwd> NATURAL /* SQL-2003-R */
+%token <kwd> NEG
+%token <kwd> NOT_SYM /* SQL-2003-R */
+%token <kwd> NO_WRITE_TO_BINLOG
+%token <kwd> NOW_SYM
+%token <kwd> NTH_VALUE_SYM /* SQL-2011 */
+%token <kwd> NTILE_SYM
+%token <kwd> NULL_SYM /* SQL-2003-R */
+%token <kwd> NUMERIC_SYM /* SQL-2003-R */
+%token <kwd> ON /* SQL-2003-R */
+%token <kwd> OPTIMIZE
+%token <kwd> OPTIONALLY
+%token <kwd> ORDER_SYM /* SQL-2003-R */
+%token <kwd> OR_SYM /* SQL-2003-R */
+%token <kwd> OTHERS_ORACLE_SYM /* SQL-2011-N, PLSQL-R */
+%token <kwd> OUTER
+%token <kwd> OUTFILE
+%token <kwd> OUT_SYM /* SQL-2003-R */
+%token <kwd> OVER_SYM
+%token <kwd> PACKAGE_ORACLE_SYM /* Oracle-R */
+%token <kwd> PAGE_CHECKSUM_SYM
+%token <kwd> PARSE_VCOL_EXPR_SYM
+%token <kwd> PARTITION_SYM /* SQL-2003-R */
+%token <kwd> PERCENTILE_CONT_SYM
+%token <kwd> PERCENTILE_DISC_SYM
+%token <kwd> PERCENT_RANK_SYM
+%token <kwd> PORTION_SYM /* SQL-2016-R */
+%token <kwd> POSITION_SYM /* SQL-2003-N */
+%token <kwd> PRECISION /* SQL-2003-R */
+%token <kwd> PRIMARY_SYM /* SQL-2003-R */
+%token <kwd> PROCEDURE_SYM /* SQL-2003-R */
+%token <kwd> PURGE
+%token <kwd> RAISE_ORACLE_SYM /* PLSQL-R */
+%token <kwd> RANGE_SYM /* SQL-2003-R */
+%token <kwd> RANK_SYM
+%token <kwd> READS_SYM /* SQL-2003-R */
+%token <kwd> READ_SYM /* SQL-2003-N */
+%token <kwd> READ_WRITE_SYM
+%token <kwd> REAL /* SQL-2003-R */
+%token <kwd> RECURSIVE_SYM
+%token <kwd> REFERENCES /* SQL-2003-R */
+%token <kwd> REF_SYSTEM_ID_SYM
+%token <kwd> REGEXP
+%token <kwd> RELEASE_SYM /* SQL-2003-R */
+%token <kwd> RENAME
+%token <kwd> REPEAT_SYM /* MYSQL-FUNC */
+%token <kwd> REPLACE /* MYSQL-FUNC */
+%token <kwd> REQUIRE_SYM
+%token <kwd> RESIGNAL_SYM /* SQL-2003-R */
+%token <kwd> RESTRICT
+%token <kwd> RETURNING_SYM
+%token <kwd> RETURN_MARIADB_SYM /* SQL-2003-R, PLSQL-R */
+%token <kwd> RETURN_ORACLE_SYM /* SQL-2003-R, PLSQL-R */
+%token <kwd> REVOKE /* SQL-2003-R */
+%token <kwd> RIGHT /* SQL-2003-R */
+%token <kwd> ROW_NUMBER_SYM
+%token <kwd> ROWS_SYM /* SQL-2003-R */
+%token <kwd> ROWTYPE_ORACLE_SYM /* PLSQL-R */
+%token <kwd> SECOND_MICROSECOND_SYM
+%token <kwd> SELECT_SYM /* SQL-2003-R */
+%token <kwd> SENSITIVE_SYM /* FUTURE-USE */
+%token <kwd> SEPARATOR_SYM
+%token <kwd> SERVER_OPTIONS
+%token <kwd> SET /* SQL-2003-R */
+%token <kwd> SHOW
+%token <kwd> SIGNAL_SYM /* SQL-2003-R */
+%token <kwd> SMALLINT /* SQL-2003-R */
+%token <kwd> SPATIAL_SYM
+%token <kwd> SPECIFIC_SYM /* SQL-2003-R */
+%token <kwd> SQL_BIG_RESULT
+%token <kwd> SQLEXCEPTION_SYM /* SQL-2003-R */
+%token <kwd> SQL_SMALL_RESULT
+%token <kwd> SQLSTATE_SYM /* SQL-2003-R */
+%token <kwd> SQL_SYM /* SQL-2003-R */
+%token <kwd> SQLWARNING_SYM /* SQL-2003-R */
+%token <kwd> SSL_SYM
+%token <kwd> STARTING
+%token <kwd> STATS_AUTO_RECALC_SYM
+%token <kwd> STATS_PERSISTENT_SYM
+%token <kwd> STATS_SAMPLE_PAGES_SYM
+%token <kwd> STDDEV_SAMP_SYM /* SQL-2003-N */
+%token <kwd> STD_SYM
+%token <kwd> STRAIGHT_JOIN
+%token <kwd> SUBSTRING /* SQL-2003-N */
+%token <kwd> SUM_SYM /* SQL-2003-N */
+%token <kwd> SYSDATE
+%token <kwd> TABLE_REF_PRIORITY
+%token <kwd> TABLE_SYM /* SQL-2003-R */
+%token <kwd> TERMINATED
+%token <kwd> THEN_SYM /* SQL-2003-R */
+%token <kwd> TINYBLOB
+%token <kwd> TINYINT
+%token <kwd> TINYTEXT
+%token <kwd> TO_SYM /* SQL-2003-R */
+%token <kwd> TRAILING /* SQL-2003-R */
+%token <kwd> TRIGGER_SYM /* SQL-2003-R */
+%token <kwd> TRIM /* SQL-2003-N */
+%token <kwd> TRUE_SYM /* SQL-2003-R */
+%token <kwd> UNDO_SYM /* FUTURE-USE */
+%token <kwd> UNION_SYM /* SQL-2003-R */
+%token <kwd> UNIQUE_SYM
+%token <kwd> UNLOCK_SYM
+%token <kwd> UNSIGNED
+%token <kwd> UPDATE_SYM /* SQL-2003-R */
+%token <kwd> USAGE /* SQL-2003-N */
+%token <kwd> USE_SYM
+%token <kwd> USING /* SQL-2003-R */
+%token <kwd> UTC_DATE_SYM
+%token <kwd> UTC_TIMESTAMP_SYM
+%token <kwd> UTC_TIME_SYM
+%token <kwd> VALUES_IN_SYM
+%token <kwd> VALUES_LESS_SYM
+%token <kwd> VALUES /* SQL-2003-R */
+%token <kwd> VARBINARY
+%token <kwd> VARCHAR /* SQL-2003-R */
+%token <kwd> VARIANCE_SYM
+%token <kwd> VAR_SAMP_SYM
+%token <kwd> VARYING /* SQL-2003-R */
+%token <kwd> WHEN_SYM /* SQL-2003-R */
+%token <kwd> WHERE /* SQL-2003-R */
+%token <kwd> WHILE_SYM
+%token <kwd> WITH /* SQL-2003-R */
+%token <kwd> XOR
+%token <kwd> YEAR_MONTH_SYM
+%token <kwd> ZEROFILL
/*
@@ -1226,7 +765,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> COALESCE /* SQL-2003-N */
%token <kwd> CODE_SYM
%token <kwd> COLLATION_SYM /* SQL-2003-N */
-%token <kwd> COLON_ORACLE_SYM /* INTERNAL */
%token <kwd> COLUMNS
%token <kwd> COLUMN_ADD_SYM
%token <kwd> COLUMN_CHECK_SYM
@@ -1304,6 +842,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> EXTENT_SIZE_SYM
%token <kwd> FAST_SYM
%token <kwd> FAULTS_SYM
+%token <kwd> FEDERATED_SYM /* MariaDB privilege */
%token <kwd> FILE_SYM
%token <kwd> FIRST_SYM /* SQL-2003-N */
%token <kwd> FIXED_SYM
@@ -1317,8 +856,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> FUNCTION_SYM /* SQL-2003-R, Oracle-R */
%token <kwd> GENERAL
%token <kwd> GENERATED_SYM
-%token <kwd> GEOMETRYCOLLECTION
-%token <kwd> GEOMETRY_SYM
%token <kwd> GET_FORMAT /* MYSQL-FUNC */
%token <kwd> GET_SYM /* SQL-2003-R */
%token <kwd> GLOBAL_SYM /* SQL-2003-R */
@@ -1358,7 +895,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> LEAVES
%token <kwd> LESS_SYM
%token <kwd> LEVEL_SYM
-%token <kwd> LINESTRING
%token <kwd> LIST_SYM
%token <kwd> LOCAL_SYM /* SQL-2003-R */
%token <kwd> LOCKS_SYM
@@ -1403,10 +939,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> MIN_ROWS
%token <kwd> MODE_SYM
%token <kwd> MODIFY_SYM
+%token <kwd> MONITOR_SYM /* MariaDB privilege */
%token <kwd> MONTH_SYM /* SQL-2003-R */
-%token <kwd> MULTILINESTRING
-%token <kwd> MULTIPOINT
-%token <kwd> MULTIPOLYGON
%token <kwd> MUTEX_SYM
%token <kwd> MYSQL_SYM
%token <kwd> MYSQL_ERRNO_SYM
@@ -1440,6 +974,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> OPEN_SYM /* SQL-2003-R */
%token <kwd> OPTIONS_SYM
%token <kwd> OPTION /* SQL-2003-N */
+%token <kwd> OVERLAPS_SYM
%token <kwd> OWNER_SYM
%token <kwd> PACK_KEYS_SYM
%token <kwd> PAGE_SYM
@@ -1453,8 +988,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> PHASE_SYM
%token <kwd> PLUGINS_SYM
%token <kwd> PLUGIN_SYM
-%token <kwd> POINT_SYM
-%token <kwd> POLYGON
%token <kwd> PORT_SYM
%token <kwd> PRECEDES_SYM /* MYSQL */
%token <kwd> PRECEDING_SYM /* SQL-2011-N */
@@ -1489,6 +1022,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> REORGANIZE_SYM
%token <kwd> REPAIR
%token <kwd> REPEATABLE_SYM /* SQL-2003-N */
+%token <kwd> REPLAY_SYM /* MariaDB privilege */
%token <kwd> REPLICATION
%token <kwd> RESET_SYM
%token <kwd> RESTART_SYM
@@ -1573,6 +1107,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> TIME_SYM /* SQL-2003-R, Oracle-R */
%token <kwd> TRANSACTION_SYM
%token <kwd> TRANSACTIONAL_SYM
+%token <kwd> THREADS_SYM
%token <kwd> TRIGGERS_SYM
%token <kwd> TRIM_ORACLE
%token <kwd> TRUNCATE_SYM
@@ -1598,6 +1133,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%token <kwd> VERSIONING_SYM /* SQL-2011-R */
%token <kwd> VIA_SYM
%token <kwd> VIEW_SYM /* SQL-2003-N */
+%token <kwd> VISIBLE_SYM /* MySQL 8.0 */
%token <kwd> VIRTUAL_SYM
%token <kwd> WAIT_SYM
%token <kwd> WARNINGS
@@ -1704,7 +1240,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
and until NEXT_SYM / PREVIOUS_SYM.
*/
%left PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE
-%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM USER
+%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM USER COMMENT_SYM
/*
@@ -1742,20 +1278,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
key_cache_name
sp_opt_label BIN_NUM TEXT_STRING_filesystem
opt_constraint constraint opt_ident
- sp_block_label opt_place opt_db
-
-%type <lex_str>
- sp_label
+ sp_block_label sp_control_label opt_place opt_db
%type <ident_sys>
IDENT_sys
ident
label_ident
sp_decl_ident
- ident_set_usual_case
ident_or_empty
ident_table_alias
ident_sysvar_name
+ ident_for_loop_index
%type <lex_string_with_metadata>
TEXT_STRING
@@ -1770,9 +1303,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
IDENT_QUOTED
IDENT_cli
ident_cli
+ ident_cli_set_usual_case
+
+%type <ident_sys_ptr>
+ ident_sys_alloc
%type <kwd>
keyword_data_type
+ keyword_cast_type
keyword_ident
keyword_label
keyword_set_special_case
@@ -1786,6 +1324,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
keyword_sysvar_type
keyword_table_alias
keyword_verb_clause
+ charset
+ reserved_keyword_udt
+ reserved_keyword_udt_not_param_type
+ non_reserved_keyword_udt
%type <table>
table_ident table_ident_nodb references xid
@@ -1800,7 +1342,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
wild_and_where
%type <const_simple_string>
- field_length opt_field_length opt_field_length_default_1
+ field_length opt_field_length
opt_compression_method
%type <string>
@@ -1808,6 +1350,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <type_handler> int_type real_type
+%type <sp_handler> sp_handler
+
%type <Lex_field_type> type_with_opt_collate field_type
qualified_field_type
field_type_numeric
@@ -1819,9 +1363,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <Lex_dyncol_type> opt_dyncol_type dyncol_type
numeric_dyncol_type temporal_dyncol_type string_dyncol_type
-%type <create_field> field_spec column_def
+%type <column_list_privilege>
+ column_list_privilege
-%type <geom_type> spatial_type
+%type <create_field> field_spec column_def
%type <num>
order_dir lock_option
@@ -1858,12 +1403,28 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <m_fk_option>
delete_option
+%type <privilege>
+ column_privilege
+ object_privilege
+ opt_grant_options
+ opt_grant_option
+ grant_option_list
+ grant_option
+
+%type <lex_grant>
+ object_privilege_list
+ grant_privileges
+
+%type <lex_grant_ident>
+ grant_ident
+
%type <ulong_num>
ulong_num real_ulong_num merge_insert_types
- ws_nweights opt_versioning_interval_start
+ 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 bool
+ field_options last_field_options
%type <ulonglong_number>
ulonglong_num real_ulonglong_num size_number
@@ -1875,6 +1436,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <lock_type>
replace_lock_option opt_low_priority insert_lock_option load_data_lock
+ insert_replace_option
%type <item>
literal insert_ident order_ident temporal_literal
@@ -1885,7 +1447,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
primary_expr string_factor_expr mysql_concatenation_expr
select_sublist_qualified_asterisk
expr_or_default set_expr_or_default
- geometry_function signed_literal expr_or_literal
+ signed_literal expr_or_literal
sp_opt_default
simple_ident_nospvar
field_or_var limit_option
@@ -1905,7 +1467,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
signal_allowed_expr
simple_target_specification
condition_number
- reset_lex_expr
+ opt_versioning_interval_start
%type <item_param> param_marker
@@ -1925,6 +1487,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
sp_cursor_stmt_lex
sp_cursor_stmt
+%type <expr_lex>
+ expr_lex
+
%type <assignment_lex>
assignment_source_lex
assignment_source_expr
@@ -1938,7 +1503,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
option_type opt_var_type opt_var_ident_type
%type <key_type>
- opt_unique constraint_key_type fulltext spatial
+ constraint_key_type fulltext spatial
%type <key_alg>
btree_or_rtree opt_key_algorithm_clause opt_USING_key_algorithm
@@ -1978,6 +1543,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
admin_option_for_role user_maybe_role
%type <user_auth> opt_auth_str auth_expression auth_token
+ text_or_password
%type <charset>
opt_collate
@@ -2039,9 +1605,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <select_order> opt_order_clause order_clause order_list
%type <NONE>
+ directly_executable_statement
analyze_stmt_command backup backup_statements
- query verb_clause create change select select_into
- do drop insert replace insert2
+ query verb_clause create create_routine change select select_into
+ do drop drop_routine insert replace insert_start stmt_end
insert_values update delete truncate rename compound_statement
show describe load alter optimize keycache preload flush
reset purge begin_stmt_mariadb commit rollback savepoint release
@@ -2051,7 +1618,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
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
+ check start checksum opt_returning
field_list field_list_item kill key_def constraint_def
keycache_list keycache_list_or_parts assign_to_keycache
assign_to_keycache_parts
@@ -2062,7 +1629,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
procedure_list procedure_list2 procedure_item
field_def handler opt_generated_always
opt_ignore opt_column opt_restrict
- grant revoke set lock unlock string_list field_options
+ grant revoke set lock unlock string_list
opt_binary table_lock_list table_lock
ref_list opt_match_clause opt_on_update_delete use
opt_delete_options opt_delete_option varchar nchar nvarchar
@@ -2070,9 +1637,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
attribute attribute_list
compressed_deprecated_data_type_attribute
compressed_deprecated_column_attribute
- column_list column_list_id
- opt_column_list grant_privileges grant_ident grant_list grant_option
- object_privilege object_privilege_list user_list user_and_role_list
+ grant_list
+ user_list user_and_role_list
rename_list table_or_tables
clear_privileges flush_options flush_option
opt_flush_lock flush_lock flush_options_list
@@ -2081,7 +1647,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
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
- opt_and charset
+ opt_and
select_var_list select_var_list_init help
opt_extended_describe shutdown
opt_format_json
@@ -2090,7 +1656,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
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_list_opt view_list view_select
- trigger_tail sp_tail event_tail
+ trigger_tail event_tail
install uninstall partition_entry binlog_base64_event
normal_key_options normal_key_opts all_key_opt
spatial_key_options fulltext_key_options normal_key_opt
@@ -2110,23 +1676,24 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_delete_gtid_domain
asrow_attribute
opt_constraint_no_id
-END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
+%type <NONE> sp_if_then_statements sp_case_then_statements
%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return
- sp_proc_stmt_in_returns_clause
%type <NONE> sp_proc_stmt_compound_ok
%type <NONE> sp_proc_stmt_if
%type <NONE> sp_labeled_control sp_unlabeled_control
-%type <NONE> sp_labeled_block sp_unlabeled_block sp_unlabeled_block_not_atomic
+%type <NONE> sp_labeled_block sp_unlabeled_block
%type <NONE> sp_proc_stmt_continue_oracle
%type <NONE> sp_proc_stmt_exit_oracle
%type <NONE> sp_proc_stmt_leave
%type <NONE> sp_proc_stmt_iterate
%type <NONE> sp_proc_stmt_goto_oracle
+%type <NONE> sp_proc_stmt_with_cursor
%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
%type <NONE> case_stmt_specification
%type <NONE> loop_body while_body repeat_body
+%type <NONE> for_loop_statements
%type <num> view_algorithm view_check_option
%type <view_suid> view_suid opt_view_suid
@@ -2138,15 +1705,14 @@ END_OF_INPUT
%type <num> sp_decl_idents sp_decl_idents_init_vars
%type <num> sp_handler_type sp_hcond_list
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
-%type <spblock> sp_decl_handler
-%type <spblock> sp_decls sp_decl sp_decl_body sp_decl_variable_list
%type <spname> sp_name
%type <spvar> sp_param_name sp_param_name_and_type
+%type <spvar> sp_param_name_and_type_anchored
%type <for_loop> sp_for_loop_index_and_bounds
%type <for_loop_bounds> sp_for_loop_bounds
%type <trim> trim_operands
%type <num> opt_sp_for_loop_direction
-%type <spvar_mode> sp_opt_inout
+%type <spvar_mode> sp_parameter_type
%type <index_hint> index_hint_type
%type <num> index_hint_clause normal_join inner_join
%type <filetype> data_or_xml
@@ -2181,20 +1747,72 @@ END_OF_INPUT
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|'
-%type <NONE>
- AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
- THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
- MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM
-
%type <with_clause> with_clause
%type <lex_str_ptr> query_name
-%type <lex_str_list> opt_with_column_list
+%type <ident_sys_list>
+ comma_separated_ident_list
+ opt_with_column_list
+ with_column_list
+ opt_cycle
%type <vers_range_unit> opt_history_unit
%type <vers_history_point> history_point
%type <vers_column_versioning> with_or_without_system
+
+/* Start SQL_MODE_DEFAULT_SPECIFIC */
+%type <NONE> sp_tail_standalone
+%type <NONE> sp_unlabeled_block_not_atomic
+%type <NONE> sp_proc_stmt_in_returns_clause
+%type <lex_str> sp_label
+%type <spblock> sp_decl_handler
+%type <spblock> sp_decls
+%type <spblock> sp_decl
+%type <spblock> sp_decl_body
+%type <spblock> sp_decl_variable_list
+%type <spblock> sp_decl_variable_list_anchored
+%type <kwd> reserved_keyword_udt_param_type
+/* End SQL_MODE_DEFAULT_SPECIFIC */
+
+
+/* Start SQL_MODE_ORACLE_SPECIFIC
+%type <NONE> set_assign
+%type <spvar_mode> sp_opt_inout
+%type <NONE> sp_tail_standalone
+%type <NONE> sp_labelable_stmt
+%type <simple_string> remember_end_opt
+%type <lex_str> opt_package_routine_end_name
+%type <lex_str> label_declaration_oracle
+%type <lex_str> labels_declaration_oracle
+%type <kwd> keyword_directly_assignable
+%type <ident_sys> ident_directly_assignable
+%type <ident_cli> ident_cli_directly_assignable
+%type <spname> opt_sp_name
+%type <spblock> sp_decl_body_list
+%type <spblock> opt_sp_decl_body_list
+%type <spblock> sp_decl_variable_list
+%type <spblock> sp_decl_variable_list_anchored
+%type <spblock> sp_decl_non_handler
+%type <spblock> sp_decl_non_handler_list
+%type <spblock> sp_decl_handler
+%type <spblock> sp_decl_handler_list
+%type <spblock> opt_sp_decl_handler_list
+%type <spblock> package_implementation_routine_definition
+%type <spblock> package_implementation_item_declaration
+%type <spblock> package_implementation_declare_section
+%type <spblock> package_implementation_declare_section_list1
+%type <spblock> package_implementation_declare_section_list2
+%type <spblock_handlers> sp_block_statements_and_exceptions
+%type <spblock_handlers> package_implementation_executable_section
+%type <sp_instr_addr> sp_instr_addr
+%type <num> opt_exception_clause exception_handlers
+%type <lex> remember_lex
+%type <lex> package_routine_lex
+%type <lex> package_specification_function
+%type <lex> package_specification_procedure
+End SQL_MODE_ORACLE_SPECIFIC */
+
%%
@@ -2229,7 +1847,7 @@ query:
thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
YYLIP->found_semicolon= NULL;
}
- | verb_clause
+ | directly_executable_statement
{
Lex_input_stream *lip = YYLIP;
@@ -2254,7 +1872,7 @@ query:
}
';'
opt_end_of_input
- | verb_clause END_OF_INPUT
+ | directly_executable_statement END_OF_INPUT
{
/* Single query, not terminated. */
YYLIP->found_semicolon= NULL;
@@ -2266,14 +1884,14 @@ opt_end_of_input:
| END_OF_INPUT
;
-verb_clause:
+directly_executable_statement:
statement
| begin_stmt_mariadb
| compound_statement
;
/* Verb clauses, except begin and compound_statement */
-statement:
+verb_clause:
alter
| analyze
| analyze_stmt_command
@@ -2748,7 +2366,7 @@ create:
create_table_set_open_action_and_adjust_tables(lex);
Lex->pop_select(); //main select
}
- | create_or_replace opt_unique INDEX_SYM opt_if_not_exists
+ | create_or_replace INDEX_SYM opt_if_not_exists
{
if (Lex->main_select_push())
MYSQL_YYABORT;
@@ -2757,9 +2375,9 @@ create:
opt_key_algorithm_clause
ON table_ident
{
- if (Lex->add_create_index_prepare($9))
+ if (Lex->add_create_index_prepare($8))
MYSQL_YYABORT;
- if (Lex->add_create_index($2, &$6, $7, $1 | $4))
+ if (Lex->add_create_index(Key::MULTIPLE, &$5, $6, $1 | $3))
MYSQL_YYABORT;
}
'(' key_list ')' opt_lock_wait_timeout normal_key_options
@@ -2767,6 +2385,26 @@ create:
{
Lex->pop_select(); //main select
}
+ | create_or_replace UNIQUE_SYM INDEX_SYM opt_if_not_exists
+ {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ }
+ ident
+ opt_key_algorithm_clause
+ ON table_ident
+ {
+ if (Lex->add_create_index_prepare($9))
+ MYSQL_YYABORT;
+ if (Lex->add_create_index(Key::UNIQUE, &$6, $7, $1 | $4))
+ MYSQL_YYABORT;
+ }
+ '(' key_list opt_without_overlaps ')'
+ opt_lock_wait_timeout normal_key_options
+ opt_index_lock_algorithm
+ {
+ Lex->pop_select(); //main select
+ }
| create_or_replace fulltext INDEX_SYM
{
if (Lex->main_select_push())
@@ -2806,6 +2444,7 @@ create:
| create_or_replace DATABASE opt_if_not_exists ident
{
Lex->create_info.default_table_charset= NULL;
+ Lex->create_info.schema_comment= NULL;
Lex->create_info.used_fields= 0;
}
opt_create_database_options
@@ -2851,15 +2490,6 @@ create:
{
Lex->pop_select(); //main select
}
- | create_or_replace definer_opt PROCEDURE_SYM opt_if_not_exists
- {
- if (Lex->stmt_create_procedure_start($1 | $4))
- MYSQL_YYABORT;
- }
- sp_tail
- {
- Lex->stmt_create_routine_finalize();
- }
| create_or_replace definer_opt EVENT_SYM
{
if (Lex->main_select_push())
@@ -2870,37 +2500,6 @@ create:
{
Lex->pop_select(); //main select
}
- | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- sp_name '('
- {
- if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
- MYSQL_YYABORT;
- }
- sp_fdparam_list ')'
- sf_return_type
- sf_c_chistics_and_body
- {
- Lex->stmt_create_routine_finalize();
- }
- | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- sp_name '('
- {
- if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
- MYSQL_YYABORT;
- }
- sp_fdparam_list ')'
- sf_return_type
- sf_c_chistics_and_body
- {
- Lex->stmt_create_routine_finalize();
- }
- | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
- {
- if (Lex->stmt_create_udf_function($1 | $5, $3, $6,
- (Item_result) $8, $10))
- MYSQL_YYABORT;
- }
| create_or_replace USER_SYM opt_if_not_exists clear_privileges
grant_list opt_require_clause opt_resource_options opt_account_locking opt_password_expiration
{
@@ -2926,6 +2525,7 @@ create:
| create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); }
server_def
{ }
+ | create_routine
;
opt_sequence:
@@ -3265,9 +2865,6 @@ clear_privileges:
{
LEX *lex=Lex;
lex->users_list.empty();
- lex->columns.empty();
- lex->grant= lex->grant_tot_col= 0;
- lex->all_privileges= 0;
lex->first_select_lex()->db= null_clex_str;
lex->account_options.reset();
}
@@ -3278,6 +2875,15 @@ opt_aggregate:
| AGGREGATE_SYM { $$= GROUP_AGGREGATE; }
;
+
+sp_handler:
+ FUNCTION_SYM { $$= &sp_handler_function; }
+ | PROCEDURE_SYM { $$= &sp_handler_procedure; }
+ | PACKAGE_ORACLE_SYM { $$= &sp_handler_package_spec; }
+ | PACKAGE_ORACLE_SYM BODY_ORACLE_SYM { $$= &sp_handler_package_body; }
+ ;
+
+
sp_name:
ident '.' ident
{
@@ -3394,30 +3000,7 @@ sp_param_name:
sp_param_name_and_type:
sp_param_name type_with_opt_collate
{
- if (unlikely(Lex->sp_param_fill_definition($$= $1)))
- MYSQL_YYABORT;
- }
- | sp_param_name TYPE_SYM OF_SYM ident '.' ident
- {
- if (unlikely(Lex->sphead->spvar_fill_type_reference(thd,
- $$= $1, $4,
- $6)))
- MYSQL_YYABORT;
- }
- | sp_param_name TYPE_SYM OF_SYM ident '.' ident '.' ident
- {
- if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1,
- $4, $6, $8)))
- MYSQL_YYABORT;
- }
- | sp_param_name ROW_SYM TYPE_SYM OF_SYM ident
- {
- if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $5)))
- MYSQL_YYABORT;
- }
- | sp_param_name ROW_SYM TYPE_SYM OF_SYM ident '.' ident
- {
- if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $5, $7)))
+ if (unlikely(Lex->sp_param_fill_definition($$= $1, $2)))
MYSQL_YYABORT;
}
| sp_param_name ROW_SYM row_type_body
@@ -3425,6 +3008,7 @@ sp_param_name_and_type:
if (unlikely(Lex->sphead->spvar_fill_row(thd, $$= $1, $3)))
MYSQL_YYABORT;
}
+ | sp_param_name_and_type_anchored
;
/* Stored PROCEDURE parameter declaration list */
@@ -3438,13 +3022,8 @@ sp_pdparams:
| sp_pdparam
;
-sp_pdparam:
- sp_opt_inout sp_param_name_and_type { $2->mode=$1; }
- ;
-
-sp_opt_inout:
- /* Empty */ { $$= sp_variable::MODE_IN; }
- | IN_SYM { $$= sp_variable::MODE_IN; }
+sp_parameter_type:
+ IN_SYM { $$= sp_variable::MODE_IN; }
| OUT_SYM { $$= sp_variable::MODE_OUT; }
| INOUT_SYM { $$= sp_variable::MODE_INOUT; }
;
@@ -3461,6 +3040,10 @@ sp_parenthesized_pdparam_list:
}
;
+sp_parenthesized_fdparam_list:
+ '(' sp_fdparam_list ')'
+ ;
+
sp_proc_stmts:
/* Empty */ {}
| sp_proc_stmts sp_proc_stmt ';'
@@ -3471,26 +3054,6 @@ sp_proc_stmts1:
| sp_proc_stmts1 sp_proc_stmt ';'
;
-sp_decls:
- /* Empty */
- {
- $$.init();
- }
- | sp_decls sp_decl ';'
- {
- /* We check for declarations out of (standard) order this way
- because letting the grammar rules reflect it caused tricky
- shift/reduce conflicts with the wrong result. (And we get
- better error handling this way.) */
- if (unlikely(Lex->sp_declarations_join(&$$, $1, $2)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_decl:
- DECLARE_MARIADB_SYM sp_decl_body { $$= $2; }
- ;
-
optionally_qualified_column_ident:
sp_decl_ident
@@ -3513,16 +3076,13 @@ optionally_qualified_column_ident:
}
;
-row_field_name:
- ident
- {
- if (!($$= Lex->row_field_name(thd, $1)))
- MYSQL_YYABORT;
- }
- ;
row_field_definition:
row_field_name type_with_opt_collate
+ {
+ Lex->last_field->set_attributes(thd, $2, Lex->charset,
+ COLUMN_DEFINITION_ROUTINE_LOCAL);
+ }
;
row_field_definition_list:
@@ -3552,27 +3112,15 @@ sp_decl_idents_init_vars:
sp_decl_variable_list:
sp_decl_idents_init_vars
type_with_opt_collate
- sp_opt_default
- {
- if (unlikely(Lex->sp_variable_declarations_finalize(thd, $1,
- &Lex->last_field[0],
- $3)))
- MYSQL_YYABORT;
- $$.init_using_vars($1);
- }
- | sp_decl_idents_init_vars
- TYPE_SYM OF_SYM optionally_qualified_column_ident
- sp_opt_default
{
- if (unlikely(Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $4, $5)))
- MYSQL_YYABORT;
- $$.init_using_vars($1);
+ Lex->last_field->set_attributes(thd, $2, Lex->charset,
+ COLUMN_DEFINITION_ROUTINE_LOCAL);
}
- | sp_decl_idents_init_vars
- ROW_SYM TYPE_SYM OF_SYM optionally_qualified_column_ident
sp_opt_default
{
- if (unlikely(Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $5, $6)))
+ if (unlikely(Lex->sp_variable_declarations_finalize(thd, $1,
+ &Lex->last_field[0],
+ $4)))
MYSQL_YYABORT;
$$.init_using_vars($1);
}
@@ -3584,33 +3132,7 @@ sp_decl_variable_list:
MYSQL_YYABORT;
$$.init_using_vars($1);
}
- ;
-
-sp_decl_body:
- sp_decl_variable_list
- | sp_decl_ident CONDITION_SYM FOR_SYM sp_cond
- {
- if (unlikely(Lex->spcont->declare_condition(thd, &$1, $4)))
- MYSQL_YYABORT;
- $$.vars= $$.hndlrs= $$.curs= 0;
- $$.conds= 1;
- }
- | sp_decl_handler
- | sp_decl_ident CURSOR_SYM
- {
- Lex->sp_block_init(thd);
- }
- opt_parenthesized_cursor_formal_parameters
- FOR_SYM sp_cursor_stmt
- {
- sp_pcontext *param_ctx= Lex->spcont;
- if (unlikely(Lex->sp_block_finalize(thd)))
- MYSQL_YYABORT;
- if (unlikely(Lex->sp_declare_cursor(thd, &$1, $6, param_ctx, true)))
- MYSQL_YYABORT;
- $$.vars= $$.conds= $$.hndlrs= 0;
- $$.curs= 1;
- }
+ | sp_decl_variable_list_anchored
;
sp_decl_handler:
@@ -3791,18 +3313,8 @@ signal_stmt:
signal_value:
ident
{
- LEX *lex= Lex;
- sp_condition_value *cond;
-
- /* SIGNAL foo cannot be used outside of stored programs */
- if (unlikely(lex->spcont == NULL))
- my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
- cond= lex->spcont->find_declared_or_predefined_condition(thd, &$1);
- if (unlikely(cond == NULL))
- my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
- if (unlikely(cond->type != sp_condition_value::SQLSTATE))
- my_yyabort_error((ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0)));
- $$= cond;
+ if (!($$= Lex->stmt_signal_value($1)))
+ MYSQL_YYABORT;
}
| sqlstate
{ $$= $1; }
@@ -4093,48 +3605,6 @@ sp_decl_idents:
}
;
-sp_opt_default:
- /* Empty */ { $$ = NULL; }
- | DEFAULT expr { $$ = $2; }
- ;
-
-/*
- ps_proc_stmt_in_returns_clause is a statement that is allowed
- in the RETURNS clause of a stored function definition directly,
- without the BEGIN..END block.
- It should not include any syntax structures starting with '(', to avoid
- shift/reduce conflicts with the rule "field_type" and its sub-rules
- that scan an optional length, like CHAR(1) or YEAR(4).
- See MDEV-9166.
-*/
-sp_proc_stmt_in_returns_clause:
- sp_proc_stmt_return
- | sp_labeled_block
- | sp_unlabeled_block
- | sp_labeled_control
- | sp_proc_stmt_compound_ok
- ;
-
-sp_proc_stmt:
- sp_proc_stmt_in_returns_clause
- | sp_proc_stmt_statement
- | sp_proc_stmt_continue_oracle
- | sp_proc_stmt_exit_oracle
- | sp_proc_stmt_leave
- | sp_proc_stmt_iterate
- | sp_proc_stmt_goto_oracle
- | sp_proc_stmt_open
- | sp_proc_stmt_fetch
- | sp_proc_stmt_close
- ;
-
-sp_proc_stmt_compound_ok:
- sp_proc_stmt_if
- | case_stmt_specification
- | sp_unlabeled_block_not_atomic
- | sp_unlabeled_control
- ;
-
sp_proc_stmt_if:
IF_SYM
{
@@ -4145,7 +3615,7 @@ sp_proc_stmt_if:
sp_if END IF_SYM
{ Lex->sphead->do_cont_backpatch(); }
;
-
+
sp_proc_stmt_statement:
{
LEX *lex= thd->lex;
@@ -4159,7 +3629,7 @@ sp_proc_stmt_statement:
*/
lex->sphead->m_tmp_query= lip->get_tok_start();
}
- statement
+ sp_statement
{
if (Lex->sp_proc_stmt_statement_finalize(thd, yychar == YYEMPTY) ||
Lex->sphead->restore_lex(thd))
@@ -4174,20 +3644,11 @@ RETURN_ALLMODES_SYM:
;
sp_proc_stmt_return:
- RETURN_ALLMODES_SYM
+ RETURN_ALLMODES_SYM expr_lex
{
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- Lex->pop_select(); //main select
- if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, lex->spcont,
- $3, lex)) ||
- unlikely(sp->restore_lex(thd)))
+ sp_head *sp= $2->sphead;
+ if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, $2->spcont,
+ $2->get_item(), $2)))
MYSQL_YYABORT;
}
| RETURN_ORACLE_SYM
@@ -4200,19 +3661,6 @@ sp_proc_stmt_return:
}
;
-reset_lex_expr:
- {
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- Lex->pop_select(); //main select
- $$= $2;
- }
- ;
-
sp_proc_stmt_exit_oracle:
EXIT_ORACLE_SYM
{
@@ -4224,16 +3672,14 @@ sp_proc_stmt_exit_oracle:
if (unlikely(Lex->sp_exit_statement(thd, &$2, NULL)))
MYSQL_YYABORT;
}
- | EXIT_ORACLE_SYM WHEN_SYM reset_lex_expr
+ | EXIT_ORACLE_SYM WHEN_SYM expr_lex
{
- if (unlikely(Lex->sp_exit_statement(thd, $3)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (unlikely($3->sp_exit_statement(thd, $3->get_item())))
MYSQL_YYABORT;
}
- | EXIT_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
+ | EXIT_ORACLE_SYM label_ident WHEN_SYM expr_lex
{
- if (unlikely(Lex->sp_exit_statement(thd, &$2, $4)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (unlikely($4->sp_exit_statement(thd, &$2, $4->get_item())))
MYSQL_YYABORT;
}
;
@@ -4241,24 +3687,22 @@ sp_proc_stmt_exit_oracle:
sp_proc_stmt_continue_oracle:
CONTINUE_ORACLE_SYM
{
- if (unlikely(Lex->sp_continue_statement(thd, NULL)))
+ if (unlikely(Lex->sp_continue_statement(thd)))
MYSQL_YYABORT;
}
| CONTINUE_ORACLE_SYM label_ident
{
- if (unlikely(Lex->sp_continue_statement(thd, &$2, NULL)))
+ if (unlikely(Lex->sp_continue_statement(thd, &$2)))
MYSQL_YYABORT;
}
- | CONTINUE_ORACLE_SYM WHEN_SYM reset_lex_expr
+ | CONTINUE_ORACLE_SYM WHEN_SYM expr_lex
{
- if (unlikely(Lex->sp_continue_statement(thd, $3)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (unlikely($3->sp_continue_when_statement(thd)))
MYSQL_YYABORT;
}
- | CONTINUE_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
+ | CONTINUE_ORACLE_SYM label_ident WHEN_SYM expr_lex
{
- if (unlikely(Lex->sp_continue_statement(thd, &$2, $4)) ||
- unlikely(Lex->sphead->restore_lex(thd)))
+ if (unlikely($4->sp_continue_when_statement(thd, &$2)))
MYSQL_YYABORT;
}
;
@@ -4288,6 +3732,29 @@ sp_proc_stmt_goto_oracle:
}
;
+
+expr_lex:
+ {
+ DBUG_ASSERT(Lex->sphead);
+ if (unlikely(!($<expr_lex>$= new (thd->mem_root)
+ sp_expr_lex(thd, thd->lex))))
+ MYSQL_YYABORT;
+ Lex->sphead->reset_lex(thd, $<expr_lex>$);
+ if (Lex->main_select_push(true))
+ MYSQL_YYABORT;
+ }
+ expr
+ {
+ $$= $<expr_lex>1;
+ $$->sp_lex_in_use= true;
+ $$->set_item($2);
+ Lex->pop_select(); //min select
+ if ($$->sphead->restore_lex(thd))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
assignment_source_lex:
{
DBUG_ASSERT(Lex->sphead);
@@ -4358,6 +3825,12 @@ opt_parenthesized_cursor_actual_parameters:
| '(' cursor_actual_parameters ')' { $$= $2; }
;
+sp_proc_stmt_with_cursor:
+ sp_proc_stmt_open
+ | sp_proc_stmt_fetch
+ | sp_proc_stmt_close
+ ;
+
sp_proc_stmt_open:
OPEN_SYM ident opt_parenthesized_cursor_actual_parameters
{
@@ -4447,40 +3920,16 @@ sp_fetch_list:
;
sp_if:
+ expr_lex THEN_SYM
{
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
+ if (unlikely($1->sp_if_expr(thd)))
MYSQL_YYABORT;
}
- expr THEN_SYM
+ sp_if_then_statements
{
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i= new (thd->mem_root)
- sp_instr_jump_if_not(ip, ctx, $2, lex);
- if (unlikely(i == NULL) ||
- unlikely(sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0))) ||
- unlikely(sp->add_cont_backpatch(i)) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (unlikely(sp->restore_lex(thd)))
+ if (unlikely($1->sp_if_after_statements(thd)))
MYSQL_YYABORT;
}
- sp_proc_stmts1
- {
- sp_head *sp= Lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
- if (unlikely(i == NULL) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- sp->backpatch(ctx->pop_label());
- sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0));
- }
sp_elseifs
{
LEX *lex= Lex;
@@ -4492,7 +3941,8 @@ sp_if:
sp_elseifs:
/* Empty */
| ELSEIF_MARIADB_SYM sp_if
- | ELSE sp_proc_stmts1
+ | ELSIF_ORACLE_SYM sp_if
+ | ELSE sp_if_then_statements
;
case_stmt_specification:
@@ -4566,18 +4016,9 @@ case_stmt_specification:
;
case_stmt_body:
+ expr_lex
{
- Lex->sphead->reset_lex(thd); /* For expr $2 */
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- if (unlikely(Lex->case_stmt_action_expr($2)))
- MYSQL_YYABORT;
-
- Lex->pop_select(); //main select
- if (Lex->sphead->restore_lex(thd))
+ if (unlikely($1->case_stmt_action_expr()))
MYSQL_YYABORT;
}
simple_when_clause_list
@@ -4597,26 +4038,14 @@ searched_when_clause_list:
;
simple_when_clause:
- WHEN_SYM
- {
- Lex->sphead->reset_lex(thd); /* For expr $3 */
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
+ WHEN_SYM expr_lex
{
/* Simple case: <caseval> = <whenval> */
-
- LEX *lex= Lex;
- if (unlikely(lex->case_stmt_action_when($3, true)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- /* For expr $3 */
- if (unlikely(lex->sphead->restore_lex(thd)))
+ if (unlikely($2->case_stmt_action_when(true)))
MYSQL_YYABORT;
}
THEN_SYM
- sp_proc_stmts1
+ sp_case_then_statements
{
if (unlikely(Lex->case_stmt_action_then()))
MYSQL_YYABORT;
@@ -4624,24 +4053,13 @@ simple_when_clause:
;
searched_when_clause:
- WHEN_SYM
+ WHEN_SYM expr_lex
{
- Lex->sphead->reset_lex(thd); /* For expr $3 */
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- LEX *lex= Lex;
- if (unlikely(lex->case_stmt_action_when($3, false)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- /* For expr $3 */
- if (unlikely(lex->sphead->restore_lex(thd)))
+ if (unlikely($2->case_stmt_action_when(false)))
MYSQL_YYABORT;
}
THEN_SYM
- sp_proc_stmts1
+ sp_case_then_statements
{
if (unlikely(Lex->case_stmt_action_then()))
MYSQL_YYABORT;
@@ -4660,11 +4078,7 @@ else_clause_opt:
unlikely(sp->add_instr(i)))
MYSQL_YYABORT;
}
- | ELSE sp_proc_stmts1
- ;
-
-sp_label:
- label_ident ':' { $$= $1; }
+ | ELSE sp_case_then_statements
;
sp_opt_label:
@@ -4672,61 +4086,6 @@ sp_opt_label:
| label_ident { $$= $1; }
;
-sp_block_label:
- sp_label
- {
- if (unlikely(Lex->spcont->block_label_declare(&$1)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-sp_labeled_block:
- sp_block_label
- BEGIN_MARIADB_SYM
- {
- Lex->sp_block_init(thd, &$1);
- }
- sp_decls
- sp_proc_stmts
- END
- sp_opt_label
- {
- if (unlikely(Lex->sp_block_finalize(thd, $4, &$7)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_unlabeled_block:
- BEGIN_MARIADB_SYM
- {
- Lex->sp_block_init(thd);
- }
- sp_decls
- sp_proc_stmts
- END
- {
- if (unlikely(Lex->sp_block_finalize(thd, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_unlabeled_block_not_atomic:
- BEGIN_MARIADB_SYM not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */
- {
- if (unlikely(Lex->maybe_start_compound_statement(thd)))
- MYSQL_YYABORT;
- Lex->sp_block_init(thd);
- }
- sp_decls
- sp_proc_stmts
- END
- {
- if (unlikely(Lex->sp_block_finalize(thd, $5)))
- MYSQL_YYABORT;
- }
- ;
-
/* This adds one shift/reduce conflict */
opt_sp_for_loop_direction:
/* Empty */ { $$= 1; }
@@ -4734,15 +4093,9 @@ opt_sp_for_loop_direction:
;
sp_for_loop_index_and_bounds:
- ident
+ ident_for_loop_index sp_for_loop_bounds
{
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- sp_for_loop_bounds
- {
- Lex->pop_select(); //main select
- if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $3)))
+ if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2)))
MYSQL_YYABORT;
}
;
@@ -4782,47 +4135,11 @@ loop_body:
}
;
-while_body:
- expr DO_SYM
- {
- LEX *lex= Lex;
- if (unlikely(lex->sp_while_loop_expression(thd, $1)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- if (lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- sp_proc_stmts1 END WHILE_SYM
- {
- if (unlikely(Lex->sp_while_loop_finalize(thd)))
- MYSQL_YYABORT;
- }
- ;
-
repeat_body:
- sp_proc_stmts1 UNTIL_SYM
- {
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr END REPEAT_SYM
+ sp_proc_stmts1 UNTIL_SYM expr_lex END REPEAT_SYM
{
- LEX *lex= Lex;
- uint ip= lex->sphead->instructions();
- sp_label *lab= lex->spcont->last_label(); /* Jumping back */
- sp_instr_jump_if_not *i= new (thd->mem_root)
- sp_instr_jump_if_not(ip, lex->spcont, $4, lab->ip, lex);
- if (unlikely(i == NULL) ||
- unlikely(lex->sphead->add_instr(i)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (lex->sphead->restore_lex(thd))
+ if ($3->sp_repeat_loop_finalize(thd))
MYSQL_YYABORT;
- /* We can shortcut the cont_backpatch here */
- i->m_cont_dest= ip+1;
}
;
@@ -4835,24 +4152,21 @@ pop_sp_loop_label:
;
sp_labeled_control:
- sp_label LOOP_SYM
+ sp_control_label LOOP_SYM
{
if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
}
loop_body pop_sp_loop_label
{ }
- | sp_label WHILE_SYM
+ | sp_control_label WHILE_SYM
{
if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
}
while_body pop_sp_loop_label
{ }
- | sp_label FOR_SYM
+ | sp_control_label FOR_SYM
{
// See "The FOR LOOP statement" comments in sql_lex.cc
Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block
@@ -4864,9 +4178,7 @@ sp_labeled_control:
if (unlikely(Lex->sp_for_loop_condition_test(thd, $4)))
MYSQL_YYABORT;
}
- DO_SYM
- sp_proc_stmts1
- END FOR_SYM
+ for_loop_statements
{
if (unlikely(Lex->sp_for_loop_finalize(thd, $4)))
MYSQL_YYABORT;
@@ -4876,7 +4188,7 @@ sp_labeled_control:
if (unlikely(Lex->sp_for_loop_outer_block_finalize(thd, $4)))
MYSQL_YYABORT;
}
- | sp_label REPEAT_SYM
+ | sp_control_label REPEAT_SYM
{
if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
MYSQL_YYABORT;
@@ -4899,9 +4211,6 @@ sp_unlabeled_control:
{
if (unlikely(Lex->sp_push_loop_empty_label(thd)))
MYSQL_YYABORT;
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
}
while_body
{
@@ -4921,9 +4230,7 @@ sp_unlabeled_control:
if (unlikely(Lex->sp_for_loop_condition_test(thd, $3)))
MYSQL_YYABORT;
}
- DO_SYM
- sp_proc_stmts1
- END FOR_SYM
+ for_loop_statements
{
if (unlikely(Lex->sp_for_loop_finalize(thd, $3)))
MYSQL_YYABORT;
@@ -5362,7 +4669,7 @@ opt_create_partitioning:
/*
This part of the parser is about handling of the partition information.
- It's first version was written by Mikael Ronstrm with lots of answers to
+ Its first version was written by Mikael Ronström with lots of answers to
questions provided by Antony Curtis.
The partition grammar can be called from three places.
@@ -6047,7 +5354,8 @@ opt_versioning_rotation:
| INTERVAL_SYM expr interval opt_versioning_interval_start
{
partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4)))
+ const char *table_name= Lex->create_last_non_select_table->table_name.str;
+ if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4, table_name)))
MYSQL_YYABORT;
}
| LIMIT ulonglong_num
@@ -6067,17 +5375,11 @@ opt_versioning_rotation:
opt_versioning_interval_start:
/* empty */
{
- $$= thd->query_start();
+ $$= NULL;
}
- | STARTS_SYM ulong_num
+ | STARTS_SYM literal
{
- /* only allowed from mysql_unpack_partition() */
- if (unlikely(!Lex->part_info->table))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $1.pos());
- MYSQL_YYABORT;
- }
- $$= (ulong)$2;
+ $$= $2;
}
;
@@ -6103,6 +5405,11 @@ create_database_options:
create_database_option:
default_collation {}
| default_charset {}
+ | COMMENT_SYM opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.schema_comment= thd->make_clex_string($3);
+ Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT;
+ }
;
opt_if_not_exists_table_element:
@@ -6535,8 +5842,13 @@ field_list_item:
column_def:
field_spec
{ $$= $1; }
- | field_spec references
- { $$= $1; }
+ | field_spec opt_constraint references
+ {
+ if (unlikely(Lex->add_column_foreign_key(&($1->field_name), &$2,
+ $3, DDL_options())))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
;
key_def:
@@ -6576,7 +5888,7 @@ key_def:
if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $5, $3)))
MYSQL_YYABORT;
}
- '(' key_list ')' normal_key_options { }
+ '(' key_list opt_without_overlaps ')' normal_key_options { }
| opt_constraint constraint_key_type opt_if_not_exists ident
TYPE_SYM btree_or_rtree
{
@@ -6584,7 +5896,7 @@ key_def:
if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $6, $3)))
MYSQL_YYABORT;
}
- '(' key_list ')' normal_key_options { }
+ '(' key_list opt_without_overlaps ')' normal_key_options { }
| opt_constraint FOREIGN KEY_SYM opt_if_not_exists opt_ident
{
if (unlikely(Lex->check_add_key($4)) ||
@@ -6597,29 +5909,9 @@ key_def:
}
'(' key_list ')' references
{
- LEX *lex=Lex;
- Key *key= (new (thd->mem_root)
- Foreign_key($5.str ? &$5 : &$1,
- &lex->last_key->columns,
- &$10->db,
- &$10->table,
- &lex->ref_list,
- lex->fk_delete_opt,
- lex->fk_update_opt,
- lex->fk_match_option,
- $4));
- if (unlikely(key == NULL))
- MYSQL_YYABORT;
- /*
- handle_if_exists_options() expectes the two keys in this order:
- the Foreign_key, followed by its auto-generated Key.
- */
- lex->alter_info.key_list.push_back(key, thd->mem_root);
- lex->alter_info.key_list.push_back(Lex->last_key, thd->mem_root);
- lex->option_list= NULL;
-
- /* Only used for ALTER TABLE. Ignored otherwise. */
- lex->alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
+ if (unlikely(Lex->add_table_foreign_key($5.str ? &$5 : &$1,
+ $1.str ? &$1 : &$5, $10, $4)))
+ MYSQL_YYABORT;
}
;
@@ -6715,11 +6007,15 @@ field_spec:
;
field_type_or_serial:
- qualified_field_type { Lex->last_field->set_attributes($1, Lex->charset); }
+ qualified_field_type
+ {
+ Lex->last_field->set_attributes(thd, $1, Lex->charset,
+ COLUMN_DEFINITION_TABLE_FIELD);
+ }
field_def
| SERIAL_SYM
{
- Lex->last_field->set_handler(&type_handler_longlong);
+ Lex->last_field->set_handler(&type_handler_ulonglong);
Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG
| UNSIGNED_FLAG | UNIQUE_KEY_FLAG;
}
@@ -6906,12 +6202,30 @@ field_type:
| field_type_string
| field_type_lob
| field_type_misc
+ | IDENT_sys float_options srid_option
+ {
+ if (Lex->set_field_type_udt(&$$, $1, $2))
+ MYSQL_YYABORT;
+ }
+ | reserved_keyword_udt float_options srid_option
+ {
+ if (Lex->set_field_type_udt(&$$, $1, $2))
+ MYSQL_YYABORT;
+ }
+ | non_reserved_keyword_udt float_options srid_option
+ {
+ if (Lex->set_field_type_udt(&$$, $1, $2))
+ MYSQL_YYABORT;
+ }
;
field_type_numeric:
- int_type opt_field_length field_options { $$.set($1, $2); }
- | real_type opt_precision field_options { $$.set($1, $2); }
- | FLOAT_SYM float_options field_options
+ int_type opt_field_length last_field_options
+ {
+ $$.set_handler_length_flags($1, $2, (uint32) $3);
+ }
+ | real_type opt_precision last_field_options { $$.set($1, $2); }
+ | FLOAT_SYM float_options last_field_options
{
$$.set(&type_handler_float, $2);
if ($2.length() && !$2.dec())
@@ -6927,30 +6241,30 @@ field_type_numeric:
$$.set(&type_handler_float);
}
}
- | BIT_SYM opt_field_length_default_1
+ | BIT_SYM opt_field_length
{
$$.set(&type_handler_bit, $2);
}
| BOOL_SYM
{
- $$.set(&type_handler_tiny, "1");
+ $$.set(&type_handler_stiny, "1");
}
| BOOLEAN_SYM
{
- $$.set(&type_handler_tiny, "1");
+ $$.set(&type_handler_stiny, "1");
}
- | DECIMAL_SYM float_options field_options
+ | DECIMAL_SYM float_options last_field_options
{ $$.set(&type_handler_newdecimal, $2);}
- | NUMBER_ORACLE_SYM float_options field_options
+ | NUMBER_ORACLE_SYM float_options last_field_options
{
if ($2.length() != 0)
$$.set(&type_handler_newdecimal, $2);
else
$$.set(&type_handler_double);
}
- | NUMERIC_SYM float_options field_options
+ | NUMERIC_SYM float_options last_field_options
{ $$.set(&type_handler_newdecimal, $2);}
- | FIXED_SYM float_options field_options
+ | FIXED_SYM float_options last_field_options
{ $$.set(&type_handler_newdecimal, $2);}
;
@@ -6963,39 +6277,39 @@ opt_binary_and_compression:
;
field_type_string:
- char opt_field_length_default_1 opt_binary
+ char opt_field_length opt_binary
{
$$.set(&type_handler_string, $2);
}
- | nchar opt_field_length_default_1 opt_bin_mod
+ | nchar opt_field_length opt_bin_mod
{
$$.set(&type_handler_string, $2);
bincmp_collation(national_charset_info, $3);
}
- | BINARY opt_field_length_default_1
+ | BINARY opt_field_length
{
Lex->charset=&my_charset_bin;
$$.set(&type_handler_string, $2);
}
- | varchar field_length opt_binary_and_compression
+ | varchar opt_field_length opt_binary_and_compression
{
$$.set(&type_handler_varchar, $2);
}
- | VARCHAR2_ORACLE_SYM field_length opt_binary_and_compression
+ | VARCHAR2_ORACLE_SYM opt_field_length opt_binary_and_compression
{
$$.set(&type_handler_varchar, $2);
}
- | nvarchar field_length opt_compressed opt_bin_mod
+ | nvarchar opt_field_length opt_compressed opt_bin_mod
{
$$.set(&type_handler_varchar, $2);
bincmp_collation(national_charset_info, $4);
}
- | VARBINARY field_length opt_compressed
+ | VARBINARY opt_field_length opt_compressed
{
Lex->charset=&my_charset_bin;
$$.set(&type_handler_varchar, $2);
}
- | RAW_ORACLE_SYM field_length opt_compressed
+ | RAW_ORACLE_SYM opt_field_length opt_compressed
{
Lex->charset= &my_charset_bin;
$$.set(&type_handler_varchar, $2);
@@ -7003,7 +6317,7 @@ field_type_string:
;
field_type_temporal:
- YEAR_SYM opt_field_length field_options
+ YEAR_SYM opt_field_length last_field_options
{
if ($2)
{
@@ -7064,17 +6378,6 @@ field_type_lob:
Lex->charset=&my_charset_bin;
$$.set(&type_handler_long_blob);
}
- | spatial_type float_options srid_option
- {
-#ifdef HAVE_SPATIAL
- Lex->charset=&my_charset_bin;
- Lex->last_field->geom_type= $1;
- $$.set(&type_handler_geometry, $2);
-#else
- my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name,
- sym_group_geom.needed_define));
-#endif
- }
| MEDIUMBLOB opt_compressed
{
Lex->charset=&my_charset_bin;
@@ -7118,17 +6421,6 @@ field_type_misc:
{ $$.set(&type_handler_set); }
;
-spatial_type:
- GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; }
- | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; }
- | POINT_SYM { $$= Field::GEOM_POINT; }
- | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; }
- | LINESTRING { $$= Field::GEOM_LINESTRING; }
- | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; }
- | POLYGON { $$= Field::GEOM_POLYGON; }
- | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; }
- ;
-
char:
CHAR_SYM {}
;
@@ -7152,11 +6444,11 @@ nvarchar:
;
int_type:
- INT_SYM { $$= &type_handler_long; }
- | TINYINT { $$= &type_handler_tiny; }
- | SMALLINT { $$= &type_handler_short; }
- | MEDIUMINT { $$= &type_handler_int24; }
- | BIGINT { $$= &type_handler_longlong; }
+ INT_SYM { $$= &type_handler_slong; }
+ | TINYINT { $$= &type_handler_stiny; }
+ | SMALLINT { $$= &type_handler_sshort; }
+ | MEDIUMINT { $$= &type_handler_sint24; }
+ | BIGINT { $$= &type_handler_slonglong; }
;
real_type:
@@ -7191,12 +6483,16 @@ precision:
;
field_options:
- /* empty */ {}
- | SIGNED_SYM {}
- | UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG;}
- | ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
- | UNSIGNED ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
- | ZEROFILL UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+ /* empty */ { $$= 0; }
+ | SIGNED_SYM { $$= 0; }
+ | UNSIGNED { $$= UNSIGNED_FLAG; }
+ | ZEROFILL { $$= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+ | UNSIGNED ZEROFILL { $$= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+ | ZEROFILL UNSIGNED { $$= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+ ;
+
+last_field_options:
+ field_options { Lex->last_field->flags|= ($$= $1); }
;
field_length:
@@ -7211,11 +6507,6 @@ opt_field_length:
| field_length { $$= $1; }
;
-opt_field_length_default_1:
- /* empty */ { $$= (char*) "1"; }
- | field_length { $$= $1; }
- ;
-
opt_precision:
/* empty */ { $$.set(0, 0); }
| precision { $$= $1; }
@@ -7373,13 +6664,12 @@ type_with_opt_collate:
if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))))
MYSQL_YYABORT;
}
- Lex->last_field->set_attributes($$, Lex->charset);
}
;
charset:
- CHAR_SYM SET {}
- | CHARSET {}
+ CHAR_SYM SET { $$= $1; }
+ | CHARSET { $$= $1; }
;
charset_name:
@@ -7651,11 +6941,6 @@ keys_or_index:
| INDEXES {}
;
-opt_unique:
- /* empty */ { $$= Key::MULTIPLE; }
- | UNIQUE_SYM { $$= Key::UNIQUE; }
- ;
-
fulltext:
FULLTEXT_SYM { $$= Key::FULLTEXT;}
;
@@ -7729,6 +7014,10 @@ all_key_opt:
}
| COMMENT_SYM TEXT_STRING_sys
{ Lex->last_key->key_create_info.comment= $2; }
+ | VISIBLE_SYM
+ {
+ /* This is mainly for MySQL 8.0 compatiblity */
+ }
| IDENT_sys equal TEXT_STRING_sys
{
if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
@@ -7796,6 +7085,15 @@ key_list:
}
;
+opt_without_overlaps:
+ /* nothing */ {}
+ | ',' ident WITHOUT OVERLAPS_SYM
+ {
+ Lex->last_key->without_overlaps= true;
+ Lex->last_key->period= $2;
+ }
+ ;
+
key_part:
ident
{
@@ -7847,10 +7145,11 @@ alter:
MYSQL_YYABORT;
DBUG_ASSERT(!Lex->m_sql_cmd);
}
- alter_options TABLE_SYM table_ident opt_lock_wait_timeout
+ alter_options TABLE_SYM opt_if_exists table_ident opt_lock_wait_timeout
{
+ Lex->create_info.set($5);
if (!Lex->first_select_lex()->
- add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING,
+ add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
TL_READ_NO_INSERT, MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
Lex->first_select_lex()->db=
@@ -7872,6 +7171,7 @@ alter:
| ALTER DATABASE ident_or_empty
{
Lex->create_info.default_table_charset= NULL;
+ Lex->create_info.schema_comment= NULL;
Lex->create_info.used_fields= 0;
if (Lex->main_select_push(true))
MYSQL_YYABORT;
@@ -7886,6 +7186,22 @@ alter:
MYSQL_YYABORT;
Lex->pop_select(); //main select
}
+ | ALTER DATABASE COMMENT_SYM opt_equal TEXT_STRING_sys
+ {
+ Lex->create_info.default_table_charset= NULL;
+ Lex->create_info.used_fields= 0;
+ Lex->create_info.schema_comment= thd->make_clex_string($5);
+ Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT;
+ }
+ opt_create_database_options
+ {
+ LEX *lex=Lex;
+ lex->sql_command=SQLCOM_ALTER_DB;
+ lex->name= Lex_ident_sys();
+ if (lex->name.str == NULL &&
+ unlikely(lex->copy_db_to(&lex->name)))
+ MYSQL_YYABORT;
+ }
| ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM
{
LEX *lex= Lex;
@@ -7896,44 +7212,18 @@ alter:
}
| ALTER PROCEDURE_SYM sp_name
{
- LEX *lex= Lex;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
- if (Lex->main_select_push())
+ if (Lex->stmt_alter_procedure_start($3))
MYSQL_YYABORT;
- lex->sp_chistics.init();
}
sp_a_chistics
- {
- LEX *lex=Lex;
-
- lex->sql_command= SQLCOM_ALTER_PROCEDURE;
- lex->spname= $3;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ stmt_end {}
| ALTER FUNCTION_SYM sp_name
{
- LEX *lex= Lex;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- if (Lex->main_select_push())
+ if (Lex->stmt_alter_function_start($3))
MYSQL_YYABORT;
- lex->sp_chistics.init();
}
sp_a_chistics
- {
- LEX *lex=Lex;
-
- lex->sql_command= SQLCOM_ALTER_FUNCTION;
- lex->spname= $3;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ stmt_end {}
| ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident
{
if (Lex->main_select_push())
@@ -7941,12 +7231,7 @@ alter:
if (Lex->add_alter_view(thd, $2, $4, $6))
MYSQL_YYABORT;
}
- view_list_opt AS view_select
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ view_list_opt AS view_select stmt_end {}
| ALTER definer_opt opt_view_suid VIEW_SYM table_ident
/*
We have two separate rules for ALTER VIEW rather that
@@ -7959,12 +7244,7 @@ alter:
if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5))
MYSQL_YYABORT;
}
- view_list_opt AS view_select
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ view_list_opt AS view_select stmt_end {}
| ALTER definer_opt remember_name EVENT_SYM sp_name
{
if (Lex->main_select_push())
@@ -8065,10 +7345,7 @@ alter:
Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3);
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ } stmt_end {}
;
opt_account_locking:
@@ -8130,7 +7407,8 @@ opt_ev_sql_stmt:
;
ident_or_empty:
- /* empty */ { $$= Lex_ident_sys(); }
+ /* empty */
+ %prec PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE { $$= Lex_ident_sys(); }
| ident
;
@@ -8480,13 +7758,12 @@ alter_list_item:
{
if (check_expression($7, &$4, VCOL_DEFAULT))
MYSQL_YYABORT;
- if (unlikely(Lex->add_alter_list($4.str, $7, $3)))
+ if (unlikely(Lex->add_alter_list($4, $7, $3)))
MYSQL_YYABORT;
}
| ALTER opt_column opt_if_exists_table_element field_ident DROP DEFAULT
{
- if (unlikely(Lex->add_alter_list($4.str, (Virtual_column_info*) 0,
- $3)))
+ if (unlikely(Lex->add_alter_list($4, (Virtual_column_info*) 0, $3)))
MYSQL_YYABORT;
}
| RENAME opt_to table_ident
@@ -8503,6 +7780,21 @@ alter_list_item:
lex->name= $3->table;
lex->alter_info.flags|= ALTER_RENAME;
}
+ | RENAME COLUMN_SYM opt_if_exists_table_element ident TO_SYM ident
+ {
+ if (unlikely(Lex->add_alter_list($4, $6, $3)))
+ MYSQL_YYABORT;
+ }
+ | RENAME key_or_index opt_if_exists_table_element field_ident TO_SYM field_ident
+ {
+ LEX *lex=Lex;
+ Alter_rename_key *ak= new (thd->mem_root)
+ Alter_rename_key($4, $6, $3);
+ if (ak == NULL)
+ MYSQL_YYABORT;
+ lex->alter_info.alter_rename_key_list.push_back(ak);
+ lex->alter_info.flags|= ALTER_RENAME_INDEX;
+ }
| CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
{
if (!$4)
@@ -9056,9 +8348,10 @@ opt_no_write_to_binlog:
;
rename:
- RENAME table_or_tables
+ RENAME table_or_tables opt_if_exists
{
Lex->sql_command= SQLCOM_RENAME_TABLE;
+ Lex->create_info.set($3);
if (Lex->main_select_push())
MYSQL_YYABORT;
}
@@ -9328,8 +8621,7 @@ query_specification_start:
{
SELECT_LEX *sel;
LEX *lex= Lex;
- if (!(sel= lex->alloc_select(TRUE)) ||
- lex->push_select(sel))
+ if (!(sel= lex->alloc_select(TRUE)) || lex->push_select(sel))
MYSQL_YYABORT;
sel->init_select();
sel->braces= FALSE;
@@ -9519,8 +8811,7 @@ query_expression_body:
{
if (!($$= Lex->add_primary_to_query_expression_body($1, $4,
$2.unit_type,
- $2.distinct,
- FALSE)))
+ $2.distinct)))
MYSQL_YYABORT;
}
| query_expression_body_ext_parens
@@ -9570,7 +8861,7 @@ subselect:
Consider the production rule of the SQL Standard
subquery:
- '(' query_expression')'
+ '(' query_expression ')'
This rule is equivalent to the rule
subquery:
@@ -9584,7 +8875,7 @@ subselect:
The latter can be re-written into
subquery:
- query_expression_body_ext_parens ')'
+ query_expression_body_ext_parens
| '(' with_clause query_expression_no_with_clause ')'
The last rule allows us to resolve properly the shift/reduce conflict
@@ -9652,7 +8943,7 @@ select_options:
opt_history_unit:
/* empty*/ %prec PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE
{
- $$= VERS_UNDEFINED;
+ $$= VERS_TIMESTAMP;
}
| TRANSACTION_SYM
{
@@ -9821,7 +9112,7 @@ select_item_list:
{
Item *item= new (thd->mem_root)
Item_field(thd, &thd->lex->current_select->context,
- NULL, NULL, &star_clex_str);
+ star_clex_str);
if (unlikely(item == NULL))
MYSQL_YYABORT;
if (unlikely(add_item_to_list(thd, item)))
@@ -9847,8 +9138,8 @@ select_item:
if (unlikely(Lex->sql_command == SQLCOM_CREATE_VIEW &&
check_column_name($4.str)))
my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), $4.str));
- $2->is_autogenerated_name= FALSE;
- $2->set_name(thd, $4.str, $4.length, system_charset_info);
+ $2->common_flags&= ~IS_AUTO_GENERATED_NAME;
+ $2->set_name(thd, $4);
}
else if (!$2->name.str || $2->name.str == item_empty_name)
{
@@ -10539,7 +9830,8 @@ column_default_non_parenthesized_expr:
}
| CAST_SYM '(' expr AS cast_type ')'
{
- if (unlikely(!($$= $5.create_typecast_item(thd, $3, Lex->charset))))
+ if (unlikely(!($$= $5.create_typecast_item_or_error(thd, $3,
+ Lex->charset))))
MYSQL_YYABORT;
}
| CASE_SYM when_list_opt_else END
@@ -10555,7 +9847,8 @@ column_default_non_parenthesized_expr:
}
| CONVERT_SYM '(' expr ',' cast_type ')'
{
- if (unlikely(!($$= $5.create_typecast_item(thd, $3, Lex->charset))))
+ if (unlikely(!($$= $5.create_typecast_item_or_error(thd, $3,
+ Lex->charset))))
MYSQL_YYABORT;
}
| CONVERT_SYM '(' expr USING charset_name ')'
@@ -11268,78 +10561,6 @@ function_call_conflict:
if (unlikely($$ == NULL))
MYSQL_YYABORT;
}
- | geometry_function
- {
-#ifdef HAVE_SPATIAL
- $$= $1;
- /* $1 may be NULL, GEOM_NEW not tested for out of memory */
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
-#else
- my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name,
- sym_group_geom.needed_define));
-#endif
- }
- ;
-
-geometry_function:
- CONTAINS_SYM '(' expr ',' expr ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_precise_rel(thd, $3, $5,
- Item_func::SP_CONTAINS_FUNC));
- }
- | GEOMETRYCOLLECTION '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_geometrycollection,
- Geometry::wkb_point));
- }
- | LINESTRING '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_linestring,
- Geometry::wkb_point));
- }
- | MULTILINESTRING '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_multilinestring,
- Geometry::wkb_linestring));
- }
- | MULTIPOINT '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_multipoint,
- Geometry::wkb_point));
- }
- | MULTIPOLYGON '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_multipolygon,
- Geometry::wkb_polygon));
- }
- | POINT_SYM '(' expr ',' expr ')'
- {
- $$= GEOM_NEW(thd, Item_func_point(thd, $3, $5));
- }
- | POLYGON '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_polygon,
- Geometry::wkb_linestring));
- }
- | WITHIN '(' expr ',' expr ')'
- {
- $$= GEOM_NEW(thd, Item_func_spatial_precise_rel(thd, $3, $5,
- Item_func::SP_WITHIN_FUNC));
- }
;
/*
@@ -11373,6 +10594,7 @@ function_call_generic:
}
opt_udf_expr_list ')'
{
+ const Type_handler *h;
Create_func *builder;
Item *item= NULL;
@@ -11384,15 +10606,20 @@ function_call_generic:
names are resolved with the following order:
- MySQL native functions,
- User Defined Functions,
+ - Constructors, like POINT(1,1)
- Stored Functions (assuming the current <use> database)
This will be revised with WL#2128 (SQL PATH)
*/
- builder= find_native_function_builder(thd, &$1);
- if (builder)
+ if ((builder= find_native_function_builder(thd, &$1)))
{
item= builder->create_func(thd, &$1, $4);
}
+ else if ((h= Type_handler::handler_by_name(thd, $1)) &&
+ (item= h->make_constructor_item(thd, $4)))
+ {
+ // Found a constructor with a proper argument count
+ }
else
{
#ifdef HAVE_DLOPEN
@@ -11420,6 +10647,24 @@ function_call_generic:
if (unlikely(! ($$= item)))
MYSQL_YYABORT;
}
+ | CONTAINS_SYM '(' opt_expr_list ')'
+ {
+ if (!($$= Lex->make_item_func_call_native_or_parse_error(thd,
+ $1, $3)))
+ MYSQL_YYABORT;
+ }
+ | OVERLAPS_SYM '(' opt_expr_list ')'
+ {
+ if (!($$= Lex->make_item_func_call_native_or_parse_error(thd,
+ $1, $3)))
+ MYSQL_YYABORT;
+ }
+ | WITHIN '(' opt_expr_list ')'
+ {
+ if (!($$= Lex->make_item_func_call_native_or_parse_error(thd,
+ $1, $3)))
+ MYSQL_YYABORT;
+ }
| ident_cli '.' ident_cli '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, $5))))
@@ -11475,8 +10720,8 @@ udf_expr:
*/
if ($4.str)
{
- $2->is_autogenerated_name= FALSE;
- $2->set_name(thd, $4.str, $4.length, system_charset_info);
+ $2->common_flags&= ~IS_AUTO_GENERATED_NAME;
+ $2->set_name(thd, $4);
}
/*
A field has to have its proper name in order for name
@@ -11634,6 +10879,49 @@ sum_expr:
$5->empty();
sel->gorder_list.empty();
}
+ | JSON_ARRAYAGG_SYM '(' opt_distinct
+ { Select->in_sum_expr++; }
+ expr_list opt_gorder_clause opt_glimit_clause
+ ')'
+ {
+ SELECT_LEX *sel= Select;
+ List<Item> *args= $5;
+ sel->in_sum_expr--;
+ if (args && args->elements > 1)
+ {
+ /* JSON_ARRAYAGG supports only one parameter */
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), "JSON_ARRAYAGG");
+ MYSQL_YYABORT;
+ }
+ String* s= new (thd->mem_root) String(",", 1, &my_charset_latin1);
+ if (unlikely(s == NULL))
+ MYSQL_YYABORT;
+
+ $$= new (thd->mem_root)
+ Item_func_json_arrayagg(thd, Lex->current_context(),
+ $3, args,
+ sel->gorder_list, s, $7,
+ sel->select_limit,
+ sel->offset_limit);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ sel->select_limit= NULL;
+ sel->offset_limit= NULL;
+ sel->explicit_limit= 0;
+ $5->empty();
+ sel->gorder_list.empty();
+ }
+ | JSON_OBJECTAGG_SYM '('
+ { Select->in_sum_expr++; }
+ expr ',' expr ')'
+ {
+ SELECT_LEX *sel= Select;
+ sel->in_sum_expr--;
+
+ $$= new (thd->mem_root) Item_func_json_objectagg(thd, $4, $6);
+ if (unlikely($$ == NULL))
+ MYSQL_YYABORT;
+ }
;
window_func_expr:
@@ -12022,12 +11310,27 @@ cast_type:
}
| cast_type_numeric { $$= $1; Lex->charset= NULL; }
| cast_type_temporal { $$= $1; Lex->charset= NULL; }
+ | IDENT_sys
+ {
+ if (Lex->set_cast_type_udt(&$$, $1))
+ MYSQL_YYABORT;
+ }
+ | reserved_keyword_udt
+ {
+ if (Lex->set_cast_type_udt(&$$, $1))
+ MYSQL_YYABORT;
+ }
+ | non_reserved_keyword_udt
+ {
+ if (Lex->set_cast_type_udt(&$$, $1))
+ MYSQL_YYABORT;
+ }
;
cast_type_numeric:
- INT_SYM { $$.set(&type_handler_longlong); }
- | SIGNED_SYM { $$.set(&type_handler_longlong); }
- | SIGNED_SYM INT_SYM { $$.set(&type_handler_longlong); }
+ INT_SYM { $$.set(&type_handler_slonglong); }
+ | SIGNED_SYM { $$.set(&type_handler_slonglong); }
+ | SIGNED_SYM INT_SYM { $$.set(&type_handler_slonglong); }
| UNSIGNED { $$.set(&type_handler_ulonglong); }
| UNSIGNED INT_SYM { $$.set(&type_handler_ulonglong); }
| DECIMAL_SYM float_options { $$.set(&type_handler_newdecimal, $2); }
@@ -12683,7 +11986,7 @@ opt_window_clause:
{}
| WINDOW_SYM
window_def_list
- {}
+ {}
;
window_def_list:
@@ -13106,7 +12409,7 @@ int_num:
ulong_num:
opt_plus NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
- | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); }
+ | HEX_NUM { $$= strtoul($1.str, (char**) 0, 16); }
| opt_plus LONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
| opt_plus ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
| opt_plus DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
@@ -13175,7 +12478,7 @@ procedure_clause:
lex->proc_list.next= &lex->proc_list.first;
Item_field *item= new (thd->mem_root)
Item_field(thd, &lex->current_select->context,
- NULL, NULL, &$2);
+ $2);
if (unlikely(item == NULL))
MYSQL_YYABORT;
if (unlikely(add_proc_to_list(thd, item)))
@@ -13374,43 +12677,6 @@ drop:
lex->set_command(SQLCOM_DROP_DB, $3);
lex->name= $4;
}
- | DROP FUNCTION_SYM opt_if_exists ident '.' ident
- {
- LEX *lex= thd->lex;
- sp_name *spname;
- if (unlikely($4.str && check_db_name((LEX_STRING*) &$4)))
- my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $4.str));
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- lex->set_command(SQLCOM_DROP_FUNCTION, $3);
- spname= new (thd->mem_root) sp_name(&$4, &$6, true);
- if (unlikely(spname == NULL))
- MYSQL_YYABORT;
- lex->spname= spname;
- }
- | DROP FUNCTION_SYM opt_if_exists ident
- {
- LEX *lex= thd->lex;
- LEX_CSTRING db= {0, 0};
- sp_name *spname;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- if (thd->db.str && unlikely(lex->copy_db_to(&db)))
- MYSQL_YYABORT;
- lex->set_command(SQLCOM_DROP_FUNCTION, $3);
- spname= new (thd->mem_root) sp_name(&db, &$4, false);
- if (unlikely(spname == NULL))
- MYSQL_YYABORT;
- lex->spname= spname;
- }
- | DROP PROCEDURE_SYM opt_if_exists sp_name
- {
- LEX *lex=Lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
- lex->set_command(SQLCOM_DROP_PROCEDURE, $3);
- lex->spname= $4;
- }
| DROP USER_SYM opt_if_exists clear_privileges user_list
{
Lex->set_command(SQLCOM_DROP_USER, $3);
@@ -13465,6 +12731,7 @@ drop:
}
table_list
{}
+ | drop_routine
;
table_list:
@@ -13550,53 +12817,51 @@ opt_temporary:
insert:
INSERT
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSERT;
- lex->duplicates= DUP_ERROR;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->parsing_place= BEFORE_OPT_LIST;
+ Lex->sql_command= SQLCOM_INSERT;
+ Lex->duplicates= DUP_ERROR;
}
- insert_lock_option
- opt_ignore insert2
+ insert_start insert_lock_option opt_ignore opt_into insert_table
{
- Select->set_lock_for_tables($3, true);
- Lex->current_select= Lex->first_select_lex();
+ Select->set_lock_for_tables($4, true);
}
- insert_field_spec opt_insert_update
+ insert_field_spec opt_insert_update opt_returning
+ stmt_end
{
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
- ;
+ ;
replace:
REPLACE
{
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_REPLACE;
- lex->duplicates= DUP_REPLACE;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->parsing_place= BEFORE_OPT_LIST;
+ Lex->sql_command = SQLCOM_REPLACE;
+ Lex->duplicates= DUP_REPLACE;
}
- replace_lock_option insert2
+ insert_start replace_lock_option opt_into insert_table
{
- Select->set_lock_for_tables($3, true);
- Lex->current_select= Lex->first_select_lex();
+ Select->set_lock_for_tables($4, true);
}
- insert_field_spec
+ insert_field_spec opt_returning
+ stmt_end
{
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
- ;
+ ;
+
+insert_start: {
+ if (Lex->main_select_push())
+ MYSQL_YYABORT;
+ mysql_init_select(Lex);
+ Lex->current_select->parsing_place= BEFORE_OPT_LIST;
+ }
+ ;
+
+stmt_end: {
+ Lex->pop_select(); //main select
+ if (Lex->check_main_unit_semantics())
+ MYSQL_YYABORT;
+ }
+ ;
insert_lock_option:
/* empty */
@@ -13608,19 +12873,17 @@ insert_lock_option:
*/
$$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT);
}
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
- | DELAYED_SYM
- {
- // QQ: why was +1?
- Lex->keyword_delayed_begin_offset= (uint)($1.pos() - thd->query());
- Lex->keyword_delayed_end_offset= (uint)($1.end() - thd->query());
- $$= TL_WRITE_DELAYED;
- }
+ | insert_replace_option
| HIGH_PRIORITY { $$= TL_WRITE; }
;
replace_lock_option:
- opt_low_priority { $$= $1; }
+ /* empty */ { $$= TL_WRITE_DEFAULT; }
+ | insert_replace_option
+ ;
+
+insert_replace_option:
+ LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
| DELAYED_SYM
{
Lex->keyword_delayed_begin_offset= (uint)($1.pos() - thd->query());
@@ -13629,10 +12892,7 @@ replace_lock_option:
}
;
-insert2:
- INTO insert_table {}
- | insert_table {}
- ;
+opt_into: /* nothing */ | INTO ;
insert_table:
{
@@ -13881,10 +13141,7 @@ update:
{
if ($10)
Select->order_list= *($10);
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ } stmt_end {}
;
update_list:
@@ -13937,6 +13194,7 @@ delete:
lex->first_select_lex()->order_list.empty();
}
delete_part2
+ { }
;
opt_delete_system_time:
@@ -13975,19 +13233,19 @@ delete_single_table:
;
delete_single_table_for_period:
- delete_single_table opt_for_portion_of_time_clause
- {
- if ($2)
- Lex->last_table()->period_conditions= Lex->period_conditions;
- }
- ;
+ delete_single_table opt_for_portion_of_time_clause
+ {
+ if ($2)
+ Lex->last_table()->period_conditions= Lex->period_conditions;
+ }
+ ;
single_multi:
delete_single_table_for_period
opt_where_clause
opt_order_clause
delete_limit_clause
- opt_select_expressions
+ opt_returning
{
if ($3)
Select->order_list= *($3);
@@ -14003,10 +13261,7 @@ single_multi:
{
if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ } stmt_end {}
| FROM table_alias_ref_list
{
mysql_init_multi_delete(Lex);
@@ -14017,15 +13272,31 @@ single_multi:
{
if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
+ } stmt_end {}
;
-opt_select_expressions:
- /* empty */
- | RETURNING_SYM select_item_list
+opt_returning:
+ /* empty */
+ {
+ DBUG_ASSERT(!Lex->has_returning());
+ }
+ | RETURNING_SYM
+ {
+ DBUG_ASSERT(!Lex->has_returning());
+ if (($<num>$= (Select != Lex->returning())))
+ {
+ SELECT_LEX *sl= Lex->returning();
+ sl->set_master_unit(0);
+ Select->add_slave(Lex->create_unit(sl));
+ sl->include_global((st_select_lex_node**)&Lex->all_selects_list);
+ Lex->push_select(sl);
+ }
+ }
+ select_item_list
+ {
+ if ($<num>2)
+ Lex->pop_select();
+ }
;
table_wild_list:
@@ -14102,6 +13373,7 @@ truncate:
if (unlikely(lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
+ opt_truncate_table_storage_clause { }
;
opt_table_sym:
@@ -14418,26 +13690,36 @@ show_param:
MYSQL_YYABORT;
lex->table_type= TABLE_TYPE_SEQUENCE;
}
+ | BINLOG_SYM STATUS_SYM
+ {
+ Lex->sql_command = SQLCOM_SHOW_BINLOG_STAT;
+ }
| MASTER_SYM STATUS_SYM
{
- Lex->sql_command = SQLCOM_SHOW_MASTER_STAT;
+ Lex->sql_command = SQLCOM_SHOW_BINLOG_STAT;
}
| ALL SLAVES STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status(true)))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
+ if (!(lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
}
| SLAVE connection_name STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
}
| CREATE PROCEDURE_SYM sp_name
{
@@ -14459,12 +13741,24 @@ show_param:
lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
lex->spname= $3;
}
+ | CREATE PACKAGE_ORACLE_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
+ lex->spname= $3;
+ }
| CREATE PACKAGE_MARIADB_SYM BODY_MARIADB_SYM sp_name
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
lex->spname= $4;
}
+ | CREATE PACKAGE_ORACLE_SYM BODY_ORACLE_SYM sp_name
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
+ lex->spname= $4;
+ }
| CREATE TRIGGER_SYM sp_name
{
LEX *lex= Lex;
@@ -14505,6 +13799,13 @@ show_param:
if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
MYSQL_YYABORT;
}
+ | PACKAGE_ORACLE_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE;
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
+ MYSQL_YYABORT;
+ }
| PACKAGE_MARIADB_SYM BODY_MARIADB_SYM STATUS_SYM wild_and_where
{
LEX *lex= Lex;
@@ -14512,6 +13813,13 @@ show_param:
if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
MYSQL_YYABORT;
}
+ | PACKAGE_ORACLE_SYM BODY_ORACLE_SYM STATUS_SYM wild_and_where
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE_BODY;
+ if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
+ MYSQL_YYABORT;
+ }
| PROCEDURE_SYM CODE_SYM sp_name
{
Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
@@ -14527,6 +13835,11 @@ show_param:
Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
Lex->spname= $4;
}
+ | PACKAGE_ORACLE_SYM BODY_ORACLE_SYM CODE_SYM sp_name
+ {
+ Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
+ Lex->spname= $4;
+ }
| CREATE EVENT_SYM sp_name
{
Lex->spname= $3;
@@ -14824,6 +14137,8 @@ flush_option:
{ Lex->type|= REFRESH_USER_RESOURCES; }
| SSL_SYM
{ Lex->type|= REFRESH_SSL;}
+ | THREADS_SYM
+ { Lex->type|= REFRESH_THREADS;}
| IDENT_sys remember_tok_start
{
Lex->type|= REFRESH_GENERIC;
@@ -15092,10 +14407,8 @@ load:
opt_xml_rows_identified_by
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec
+ stmt_end
{
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
Lex->mark_first_table_as_inserting();
}
;
@@ -15403,13 +14716,15 @@ literal:
| UNDERSCORE_CHARSET hex_or_bin_String
{
Item_string_with_introducer *item_str;
+ LEX_CSTRING tmp;
+ $2->get_value(&tmp);
/*
Pass NULL as name. Name will be set in the "select_item" rule and
will include the introducer and the original hex/bin notation.
*/
item_str= new (thd->mem_root)
- Item_string_with_introducer(thd, NULL, $2->ptr(), $2->length(),
- $1);
+ Item_string_with_introducer(thd, null_clex_str,
+ tmp, $1);
if (unlikely(!item_str ||
!item_str->check_well_formed_result(true)))
MYSQL_YYABORT;
@@ -15523,46 +14838,78 @@ with_list:
with_list_element:
query_name
opt_with_column_list
- {
- $2= new List<LEX_CSTRING> (Lex->with_column_list);
- if (unlikely($2 == NULL))
- MYSQL_YYABORT;
- Lex->with_column_list.empty();
- }
- AS '(' query_expression ')'
+ AS '(' query_expression ')' opt_cycle
{
LEX *lex= thd->lex;
const char *query_start= lex->sphead ? lex->sphead->m_tmp_query
: thd->query();
- const char *spec_start= $5.pos() + 1;
- With_element *elem= new With_element($1, *$2, $6);
+ const char *spec_start= $4.pos() + 1;
+ With_element *elem= new With_element($1, *$2, $5);
if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
MYSQL_YYABORT;
- if (elem->set_unparsed_spec(thd, spec_start, $7.pos(),
+ if (elem->set_unparsed_spec(thd, spec_start, $6.pos(),
spec_start - query_start))
MYSQL_YYABORT;
+ if ($7)
+ {
+ elem->set_cycle_list($7);
+ }
}
;
+opt_cycle:
+ /* empty */
+ { $$= NULL; }
+ |
+ CYCLE_SYM
+ {
+ if (!Lex->curr_with_clause->with_recursive)
+ {
+ thd->parse_error(ER_SYNTAX_ERROR, $1.pos());
+ }
+ }
+ comma_separated_ident_list RESTRICT
+ {
+ $$= $3;
+ }
+ ;
+
opt_with_column_list:
/* empty */
- { $$= NULL; }
+ {
+ if (($$= new (thd->mem_root) List<Lex_ident_sys>) == NULL)
+ MYSQL_YYABORT;
+ }
| '(' with_column_list ')'
- { $$= NULL; }
+ { $$= $2; }
;
-
with_column_list:
- ident
+ comma_separated_ident_list
+ ;
+
+ident_sys_alloc:
+ ident_cli
{
- Lex->with_column_list.push_back((LEX_CSTRING*)
- thd->memdup(&$1, sizeof(LEX_CSTRING)));
+ void *buf= thd->alloc(sizeof(Lex_ident_sys));
+ if (!buf)
+ MYSQL_YYABORT;
+ $$= new (buf) Lex_ident_sys(thd, &$1);
+ }
+ ;
+
+comma_separated_ident_list:
+ ident_sys_alloc
+ {
+ $$= new (thd->mem_root) List<Lex_ident_sys>;
+ if (unlikely($$ == NULL || $$->push_back($1)))
+ MYSQL_YYABORT;
}
- | with_column_list ',' ident
+ | comma_separated_ident_list ',' ident_sys_alloc
{
- Lex->with_column_list.push_back((LEX_CSTRING*)
- thd->memdup(&$3, sizeof(LEX_CSTRING)));
+ if (($$= $1)->push_back($3))
+ MYSQL_YYABORT;
}
;
@@ -15799,13 +15146,9 @@ ident_table_alias:
}
;
-ident_set_usual_case:
- IDENT_sys
- | keyword_set_usual_case
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
+ident_cli_set_usual_case:
+ IDENT_cli { $$= $1; }
+ | keyword_set_usual_case { $$= $1; }
;
ident_sysvar_name:
@@ -15910,6 +15253,7 @@ user: user_maybe_role
/* Keywords which we allow as table aliases. */
keyword_table_alias:
keyword_data_type
+ | keyword_cast_type
| keyword_set_special_case
| keyword_sp_block_section
| keyword_sp_head
@@ -15917,11 +15261,14 @@ keyword_table_alias:
| keyword_sp_var_not_label
| keyword_sysvar_type
| keyword_verb_clause
+ | FUNCTION_SYM
+ | EXCEPTION_ORACLE_SYM
;
/* Keyword that we allow for identifiers (except SP labels) */
keyword_ident:
keyword_data_type
+ | keyword_cast_type
| keyword_set_special_case
| keyword_sp_block_section
| keyword_sp_head
@@ -15929,52 +15276,47 @@ keyword_ident:
| keyword_sp_var_not_label
| keyword_sysvar_type
| keyword_verb_clause
+ | FUNCTION_SYM
| WINDOW_SYM
- ;
-
-/*
- Keywords that we allow for labels in SPs.
- Should not include keywords that start a statement or SP characteristics.
-*/
-keyword_label:
- keyword_data_type
- | keyword_set_special_case
- | keyword_sp_var_and_label
- | keyword_sysvar_type
+ | EXCEPTION_ORACLE_SYM
;
keyword_sysvar_name:
keyword_data_type
+ | keyword_cast_type
| keyword_set_special_case
| keyword_sp_block_section
| keyword_sp_head
| keyword_sp_var_and_label
| keyword_sp_var_not_label
| keyword_verb_clause
+ | FUNCTION_SYM
| WINDOW_SYM
+ | EXCEPTION_ORACLE_SYM
;
-keyword_sp_decl:
+keyword_set_usual_case:
keyword_data_type
- | keyword_set_special_case
+ | keyword_cast_type
| keyword_sp_block_section
| keyword_sp_head
| keyword_sp_var_and_label
| keyword_sp_var_not_label
| keyword_sysvar_type
| keyword_verb_clause
+ | FUNCTION_SYM
| WINDOW_SYM
+ | EXCEPTION_ORACLE_SYM
;
-keyword_set_usual_case:
- keyword_data_type
- | keyword_sp_block_section
+non_reserved_keyword_udt:
+ keyword_sp_var_not_label
| keyword_sp_head
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_sysvar_type
| keyword_verb_clause
- | WINDOW_SYM
+ | keyword_set_special_case
+ | keyword_sp_block_section
+ | keyword_sysvar_type
+ | keyword_sp_var_and_label
;
/*
@@ -16026,7 +15368,6 @@ keyword_sp_var_not_label:
| RESTORE_SYM
| SECURITY_SYM
| SERVER_SYM
- | SIGNED_SYM
| SOCKET_SYM
| SLAVE
| SLAVES
@@ -16116,15 +15457,6 @@ keyword_set_special_case:
| PASSWORD_SYM
;
-/*
- Keywords that start an SP block section.
-*/
-keyword_sp_block_section:
- BEGIN_MARIADB_SYM
- | EXCEPTION_ORACLE_SYM
- | END
- ;
-
keyword_sysvar_type:
GLOBAL_SYM
| LOCAL_SYM
@@ -16146,21 +15478,13 @@ keyword_data_type:
| DATETIME
| ENUM
| FIXED_SYM
- | GEOMETRYCOLLECTION
- | GEOMETRY_SYM
| JSON_SYM
- | LINESTRING
| MEDIUM_SYM
- | MULTILINESTRING
- | MULTIPOINT
- | MULTIPOLYGON
| NATIONAL_SYM
| NCHAR_SYM
| NUMBER_MARIADB_SYM
| NUMBER_ORACLE_SYM
| NVARCHAR_SYM
- | POINT_SYM
- | POLYGON
| RAW_MARIADB_SYM
| RAW_ORACLE_SYM
| ROW_SYM
@@ -16174,6 +15498,11 @@ keyword_data_type:
;
+keyword_cast_type:
+ SIGNED_SYM
+ ;
+
+
/*
These keywords are fine for both SP variable names and SP labels.
*/
@@ -16271,6 +15600,7 @@ keyword_sp_var_and_label:
| FAST_SYM
| FOUND_SYM
| ENABLE_SYM
+ | FEDERATED_SYM
| FULL
| FILE_SYM
| FIRST_SYM
@@ -16349,6 +15679,7 @@ keyword_sp_var_and_label:
| MIN_ROWS
| MODIFY_SYM
| MODE_SYM
+ | MONITOR_SYM
| MONTH_SYM
| MUTEX_SYM
| MYSQL_SYM
@@ -16373,6 +15704,7 @@ keyword_sp_var_and_label:
| ONE_SYM
| ONLINE_SYM
| ONLY_SYM
+ | OVERLAPS_SYM
| PACKAGE_MARIADB_SYM
| PACK_KEYS_SYM
| PAGE_SYM
@@ -16410,6 +15742,7 @@ keyword_sp_var_and_label:
| RELOAD
| REORGANIZE_SYM
| REPEATABLE_SYM
+ | REPLAY_SYM
| REPLICATION
| RESOURCES
| RESTART_SYM
@@ -16469,6 +15802,7 @@ keyword_sp_var_and_label:
| THAN_SYM
| TRANSACTION_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
| TRANSACTIONAL_SYM
+ | THREADS_SYM
| TRIGGERS_SYM
| TRIM_ORACLE
| TIMESTAMP_ADD
@@ -16476,7 +15810,6 @@ keyword_sp_var_and_label:
| TYPES_SYM
| TYPE_SYM
| UDF_RETURNS_SYM
- | FUNCTION_SYM
| UNCOMMITTED_SYM
| UNDEFINED_SYM
| UNDO_BUFFER_SIZE_SYM
@@ -16489,6 +15822,7 @@ keyword_sp_var_and_label:
| VERSIONING_SYM
| VIEW_SYM
| VIRTUAL_SYM
+ | VISIBLE_SYM
| VALUE_SYM
| WARNINGS
| WAIT_SYM
@@ -16501,6 +15835,246 @@ keyword_sp_var_and_label:
| VIA_SYM
;
+
+reserved_keyword_udt_not_param_type:
+ ACCESSIBLE_SYM
+ | ADD
+ | ALL
+ | ALTER
+ | ANALYZE_SYM
+ | AND_SYM
+ | AS
+ | ASC
+ | ASENSITIVE_SYM
+ | BEFORE_SYM
+ | BETWEEN_SYM
+ | BIT_AND
+ | BIT_OR
+ | BIT_XOR
+ | BODY_ORACLE_SYM
+ | BOTH
+ | BY
+ | CALL_SYM
+ | CASCADE
+ | CASE_SYM
+ | CAST_SYM
+ | CHANGE
+ | CHECK_SYM
+ | COLLATE_SYM
+ | CONSTRAINT
+ | CONTINUE_MARIADB_SYM
+ | CONTINUE_ORACLE_SYM
+ | CONVERT_SYM
+ | COUNT_SYM
+ | CREATE
+ | CROSS
+ | CUME_DIST_SYM
+ | CURDATE
+ | CURRENT_USER
+ | CURRENT_ROLE
+ | CURTIME
+ | DATABASE
+ | DATABASES
+ | DATE_ADD_INTERVAL
+ | DATE_SUB_INTERVAL
+ | DAY_HOUR_SYM
+ | DAY_MICROSECOND_SYM
+ | DAY_MINUTE_SYM
+ | DAY_SECOND_SYM
+ | DECLARE_MARIADB_SYM
+ | DECLARE_ORACLE_SYM
+ | DEFAULT
+ | DELETE_DOMAIN_ID_SYM
+ | DELETE_SYM
+ | DENSE_RANK_SYM
+ | DESC
+ | DESCRIBE
+ | DETERMINISTIC_SYM
+ | DISTINCT
+ | DIV_SYM
+ | DO_DOMAIN_IDS_SYM
+ | DROP
+ | DUAL_SYM
+ | EACH_SYM
+ | ELSE
+ | ELSEIF_MARIADB_SYM
+ | ELSIF_ORACLE_SYM
+ | ENCLOSED
+ | ESCAPED
+ | EXCEPT_SYM
+ | EXISTS
+ | EXTRACT_SYM
+ | FALSE_SYM
+ | FETCH_SYM
+ | FIRST_VALUE_SYM
+ | FOREIGN
+ | FROM
+ | FULLTEXT_SYM
+ | GOTO_ORACLE_SYM
+ | GRANT
+ | GROUP_SYM
+ | GROUP_CONCAT_SYM
+ | LAG_SYM
+ | LEAD_SYM
+ | HAVING
+ | HOUR_MICROSECOND_SYM
+ | HOUR_MINUTE_SYM
+ | HOUR_SECOND_SYM
+ | IF_SYM
+ | IGNORE_DOMAIN_IDS_SYM
+ | IGNORE_SYM
+ | INDEX_SYM
+ | INFILE
+ | INNER_SYM
+ | INSENSITIVE_SYM
+ | INSERT
+ | INTERSECT_SYM
+ | INTERVAL_SYM
+ | INTO
+ | IS
+ | ITERATE_SYM
+ | JOIN_SYM
+ | KEYS
+ | KEY_SYM
+ | KILL_SYM
+ | LEADING
+ | LEAVE_SYM
+ | LEFT
+ | LIKE
+ | LIMIT
+ | LINEAR_SYM
+ | LINES
+ | LOAD
+ | LOCATOR_SYM
+ | LOCK_SYM
+ | LOOP_SYM
+ | LOW_PRIORITY
+ | MASTER_SSL_VERIFY_SERVER_CERT_SYM
+ | MATCH
+ | MAX_SYM
+ | MAXVALUE_SYM
+ | MEDIAN_SYM
+ | MINUTE_MICROSECOND_SYM
+ | MINUTE_SECOND_SYM
+ | MIN_SYM
+ | MODIFIES_SYM
+ | MOD_SYM
+ | NATURAL
+ | NEG
+ | NOT_SYM
+ | NOW_SYM
+ | NO_WRITE_TO_BINLOG
+ | NTILE_SYM
+ | NULL_SYM
+ | NTH_VALUE_SYM
+ | ON
+ | OPTIMIZE
+ | OPTIONALLY
+ | ORDER_SYM
+ | OR_SYM
+ | OTHERS_ORACLE_SYM
+ | OUTER
+ | OUTFILE
+ | OVER_SYM
+ | PACKAGE_ORACLE_SYM
+ | PAGE_CHECKSUM_SYM
+ | PARSE_VCOL_EXPR_SYM
+ | PARTITION_SYM
+ | PERCENT_RANK_SYM
+ | PERCENTILE_CONT_SYM
+ | PERCENTILE_DISC_SYM
+ | PORTION_SYM
+ | POSITION_SYM
+ | PRECISION
+ | PRIMARY_SYM
+ | PROCEDURE_SYM
+ | PURGE
+ | RAISE_ORACLE_SYM
+ | RANGE_SYM
+ | RANK_SYM
+ | READS_SYM
+ | READ_SYM
+ | READ_WRITE_SYM
+ | RECURSIVE_SYM
+ | REF_SYSTEM_ID_SYM
+ | REFERENCES
+ | REGEXP
+ | RELEASE_SYM
+ | RENAME
+ | REPEAT_SYM
+ | REPLACE
+ | REQUIRE_SYM
+ | RESIGNAL_SYM
+ | RESTRICT
+ | RETURNING_SYM
+ | RETURN_MARIADB_SYM
+ | RETURN_ORACLE_SYM
+ | REVOKE
+ | RIGHT
+ | ROWS_SYM
+ | ROWTYPE_ORACLE_SYM
+ | ROW_NUMBER_SYM
+ | SECOND_MICROSECOND_SYM
+ | SELECT_SYM
+ | SENSITIVE_SYM
+ | SEPARATOR_SYM
+ | SERVER_OPTIONS
+ | SHOW
+ | SIGNAL_SYM
+ | SPATIAL_SYM
+ | SPECIFIC_SYM
+ | SQLEXCEPTION_SYM
+ | SQLSTATE_SYM
+ | SQLWARNING_SYM
+ | SQL_BIG_RESULT
+ | SQL_SMALL_RESULT
+ | SQL_SYM
+ | SSL_SYM
+ | STARTING
+ | STATS_AUTO_RECALC_SYM
+ | STATS_PERSISTENT_SYM
+ | STATS_SAMPLE_PAGES_SYM
+ | STDDEV_SAMP_SYM
+ | STD_SYM
+ | STRAIGHT_JOIN
+ | SUBSTRING
+ | SUM_SYM
+ | SYSDATE
+ | TABLE_REF_PRIORITY
+ | TABLE_SYM
+ | TERMINATED
+ | THEN_SYM
+ | TO_SYM
+ | TRAILING
+ | TRIGGER_SYM
+ | TRIM
+ | TRUE_SYM
+ | UNDO_SYM
+ | UNION_SYM
+ | UNIQUE_SYM
+ | UNLOCK_SYM
+ | UPDATE_SYM
+ | USAGE
+ | USE_SYM
+ | USING
+ | UTC_DATE_SYM
+ | UTC_TIMESTAMP_SYM
+ | UTC_TIME_SYM
+ | VALUES
+ | VALUES_IN_SYM
+ | VALUES_LESS_SYM
+ | VARIANCE_SYM
+ | VARYING
+ | VAR_SAMP_SYM
+ | WHEN_SYM
+ | WHERE
+ | WHILE_SYM
+ | WITH
+ | XOR
+ | YEAR_MONTH_SYM
+ | ZEROFILL
+ ;
+
/*
SQLCOM_SET_OPTION statement.
@@ -16512,117 +16086,78 @@ set:
SET
{
LEX *lex=Lex;
- if (lex->main_select_push(true))
- MYSQL_YYABORT;
lex->set_stmt_init();
- lex->var_list.empty();
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
}
- start_option_value_list
+ set_param
{
- Lex->pop_select(); //main select
if (Lex->check_main_unit_semantics())
MYSQL_YYABORT;
}
- | SET STATEMENT_SYM
+ ;
+
+set_param:
+ option_value_no_option_type
+ | option_value_no_option_type ',' option_value_list
+ | TRANSACTION_SYM
{
- if (Lex->main_select_push())
+ Lex->option_type= OPT_DEFAULT;
+ if (sp_create_assignment_lex(thd, $1.pos()))
MYSQL_YYABORT;
- Lex->set_stmt_init();
}
- set_stmt_option_value_following_option_type_list
+ transaction_characteristics
+ {
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | option_type
+ {
+ Lex->option_type= $1;
+ }
+ start_option_value_list_following_option_type
+ | STATEMENT_SYM
+ set_stmt_option_list
{
LEX *lex= Lex;
if (unlikely(lex->table_or_sp_used()))
my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT"));
lex->stmt_var_list= lex->var_list;
lex->var_list.empty();
- Lex->pop_select(); //main select
if (Lex->check_main_unit_semantics())
MYSQL_YYABORT;
}
- FOR_SYM verb_clause
- {}
+ FOR_SYM directly_executable_statement
;
-set_stmt_option_value_following_option_type_list:
+set_stmt_option_list:
/*
Only system variables can be used here. If this condition is changed
please check careful code under lex->option_type == OPT_STATEMENT
condition on wrong type casts.
*/
- option_value_following_option_type
- | set_stmt_option_value_following_option_type_list ',' option_value_following_option_type
- ;
-
-/* Start of option value list */
-start_option_value_list:
- option_value_no_option_type
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- option_value_list_continued
- | TRANSACTION_SYM
- {
- Lex->option_type= OPT_DEFAULT;
- }
- transaction_characteristics
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- | option_type
- {
- Lex->option_type= $1;
- }
- start_option_value_list_following_option_type
+ set_stmt_option
+ | set_stmt_option_list ',' set_stmt_option
;
-
/* Start of option value list, option_type was given */
start_option_value_list_following_option_type:
option_value_following_option_type
+ | option_value_following_option_type ',' option_value_list
+ | TRANSACTION_SYM
{
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ if (sp_create_assignment_lex(thd, $1.pos()))
MYSQL_YYABORT;
}
- option_value_list_continued
- | TRANSACTION_SYM transaction_characteristics
+ transaction_characteristics
{
if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
;
-/* Remainder of the option value list after first option value. */
-option_value_list_continued:
- /* empty */
- | ',' option_value_list
- ;
-
/* Repeating list of option values after first option value. */
option_value_list:
- {
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
option_value
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- | option_value_list ','
- {
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- option_value
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
+ | option_value_list ',' option_value
;
/* Wrapper around option values following the first option value in the stmt. */
@@ -16655,64 +16190,182 @@ opt_var_ident_type:
| SESSION_SYM '.' { $$=OPT_SESSION; }
;
+/*
+ SET STATEMENT options do not need their own LEX or Query_arena.
+ Let's put them to the main ones.
+*/
+set_stmt_option:
+ ident_cli equal
+ {
+ if (Lex->main_select_push(false))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_system_variable(Lex->option_type, &tmp, $4)))
+ MYSQL_YYABORT;
+ Lex->pop_select(); //min select
+ }
+ | ident_cli '.' ident equal
+ {
+ if (Lex->main_select_push(false))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_system_variable(thd, Lex->option_type,
+ &tmp, &$3, $6)))
+ MYSQL_YYABORT;
+ Lex->pop_select(); //min select
+ }
+ | DEFAULT '.' ident equal
+ {
+ if (Lex->main_select_push(false))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ if (unlikely(Lex->set_default_system_variable(Lex->option_type,
+ &$3, $6)))
+ MYSQL_YYABORT;
+ Lex->pop_select(); //min select
+ }
+ ;
+
+
/* Option values with preceding option_type. */
option_value_following_option_type:
- ident equal set_expr_or_default
+ ident_cli equal
{
- if (unlikely(Lex->set_system_variable(Lex->option_type, &$1, $3)))
+ if (sp_create_assignment_lex(thd, $1.pos()))
MYSQL_YYABORT;
}
- | ident '.' ident equal set_expr_or_default
+ set_expr_or_default
{
- if (unlikely(Lex->set_system_variable(thd, Lex->option_type, &$1, &$3, $5)))
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_system_variable(Lex->option_type, &tmp, $4)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
- | DEFAULT '.' ident equal set_expr_or_default
+ | ident_cli '.' ident equal
{
- if (unlikely(Lex->set_default_system_variable(Lex->option_type, &$3, $5)))
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_system_variable(thd, Lex->option_type, &tmp, &$3, $6)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | DEFAULT '.' ident equal
+ {
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ if (unlikely(Lex->set_default_system_variable(Lex->option_type, &$3, $6)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
;
/* Option values without preceding option_type. */
option_value_no_option_type:
- ident_set_usual_case equal set_expr_or_default
+ ident_cli_set_usual_case equal
+ {
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_variable(&tmp, $4)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | ident_cli_set_usual_case '.' ident equal
+ {
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_variable(&tmp, &$3, $6)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | DEFAULT '.' ident equal
+ {
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ if (unlikely(Lex->set_default_system_variable(Lex->option_type, &$3, $6)))
+ MYSQL_YYABORT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | '@' ident_or_text equal
{
- if (unlikely(Lex->set_variable(&$1, $3)))
+ if (sp_create_assignment_lex(thd, $1.str))
MYSQL_YYABORT;
}
- | ident '.' ident equal set_expr_or_default
+ expr
{
- if (unlikely(Lex->set_variable(&$1, &$3, $5)))
+ if (unlikely(Lex->set_user_variable(thd, &$2, $5)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
- | DEFAULT '.' ident equal set_expr_or_default
+ | '@' '@' opt_var_ident_type ident_sysvar_name equal
{
- if (unlikely(Lex->set_default_system_variable(Lex->option_type, &$3, $5)))
+ if (sp_create_assignment_lex(thd, $1.str))
MYSQL_YYABORT;
}
- | '@' ident_or_text equal expr
+ set_expr_or_default
{
- if (unlikely(Lex->set_user_variable(thd, &$2, $4)))
+ if (unlikely(Lex->set_system_variable($3, &$4, $7)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
- | '@' '@' opt_var_ident_type ident_sysvar_name equal set_expr_or_default
+ | '@' '@' opt_var_ident_type ident_sysvar_name '.' ident equal
{
- if (unlikely(Lex->set_system_variable($3, &$4, $6)))
+ if (sp_create_assignment_lex(thd, $1.str))
MYSQL_YYABORT;
}
- | '@' '@' opt_var_ident_type ident_sysvar_name '.' ident equal set_expr_or_default
+ set_expr_or_default
{
- if (unlikely(Lex->set_system_variable(thd, $3, &$4, &$6, $8)))
+ if (unlikely(Lex->set_system_variable(thd, $3, &$4, &$6, $9)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
- | '@' '@' opt_var_ident_type DEFAULT '.' ident equal set_expr_or_default
+ | '@' '@' opt_var_ident_type DEFAULT '.' ident equal
{
- if (unlikely(Lex->set_default_system_variable($3, &$6, $8)))
+ if (sp_create_assignment_lex(thd, $1.str))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ if (unlikely(Lex->set_default_system_variable($3, &$6, $9)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
| charset old_or_new_charset_name_or_default
{
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
LEX *lex= thd->lex;
CHARSET_INFO *cs2;
cs2= $2 ? $2: global_system_variables.character_set_client;
@@ -16724,6 +16377,8 @@ option_value_no_option_type:
if (unlikely(var == NULL))
MYSQL_YYABORT;
lex->var_list.push_back(var, thd->mem_root);
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
| NAMES_SYM equal expr
{
@@ -16738,6 +16393,8 @@ option_value_no_option_type:
}
| NAMES_SYM charset_name_or_default opt_collate
{
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
LEX *lex= Lex;
CHARSET_INFO *cs2;
CHARSET_INFO *cs3;
@@ -16752,11 +16409,14 @@ option_value_no_option_type:
set_var_collation_client *var;
var= new (thd->mem_root) set_var_collation_client(cs3, cs3, cs3);
if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
+ unlikely(lex->var_list.push_back(var, thd->mem_root)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
| DEFAULT ROLE_SYM grant_role
{
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
LEX *lex = Lex;
LEX_USER *user;
if (unlikely(!(user=(LEX_USER *) thd->calloc(sizeof(LEX_USER)))))
@@ -16772,9 +16432,13 @@ option_value_no_option_type:
thd->lex->autocommit= TRUE;
if (lex->sphead)
lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
| DEFAULT ROLE_SYM grant_role FOR_SYM user
{
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
LEX *lex = Lex;
set_var_default_role *var= (new (thd->mem_root)
set_var_default_role($5, $3->user));
@@ -16784,35 +16448,57 @@ option_value_no_option_type:
thd->lex->autocommit= TRUE;
if (lex->sphead)
lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
}
| ROLE_SYM ident_or_text
{
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
LEX *lex = Lex;
set_var_role *var= new (thd->mem_root) set_var_role($2);
if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
+ unlikely(lex->var_list.push_back(var, thd->mem_root)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
MYSQL_YYABORT;
}
- | ROLE_SYM equal set_expr_or_default
+ | ROLE_SYM equal
{
- if (unlikely(Lex->set_variable(&$1, $3)))
+ if (sp_create_assignment_lex(thd, $1.pos()))
MYSQL_YYABORT;
}
- | PASSWORD_SYM opt_for_user text_or_password
+ set_expr_or_default
{
- LEX *lex = Lex;
- set_var_password *var= (new (thd->mem_root)
- set_var_password(lex->definer));
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_variable(&tmp, $4)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | PASSWORD_SYM equal
+ {
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ text_or_password
+ {
+ if (unlikely(Lex->sp_create_set_password_instr(thd, $4,
+ yychar == YYEMPTY)))
+ MYSQL_YYABORT;
+ }
+ | PASSWORD_SYM FOR_SYM
+ {
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ user equal text_or_password
+ {
+ if (unlikely(Lex->sp_create_set_password_instr(thd, $4, $6,
+ yychar == YYEMPTY)))
MYSQL_YYABORT;
- lex->autocommit= TRUE;
- if (lex->sphead)
- lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
}
;
-
transaction_characteristics:
transaction_access_mode
| isolation_level
@@ -16869,42 +16555,25 @@ isolation_types:
| SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; }
;
-opt_for_user:
- equal
- {
- LEX *lex= thd->lex;
- sp_pcontext *spc= lex->spcont;
- LEX_CSTRING pw= { STRING_WITH_LEN("password") };
-
- if (unlikely(spc && spc->find_variable(&pw, false)))
- my_yyabort_error((ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str));
- if (unlikely(!(lex->definer= (LEX_USER*)
- thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- lex->definer->user= current_user;
- lex->definer->auth= new (thd->mem_root) USER_AUTH();
- }
- | FOR_SYM user equal { Lex->definer= $2; }
- ;
text_or_password:
TEXT_STRING
{
- Lex->definer->auth= new (thd->mem_root) USER_AUTH();
- Lex->definer->auth->auth_str= $1;
+ $$= new (thd->mem_root) USER_AUTH();
+ $$->auth_str= $1;
}
| PASSWORD_SYM '(' TEXT_STRING ')'
{
- Lex->definer->auth= new (thd->mem_root) USER_AUTH();
- Lex->definer->auth->pwtext= $3;
+ $$= new (thd->mem_root) USER_AUTH();
+ $$->pwtext= $3;
}
| OLD_PASSWORD_SYM '(' TEXT_STRING ')'
{
- Lex->definer->auth= new (thd->mem_root) USER_AUTH();
- Lex->definer->auth->pwtext= $3;
- Lex->definer->auth->auth_str.str= Item_func_password::alloc(thd,
+ $$= new (thd->mem_root) USER_AUTH();
+ $$->pwtext= $3;
+ $$->auth_str.str= Item_func_password::alloc(thd,
$3.str, $3.length, Item_func_password::OLD);
- Lex->definer->auth->auth_str.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ $$->auth_str.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
}
;
@@ -17137,34 +16806,12 @@ revoke:
revoke_command:
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_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_FUNCTION)))
+ if (Lex->stmt_revoke_table(thd, $1, *$4))
MYSQL_YYABORT;
}
- | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
+ | grant_privileges ON sp_handler grant_ident FROM user_and_role_list
{
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_PROCEDURE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM grant_ident
- FROM user_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_PACKAGE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM BODY_ORACLE_SYM grant_ident
- FROM user_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_PACKAGE_BODY)))
+ if (Lex->stmt_revoke_sp(thd, $1, *$4, *$3))
MYSQL_YYABORT;
}
| ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
@@ -17173,10 +16820,8 @@ revoke_command:
}
| PROXY_SYM ON user FROM user_list
{
- LEX *lex= Lex;
- lex->users_list.push_front ($3);
- lex->sql_command= SQLCOM_REVOKE;
- lex->type= TYPE_ENUM_PROXY;
+ if (Lex->stmt_revoke_proxy(thd, $3))
+ MYSQL_YYABORT;
}
| admin_option_for_role FROM user_and_role_list
{
@@ -17202,44 +16847,19 @@ grant_command:
grant_privileges ON opt_table grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_GRANT;
- lex->type= 0;
- }
- | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_FUNCTION)))
+ if (Lex->stmt_grant_table(thd, $1, *$4, $8))
MYSQL_YYABORT;
}
- | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
+ | grant_privileges ON sp_handler grant_ident TO_SYM grant_list
opt_require_clause opt_grant_options
{
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_PROCEDURE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_PACKAGE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM BODY_ORACLE_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_PACKAGE_BODY)))
+ if (Lex->stmt_grant_sp(thd, $1, *$4, *$3, $8))
MYSQL_YYABORT;
}
| PROXY_SYM ON user TO_SYM grant_list opt_grant_option
{
- LEX *lex= Lex;
- lex->users_list.push_front ($3);
- lex->sql_command= SQLCOM_GRANT;
- lex->type= TYPE_ENUM_PROXY;
+ if (Lex->stmt_grant_proxy(thd, $3, $6))
+ MYSQL_YYABORT;
}
| grant_role TO_SYM grant_list opt_with_admin_option
{
@@ -17290,7 +16910,7 @@ grant_role:
{
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);
+ $1.length= cs->lengthsp($1.str, $1.length);
((char*) $1.str)[$1.length] = '\0';
if (unlikely($1.length == 0))
my_yyabort_error((ER_INVALID_ROLE, MYF(0), ""));
@@ -17314,11 +16934,11 @@ opt_table:
;
grant_privileges:
- object_privilege_list {}
+ object_privilege_list
| ALL opt_privileges
{
- Lex->all_privileges= 1;
- Lex->grant= GLOBAL_ACLS;
+ if (!($$= new (thd->mem_root) Lex_grant_privilege(GLOBAL_ACLS, true)))
+ MYSQL_YYABORT;
}
;
@@ -17329,49 +16949,85 @@ opt_privileges:
object_privilege_list:
object_privilege
+ {
+ if (!($$= new (thd->mem_root) Lex_grant_privilege($1)))
+ MYSQL_YYABORT;
+ }
+ | column_list_privilege
+ {
+ if (!($$= new (thd->mem_root) Lex_grant_privilege()) ||
+ $$->add_column_list_privilege(thd, $1.m_columns[0],
+ $1.m_privilege))
+ MYSQL_YYABORT;
+ }
| object_privilege_list ',' object_privilege
+ {
+ ($$= $1)->add_object_privilege($3);
+ }
+ | object_privilege_list ',' column_list_privilege
+ {
+ if (($$= $1)->add_column_list_privilege(thd, $3.m_columns[0],
+ $3.m_privilege))
+ MYSQL_YYABORT;
+ }
+ ;
+
+column_list_privilege:
+ column_privilege '(' comma_separated_ident_list ')'
+ {
+ $$= Lex_column_list_privilege($3, $1);
+ }
+ ;
+
+column_privilege:
+ SELECT_SYM { $$= SELECT_ACL; }
+ | INSERT { $$= INSERT_ACL; }
+ | UPDATE_SYM { $$= UPDATE_ACL; }
+ | REFERENCES { $$= REFERENCES_ACL; }
;
object_privilege:
- SELECT_SYM
- { Lex->which_columns = SELECT_ACL;}
- opt_column_list {}
- | INSERT
- { Lex->which_columns = INSERT_ACL;}
- opt_column_list {}
- | UPDATE_SYM
- { Lex->which_columns = UPDATE_ACL; }
- opt_column_list {}
- | REFERENCES
- { Lex->which_columns = REFERENCES_ACL;}
- opt_column_list {}
- | DELETE_SYM { Lex->grant |= DELETE_ACL;}
- | USAGE {}
- | INDEX_SYM { Lex->grant |= INDEX_ACL;}
- | ALTER { Lex->grant |= ALTER_ACL;}
- | CREATE { Lex->grant |= CREATE_ACL;}
- | DROP { Lex->grant |= DROP_ACL;}
- | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;}
- | RELOAD { Lex->grant |= RELOAD_ACL;}
- | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;}
- | PROCESS { Lex->grant |= PROCESS_ACL;}
- | FILE_SYM { Lex->grant |= FILE_ACL;}
- | GRANT OPTION { Lex->grant |= GRANT_ACL;}
- | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;}
- | SUPER_SYM { Lex->grant |= SUPER_ACL;}
- | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;}
- | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; }
- | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; }
- | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
- | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
- | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
- | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; }
- | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; }
- | CREATE USER_SYM { Lex->grant |= CREATE_USER_ACL; }
- | EVENT_SYM { Lex->grant |= EVENT_ACL;}
- | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; }
- | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; }
- | DELETE_SYM HISTORY_SYM { Lex->grant |= DELETE_HISTORY_ACL; }
+ SELECT_SYM { $$= SELECT_ACL; }
+ | INSERT { $$= INSERT_ACL; }
+ | UPDATE_SYM { $$= UPDATE_ACL; }
+ | REFERENCES { $$= REFERENCES_ACL; }
+ | DELETE_SYM { $$= DELETE_ACL;}
+ | USAGE { $$= NO_ACL; }
+ | INDEX_SYM { $$= INDEX_ACL;}
+ | ALTER { $$= ALTER_ACL;}
+ | CREATE { $$= CREATE_ACL;}
+ | DROP { $$= DROP_ACL;}
+ | EXECUTE_SYM { $$= EXECUTE_ACL;}
+ | RELOAD { $$= RELOAD_ACL;}
+ | SHUTDOWN { $$= SHUTDOWN_ACL;}
+ | PROCESS { $$= PROCESS_ACL;}
+ | FILE_SYM { $$= FILE_ACL;}
+ | GRANT OPTION { $$= GRANT_ACL;}
+ | SHOW DATABASES { $$= SHOW_DB_ACL;}
+ | SUPER_SYM { $$= SUPER_ACL;}
+ | CREATE TEMPORARY TABLES { $$= CREATE_TMP_ACL;}
+ | LOCK_SYM TABLES { $$= LOCK_TABLES_ACL; }
+ | REPLICATION SLAVE { $$= REPL_SLAVE_ACL; }
+ | REPLICATION CLIENT_SYM { $$= BINLOG_MONITOR_ACL; /*Compatibility*/ }
+ | CREATE VIEW_SYM { $$= CREATE_VIEW_ACL; }
+ | SHOW VIEW_SYM { $$= SHOW_VIEW_ACL; }
+ | CREATE ROUTINE_SYM { $$= CREATE_PROC_ACL; }
+ | ALTER ROUTINE_SYM { $$= ALTER_PROC_ACL; }
+ | CREATE USER_SYM { $$= CREATE_USER_ACL; }
+ | EVENT_SYM { $$= EVENT_ACL;}
+ | TRIGGER_SYM { $$= TRIGGER_ACL; }
+ | CREATE TABLESPACE { $$= CREATE_TABLESPACE_ACL; }
+ | DELETE_SYM HISTORY_SYM { $$= DELETE_HISTORY_ACL; }
+ | SET USER_SYM { $$= SET_USER_ACL; }
+ | FEDERATED_SYM ADMIN_SYM { $$= FEDERATED_ADMIN_ACL; }
+ | CONNECTION_SYM ADMIN_SYM { $$= CONNECTION_ADMIN_ACL; }
+ | READ_SYM ONLY_SYM ADMIN_SYM { $$= READ_ONLY_ADMIN_ACL; }
+ | READ_ONLY_SYM ADMIN_SYM { $$= READ_ONLY_ADMIN_ACL; }
+ | BINLOG_SYM MONITOR_SYM { $$= BINLOG_MONITOR_ACL; }
+ | BINLOG_SYM ADMIN_SYM { $$= BINLOG_ADMIN_ACL; }
+ | BINLOG_SYM REPLAY_SYM { $$= BINLOG_REPLAY_ACL; }
+ | REPLICATION MASTER_SYM ADMIN_SYM { $$= REPL_MASTER_ADMIN_ACL; }
+ | REPLICATION SLAVE ADMIN_SYM { $$= REPL_SLAVE_ADMIN_ACL; }
;
opt_and:
@@ -17411,41 +17067,30 @@ require_list_element:
grant_ident:
'*'
{
- LEX *lex= Lex;
- if (unlikely(lex->copy_db_to(&lex->first_select_lex()->db)))
+ LEX_CSTRING db;
+ if (unlikely(Lex->copy_db_to(&db)))
+ MYSQL_YYABORT;
+ if (!($$= new (thd->mem_root) Lex_grant_object_name(db,
+ Lex_grant_object_name::STAR)))
MYSQL_YYABORT;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (unlikely(lex->columns.elements))
- my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
}
| ident '.' '*'
{
- LEX *lex= Lex;
- lex->first_select_lex()->db= $1;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (unlikely(lex->columns.elements))
- my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
+ if (!($$= new (thd->mem_root) Lex_grant_object_name($1,
+ Lex_grant_object_name::IDENT_STAR)))
+ MYSQL_YYABORT;
}
| '*' '.' '*'
{
- LEX *lex= Lex;
- lex->first_select_lex()->db= null_clex_str;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
- else if (unlikely(lex->columns.elements))
- my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
+ if (!($$= new (thd->mem_root) Lex_grant_object_name(
+ null_clex_str,
+ Lex_grant_object_name::STAR_STAR)))
+ MYSQL_YYABORT;
}
| table_ident
{
- LEX *lex=Lex;
- if (unlikely(!lex->first_select_lex()->
- add_table_to_list(thd, $1,NULL,
- TL_OPTION_UPDATING)))
+ if (!($$= new (thd->mem_root) Lex_grant_object_name($1)))
MYSQL_YYABORT;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = TABLE_ACLS & ~GRANT_ACL;
}
;
@@ -17556,49 +17201,6 @@ opt_auth_str:
}
;
-opt_column_list:
- /* empty */
- {
- LEX *lex=Lex;
- lex->grant |= lex->which_columns;
- }
- | '(' column_list ')' { }
- ;
-
-column_list:
- column_list ',' column_list_id
- | column_list_id
- ;
-
-column_list_id:
- ident
- {
- String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
- if (unlikely(new_str == NULL))
- MYSQL_YYABORT;
- List_iterator <LEX_COLUMN> iter(Lex->columns);
- class LEX_COLUMN *point;
- LEX *lex=Lex;
- while ((point=iter++))
- {
- if (!my_strcasecmp(system_charset_info,
- point->column.c_ptr(), new_str->c_ptr()))
- break;
- }
- lex->grant_tot_col|= lex->which_columns;
- if (point)
- point->rights |= lex->which_columns;
- else
- {
- LEX_COLUMN *col= (new (thd->mem_root)
- LEX_COLUMN(*new_str,lex->which_columns));
- if (unlikely(col == NULL))
- MYSQL_YYABORT;
- lex->columns.push_back(col, thd->mem_root);
- }
- }
- ;
-
opt_require_clause:
/* empty */
| REQUIRE_SYM require_list
@@ -17659,23 +17261,23 @@ opt_resource_options:
opt_grant_options:
- /* empty */ {}
- | WITH grant_option_list {}
+ /* empty */ { $$= NO_ACL; }
+ | WITH grant_option_list { $$= $2; }
;
opt_grant_option:
- /* empty */ {}
- | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
+ /* empty */ { $$= NO_ACL; }
+ | WITH GRANT OPTION { $$= GRANT_ACL; }
;
grant_option_list:
- grant_option_list grant_option {}
- | grant_option {}
+ grant_option_list grant_option { $$= $1 | $2; }
+ | grant_option
;
grant_option:
- GRANT OPTION { Lex->grant |= GRANT_ACL;}
- | resource_option {}
+ GRANT OPTION { $$= GRANT_ACL;}
+ | resource_option { $$= NO_ACL; }
;
begin_stmt_mariadb:
@@ -17782,10 +17384,10 @@ release:
unit_type_decl:
UNION_SYM union_option
{ $$.unit_type= UNION_TYPE; $$.distinct= $2; }
- | INTERSECT_SYM
- { $$.unit_type= INTERSECT_TYPE; $$.distinct= 1; }
- | EXCEPT_SYM
- { $$.unit_type= EXCEPT_TYPE; $$.distinct= 1; }
+ | INTERSECT_SYM union_option
+ { $$.unit_type= INTERSECT_TYPE; $$.distinct= $2; }
+ | EXCEPT_SYM union_option
+ { $$.unit_type= EXCEPT_TYPE; $$.distinct= $2; }
;
/*
@@ -17806,6 +17408,7 @@ query_expression_option:
Select->options|= SELECT_HIGH_PRIORITY;
}
| DISTINCT { Select->options|= SELECT_DISTINCT; }
+ | UNIQUE_SYM { Select->options|= SELECT_DISTINCT; }
| SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
| SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
| SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; }
@@ -18014,7 +17617,6 @@ trigger_tail:
sf_return_type:
- RETURNS_SYM
{
LEX *lex= Lex;
lex->init_last_field(&lex->sphead->m_return_field_def,
@@ -18023,47 +17625,11 @@ sf_return_type:
}
type_with_opt_collate
{
- if (unlikely(Lex->sphead->fill_field_definition(thd,
- Lex->last_field)))
+ if (unlikely(Lex->sf_return_fill_definition($2)))
MYSQL_YYABORT;
}
;
-sf_c_chistics_and_body:
- sp_c_chistics
- {
- LEX *lex= thd->lex;
- lex->sphead->set_c_chistics(lex->sp_chistics);
- lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
- }
- sp_proc_stmt_in_returns_clause
- {
- if (unlikely(Lex->sp_body_finalize_function(thd)))
- MYSQL_YYABORT;
- }
- ;
-
-
-sp_tail:
- sp_name
- {
- if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1,
- &sp_handler_procedure,
- DEFAULT_AGGREGATE)))
- MYSQL_YYABORT;
- }
- sp_parenthesized_pdparam_list
- sp_c_chistics
- {
- Lex->sphead->set_c_chistics(Lex->sp_chistics);
- Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
- }
- sp_proc_stmt
- {
- if (unlikely(Lex->sp_body_finalize_procedure(thd)))
- MYSQL_YYABORT;
- }
- ;
/*************************************************************************/
@@ -18129,7 +17695,10 @@ xid:
}
| text_string ',' text_string ',' ulong_num
{
- MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE);
+ MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE &&
+ $3->length() <= MAXBQUALSIZE &&
+ $5 <= static_cast<ulong>(
+ std::numeric_limits<int32_t>::max()));
if (unlikely(!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))))
MYSQL_YYABORT;
Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length());
@@ -18199,6 +17768,1423 @@ keep_gcc_happy:
}
;
+_empty:
+ /* Empty */
+ ;
+
+/* Start SQL_MODE_DEFAULT_SPECIFIC */
+
+
+statement:
+ verb_clause
+ ;
+
+sp_statement:
+ statement
+ ;
+
+sp_if_then_statements:
+ sp_proc_stmts1
+ ;
+
+sp_case_then_statements:
+ sp_proc_stmts1
+ ;
+
+reserved_keyword_udt_param_type:
+ INOUT_SYM
+ | IN_SYM
+ | OUT_SYM
+ ;
+
+reserved_keyword_udt:
+ reserved_keyword_udt_not_param_type
+ | reserved_keyword_udt_param_type
+ ;
+
+// Keywords that start an SP block section
+keyword_sp_block_section:
+ BEGIN_MARIADB_SYM
+ | END
+ ;
+
+// Keywords that we allow for labels in SPs.
+// Should not include keywords that start a statement or SP characteristics.
+keyword_label:
+ keyword_data_type
+ | keyword_set_special_case
+ | keyword_sp_var_and_label
+ | keyword_sysvar_type
+ | FUNCTION_SYM
+ | EXCEPTION_ORACLE_SYM
+ ;
+
+keyword_sp_decl:
+ keyword_data_type
+ | keyword_cast_type
+ | keyword_set_special_case
+ | keyword_sp_block_section
+ | keyword_sp_head
+ | keyword_sp_var_and_label
+ | keyword_sp_var_not_label
+ | keyword_sysvar_type
+ | keyword_verb_clause
+ | FUNCTION_SYM
+ | WINDOW_SYM
+ ;
+
+opt_truncate_table_storage_clause:
+ _empty
+ ;
+
+
+ident_for_loop_index:
+ ident
+ ;
+
+row_field_name:
+ ident
+ {
+ if (!($$= Lex->row_field_name(thd, $1)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+while_body:
+ expr_lex DO_SYM
+ {
+ if (unlikely($1->sp_while_loop_expression(thd)))
+ MYSQL_YYABORT;
+ }
+ sp_proc_stmts1 END WHILE_SYM
+ {
+ if (unlikely(Lex->sp_while_loop_finalize(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+for_loop_statements:
+ DO_SYM sp_proc_stmts1 END FOR_SYM
+ { }
+ ;
+
+sp_label:
+ label_ident ':' { $$= $1; }
+ ;
+
+sp_control_label:
+ sp_label
+ ;
+
+sp_block_label:
+ sp_label
+ {
+ if (unlikely(Lex->spcont->block_label_declare(&$1)))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
+ ;
+
+sp_opt_default:
+ _empty { $$ = NULL; }
+ | DEFAULT expr { $$ = $2; }
+ ;
+
+sp_pdparam:
+ sp_parameter_type sp_param_name_and_type { $2->mode=$1; }
+ | sp_param_name_and_type { $1->mode= sp_variable::MODE_IN; }
+ ;
+
+sp_decl_variable_list_anchored:
+ sp_decl_idents_init_vars
+ TYPE_SYM OF_SYM optionally_qualified_column_ident
+ sp_opt_default
+ {
+ if (unlikely(Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $4, $5)))
+ MYSQL_YYABORT;
+ $$.init_using_vars($1);
+ }
+ | sp_decl_idents_init_vars
+ ROW_SYM TYPE_SYM OF_SYM optionally_qualified_column_ident
+ sp_opt_default
+ {
+ if (unlikely(Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $5, $6)))
+ MYSQL_YYABORT;
+ $$.init_using_vars($1);
+ }
+ ;
+
+sp_param_name_and_type_anchored:
+ sp_param_name TYPE_SYM OF_SYM ident '.' ident
+ {
+ if (unlikely(Lex->sphead->spvar_fill_type_reference(thd,
+ $$= $1, $4,
+ $6)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name TYPE_SYM OF_SYM ident '.' ident '.' ident
+ {
+ if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1,
+ $4, $6, $8)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name ROW_SYM TYPE_SYM OF_SYM ident
+ {
+ if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $5)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name ROW_SYM TYPE_SYM OF_SYM ident '.' ident
+ {
+ if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $5, $7)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+sf_c_chistics_and_body_standalone:
+ sp_c_chistics
+ {
+ LEX *lex= thd->lex;
+ lex->sphead->set_c_chistics(lex->sp_chistics);
+ lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_proc_stmt_in_returns_clause
+ {
+ if (unlikely(Lex->sp_body_finalize_function(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_tail_standalone:
+ sp_name
+ {
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1,
+ &sp_handler_procedure,
+ DEFAULT_AGGREGATE)))
+ MYSQL_YYABORT;
+ }
+ sp_parenthesized_pdparam_list
+ sp_c_chistics
+ {
+ Lex->sphead->set_c_chistics(Lex->sp_chistics);
+ Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_proc_stmt
+ {
+ if (unlikely(Lex->sp_body_finalize_procedure(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+drop_routine:
+ DROP FUNCTION_SYM opt_if_exists ident '.' ident
+ {
+ if (Lex->stmt_drop_function($3, $4, $6))
+ MYSQL_YYABORT;
+ }
+ | DROP FUNCTION_SYM opt_if_exists ident
+ {
+ if (Lex->stmt_drop_function($3, $4))
+ MYSQL_YYABORT;
+ }
+ | DROP PROCEDURE_SYM opt_if_exists sp_name
+ {
+ if (Lex->stmt_drop_procedure($3, $4))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+create_routine:
+ create_or_replace definer_opt PROCEDURE_SYM opt_if_not_exists
+ {
+ if (Lex->stmt_create_procedure_start($1 | $4))
+ MYSQL_YYABORT;
+ }
+ sp_tail_standalone
+ {
+ Lex->stmt_create_routine_finalize();
+ }
+ | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sp_parenthesized_fdparam_list
+ RETURNS_SYM sf_return_type
+ sf_c_chistics_and_body_standalone
+ {
+ Lex->stmt_create_routine_finalize();
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ sp_parenthesized_fdparam_list
+ RETURNS_SYM sf_return_type
+ sf_c_chistics_and_body_standalone
+ {
+ Lex->stmt_create_routine_finalize();
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ if (Lex->stmt_create_udf_function($1 | $5, $3, $6,
+ (Item_result) $8, $10))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+sp_decls:
+ _empty
+ {
+ $$.init();
+ }
+ | sp_decls sp_decl ';'
+ {
+ // We check for declarations out of (standard) order this way
+ // because letting the grammar rules reflect it caused tricky
+ // shift/reduce conflicts with the wrong result. (And we get
+ // better error handling this way.)
+ if (unlikely(Lex->sp_declarations_join(&$$, $1, $2)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_decl:
+ DECLARE_MARIADB_SYM sp_decl_body { $$= $2; }
+ ;
+
+
+sp_decl_body:
+ sp_decl_variable_list
+ | sp_decl_ident CONDITION_SYM FOR_SYM sp_cond
+ {
+ if (unlikely(Lex->spcont->declare_condition(thd, &$1, $4)))
+ MYSQL_YYABORT;
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | sp_decl_handler
+ | sp_decl_ident CURSOR_SYM
+ {
+ Lex->sp_block_init(thd);
+ }
+ opt_parenthesized_cursor_formal_parameters
+ FOR_SYM sp_cursor_stmt
+ {
+ sp_pcontext *param_ctx= Lex->spcont;
+ if (unlikely(Lex->sp_block_finalize(thd)))
+ MYSQL_YYABORT;
+ if (unlikely(Lex->sp_declare_cursor(thd, &$1, $6, param_ctx, true)))
+ MYSQL_YYABORT;
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
+
+
+
+// ps_proc_stmt_in_returns_clause is a statement that is allowed
+// in the RETURNS clause of a stored function definition directly,
+// without the BEGIN..END block.
+// It should not include any syntax structures starting with '(', to avoid
+// shift/reduce conflicts with the rule "field_type" and its sub-rules
+// that scan an optional length, like CHAR(1) or YEAR(4).
+// See MDEV-9166.
+
+sp_proc_stmt_in_returns_clause:
+ sp_proc_stmt_return
+ | sp_labeled_block
+ | sp_unlabeled_block
+ | sp_labeled_control
+ | sp_proc_stmt_compound_ok
+ ;
+
+sp_proc_stmt:
+ sp_proc_stmt_in_returns_clause
+ | sp_proc_stmt_statement
+ | sp_proc_stmt_continue_oracle
+ | sp_proc_stmt_exit_oracle
+ | sp_proc_stmt_leave
+ | sp_proc_stmt_iterate
+ | sp_proc_stmt_goto_oracle
+ | sp_proc_stmt_with_cursor
+ ;
+
+sp_proc_stmt_compound_ok:
+ sp_proc_stmt_if
+ | case_stmt_specification
+ | sp_unlabeled_block_not_atomic
+ | sp_unlabeled_control
+ ;
+
+
+sp_labeled_block:
+ sp_block_label
+ BEGIN_MARIADB_SYM
+ {
+ Lex->sp_block_init(thd, &$1);
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ sp_opt_label
+ {
+ if (unlikely(Lex->sp_block_finalize(thd, $4, &$7)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_unlabeled_block:
+ BEGIN_MARIADB_SYM
+ {
+ Lex->sp_block_init(thd);
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ if (unlikely(Lex->sp_block_finalize(thd, $3)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_unlabeled_block_not_atomic:
+ BEGIN_MARIADB_SYM not ATOMIC_SYM // TODO: BEGIN ATOMIC (not -> opt_not)
+ {
+ if (unlikely(Lex->maybe_start_compound_statement(thd)))
+ MYSQL_YYABORT;
+ Lex->sp_block_init(thd);
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ if (unlikely(Lex->sp_block_finalize(thd, $5)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+/* End SQL_MODE_DEFAULT_SPECIFIC */
+
+
+/* Start SQL_MODE_ORACLE_SPECIFIC
+
+statement:
+ verb_clause
+ | set_assign
+ ;
+
+sp_statement:
+ statement
+ | ident_cli_directly_assignable
+ {
+ // Direct procedure call (without the CALL keyword)
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->call_statement_start(thd, &tmp)))
+ MYSQL_YYABORT;
+ }
+ opt_sp_cparam_list
+ | ident_cli_directly_assignable '.' ident
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->call_statement_start(thd, &tmp, &$3)))
+ MYSQL_YYABORT;
+ }
+ opt_sp_cparam_list
+ ;
+
+sp_if_then_statements:
+ sp_proc_stmts1_implicit_block { }
+ ;
+
+sp_case_then_statements:
+ sp_proc_stmts1_implicit_block { }
+ ;
+
+reserved_keyword_udt:
+ reserved_keyword_udt_not_param_type
+ ;
+
+// Keywords that start an SP block section.
+keyword_sp_block_section:
+ BEGIN_ORACLE_SYM
+ | END
+ ;
+
+// Keywords that we allow for labels in SPs.
+// Should not include keywords that start a statement or SP characteristics.
+keyword_label:
+ keyword_data_type
+ | keyword_set_special_case
+ | keyword_sp_var_and_label
+ | keyword_sysvar_type
+ | FUNCTION_SYM
+ | COMPRESSED_SYM
+ | EXCEPTION_ORACLE_SYM
+ ;
+
+keyword_sp_decl:
+ keyword_sp_head
+ | keyword_set_special_case
+ | keyword_sp_var_and_label
+ | keyword_sp_var_not_label
+ | keyword_sysvar_type
+ | keyword_verb_clause
+ | WINDOW_SYM
+ ;
+
+opt_truncate_table_storage_clause:
+ _empty
+ | DROP STORAGE_SYM
+ | REUSE_SYM STORAGE_SYM
+ ;
+
+
+ident_for_loop_index:
+ ident_directly_assignable
+ ;
+
+row_field_name:
+ ident_directly_assignable
+ {
+ if (!($$= Lex->row_field_name(thd, $1)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+while_body:
+ expr_lex LOOP_SYM
+ {
+ if (unlikely($1->sp_while_loop_expression(thd)))
+ MYSQL_YYABORT;
+ }
+ sp_proc_stmts1 END LOOP_SYM
+ {
+ if (unlikely(Lex->sp_while_loop_finalize(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+for_loop_statements:
+ LOOP_SYM sp_proc_stmts1 END LOOP_SYM
+ { }
+ ;
+
+
+sp_control_label:
+ labels_declaration_oracle
+ ;
+
+sp_block_label:
+ labels_declaration_oracle
+ {
+ if (unlikely(Lex->spcont->block_label_declare(&$1)))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
+ ;
+
+
+remember_end_opt:
+ {
+ if (yychar == YYEMPTY)
+ $$= (char*) YYLIP->get_cpp_ptr_rtrim();
+ else
+ $$= (char*) YYLIP->get_cpp_tok_end_rtrim();
+ }
+ ;
+
+sp_opt_default:
+ _empty { $$ = NULL; }
+ | DEFAULT expr { $$ = $2; }
+ | SET_VAR expr { $$ = $2; }
+ ;
+
+sp_opt_inout:
+ _empty { $$= sp_variable::MODE_IN; }
+ | sp_parameter_type
+ | IN_SYM OUT_SYM { $$= sp_variable::MODE_INOUT; }
+ ;
+
+sp_pdparam:
+ sp_param_name sp_opt_inout type_with_opt_collate
+ {
+ $1->mode= $2;
+ if (unlikely(Lex->sp_param_fill_definition($1, $3)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_opt_inout sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
+ {
+ $1->mode= $2;
+ if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $1, $3, $5)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_opt_inout sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
+ {
+ $1->mode= $2;
+ if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $1, $3, $5, $7)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_opt_inout sp_decl_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
+ {
+ $1->mode= $2;
+ if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $1, $3)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_opt_inout sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
+ {
+ $1->mode= $2;
+ if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $1, $3, $5)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_opt_inout ROW_SYM row_type_body
+ {
+ $1->mode= $2;
+ if (unlikely(Lex->sphead->spvar_fill_row(thd, $1, $4)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+sp_proc_stmts1_implicit_block:
+ {
+ Lex->sp_block_init(thd);
+ }
+ sp_proc_stmts1
+ {
+ if (unlikely(Lex->sp_block_finalize(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+remember_lex:
+ {
+ $$= thd->lex;
+ }
+ ;
+
+keyword_directly_assignable:
+ keyword_data_type
+ | keyword_cast_type
+ | keyword_set_special_case
+ | keyword_sp_var_and_label
+ | keyword_sp_var_not_label
+ | keyword_sysvar_type
+ | FUNCTION_SYM
+ | WINDOW_SYM
+ ;
+
+ident_directly_assignable:
+ IDENT_sys
+ | keyword_directly_assignable
+ {
+ if (unlikely($$.copy_keyword(thd, &$1)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+ident_cli_directly_assignable:
+ IDENT_cli
+ | keyword_directly_assignable { $$= $1; }
+ ;
+
+
+set_assign:
+ ident_cli_directly_assignable SET_VAR
+ {
+ LEX *lex=Lex;
+ lex->set_stmt_init();
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(Lex->set_variable(&tmp, $4)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY,
+ false)))
+ MYSQL_YYABORT;
+ }
+ | ident_cli_directly_assignable '.' ident SET_VAR
+ {
+ LEX *lex=Lex;
+ lex->set_stmt_init();
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ LEX *lex= Lex;
+ DBUG_ASSERT(lex->var_list.is_empty());
+ Lex_ident_sys tmp(thd, &$1);
+ if (unlikely(!tmp.str) ||
+ unlikely(lex->set_variable(&tmp, &$3, $6)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY,
+ false)))
+ MYSQL_YYABORT;
+ }
+ | COLON_ORACLE_SYM ident '.' ident SET_VAR
+ {
+ LEX *lex= Lex;
+ if (unlikely(!lex->is_trigger_new_or_old_reference(&$2)))
+ {
+ thd->parse_error(ER_SYNTAX_ERROR, $1.pos());
+ MYSQL_YYABORT;
+ }
+ lex->set_stmt_init();
+ if (sp_create_assignment_lex(thd, $1.pos()))
+ MYSQL_YYABORT;
+ }
+ set_expr_or_default
+ {
+ LEX_CSTRING tmp= { $2.str, $2.length };
+ if (unlikely(Lex->set_trigger_field(&tmp, &$4, $7)) ||
+ unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY,
+ false)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+labels_declaration_oracle:
+ label_declaration_oracle { $$= $1; }
+ | labels_declaration_oracle label_declaration_oracle { $$= $2; }
+ ;
+
+label_declaration_oracle:
+ SHIFT_LEFT label_ident SHIFT_RIGHT
+ {
+ if (unlikely(Lex->sp_push_goto_label(thd, &$2)))
+ MYSQL_YYABORT;
+ $$= $2;
+ }
+ ;
+
+opt_exception_clause:
+ _empty { $$= 0; }
+ | EXCEPTION_ORACLE_SYM exception_handlers { $$= $2; }
+ ;
+
+exception_handlers:
+ exception_handler { $$= 1; }
+ | exception_handlers exception_handler { $$= $1 + 1; }
+ ;
+
+exception_handler:
+ WHEN_SYM
+ {
+ if (unlikely(Lex->sp_handler_declaration_init(thd, sp_handler::EXIT)))
+ MYSQL_YYABORT;
+ }
+ sp_hcond_list
+ THEN_SYM
+ sp_proc_stmts1_implicit_block
+ {
+ if (unlikely(Lex->sp_handler_declaration_finalize(thd, sp_handler::EXIT)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_no_param:
+ _empty
+ {
+ Lex->sphead->m_param_begin= Lex->sphead->m_param_end=
+ YYLIP->get_cpp_tok_start() + 1;
+ }
+ ;
+
+opt_sp_parenthesized_fdparam_list:
+ sp_no_param
+ | sp_parenthesized_fdparam_list
+ ;
+
+opt_sp_parenthesized_pdparam_list:
+ sp_no_param
+ | sp_parenthesized_pdparam_list
+ ;
+
+
+opt_sp_name:
+ _empty { $$= NULL; }
+ | sp_name { $$= $1; }
+ ;
+
+
+opt_package_routine_end_name:
+ _empty { $$= null_clex_str; }
+ | ident { $$= $1; }
+ ;
+
+sp_tail_is:
+ IS
+ | AS
+ ;
+
+sp_instr_addr:
+ { $$= Lex->sphead->instructions(); }
+ ;
+
+sp_body:
+ {
+ Lex->sp_block_init(thd);
+ }
+ opt_sp_decl_body_list
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
+ MYSQL_YYABORT;
+ }
+ BEGIN_ORACLE_SYM
+ sp_block_statements_and_exceptions
+ {
+ $2.hndlrs+= $5.hndlrs;
+ if (unlikely(Lex->sp_block_finalize(thd, $2)))
+ MYSQL_YYABORT;
+ }
+ END
+ ;
+
+create_package_chistic:
+ COMMENT_SYM TEXT_STRING_sys
+ { Lex->sp_chistics.comment= $2; }
+ | sp_suid
+ { Lex->sp_chistics.suid= $1; }
+ ;
+
+create_package_chistics:
+ create_package_chistic {}
+ | create_package_chistics create_package_chistic { }
+ ;
+
+opt_create_package_chistics:
+ _empty
+ | create_package_chistics { }
+ ;
+
+opt_create_package_chistics_init:
+ { Lex->sp_chistics.init(); }
+ opt_create_package_chistics
+ ;
+
+
+package_implementation_executable_section:
+ END
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_add_empty(thd)))
+ MYSQL_YYABORT;
+ $$.init(0);
+ }
+ | BEGIN_ORACLE_SYM sp_block_statements_and_exceptions END { $$= $2; }
+ ;
+
+
+// Inside CREATE PACKAGE BODY, package-wide items (e.g. variables)
+// must be declared before routine definitions.
+
+package_implementation_declare_section:
+ package_implementation_declare_section_list1
+ | package_implementation_declare_section_list2
+ | package_implementation_declare_section_list1
+ package_implementation_declare_section_list2
+ { $$.join($1, $2); }
+ ;
+
+package_implementation_declare_section_list1:
+ package_implementation_item_declaration
+ | package_implementation_declare_section_list1
+ package_implementation_item_declaration
+ { $$.join($1, $2); }
+ ;
+
+package_implementation_declare_section_list2:
+ package_implementation_routine_definition
+ | package_implementation_declare_section_list2
+ package_implementation_routine_definition
+ { $$.join($1, $2); }
+ ;
+
+package_routine_lex:
+ {
+ if (unlikely(!($$= new (thd->mem_root)
+ sp_lex_local(thd, thd->lex))))
+ MYSQL_YYABORT;
+ thd->m_parser_state->m_yacc.reset_before_substatement();
+ }
+ ;
+
+
+package_specification_function:
+ remember_lex package_routine_lex ident
+ {
+ DBUG_ASSERT($1->sphead->get_package());
+ $2->sql_command= SQLCOM_CREATE_FUNCTION;
+ sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
+ if (unlikely(!spname))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ if (unlikely(!$2->make_sp_head_no_recursive(thd, spname,
+ &sp_handler_package_function,
+ NOT_AGGREGATE)))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ (void) is_native_function_with_warn(thd, &$3);
+ }
+ opt_sp_parenthesized_fdparam_list
+ RETURN_ORACLE_SYM sf_return_type
+ sp_c_chistics
+ {
+ sp_head *sp= thd->lex->sphead;
+ sp->restore_thd_mem_root(thd);
+ thd->lex= $1;
+ $$= $2;
+ }
+ ;
+
+package_specification_procedure:
+ remember_lex package_routine_lex ident
+ {
+ DBUG_ASSERT($1->sphead->get_package());
+ $2->sql_command= SQLCOM_CREATE_PROCEDURE;
+ sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
+ if (unlikely(!spname))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ if (unlikely(!$2->make_sp_head_no_recursive(thd, spname,
+ &sp_handler_package_procedure,
+ DEFAULT_AGGREGATE)))
+ MYSQL_YYABORT;
+ $1->sphead->get_package()->m_current_routine= $2;
+ }
+ opt_sp_parenthesized_pdparam_list
+ sp_c_chistics
+ {
+ sp_head *sp= thd->lex->sphead;
+ sp->restore_thd_mem_root(thd);
+ thd->lex= $1;
+ $$= $2;
+ }
+ ;
+
+
+package_implementation_routine_definition:
+ FUNCTION_SYM package_specification_function
+ package_implementation_function_body ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (unlikely(pkg->add_routine_implementation($2)))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ $$.init();
+ }
+ | PROCEDURE_SYM package_specification_procedure
+ package_implementation_procedure_body ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (unlikely(pkg->add_routine_implementation($2)))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ $$.init();
+ }
+ | package_specification_element { $$.init(); }
+ ;
+
+
+package_implementation_function_body:
+ sp_tail_is remember_lex
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ sp_head *sp= pkg->m_current_routine->sphead;
+ thd->lex= pkg->m_current_routine;
+ sp->reset_thd_mem_root(thd);
+ sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_body opt_package_routine_end_name
+ {
+ if (unlikely(thd->lex->sp_body_finalize_function(thd) ||
+ thd->lex->sphead->check_package_routine_end_name($5)))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ }
+ ;
+
+package_implementation_procedure_body:
+ sp_tail_is remember_lex
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ sp_head *sp= pkg->m_current_routine->sphead;
+ thd->lex= pkg->m_current_routine;
+ sp->reset_thd_mem_root(thd);
+ sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_body opt_package_routine_end_name
+ {
+ if (unlikely(thd->lex->sp_body_finalize_procedure(thd) ||
+ thd->lex->sphead->check_package_routine_end_name($5)))
+ MYSQL_YYABORT;
+ thd->lex= $2;
+ }
+ ;
+
+
+package_implementation_item_declaration:
+ sp_decl_variable_list ';'
+ ;
+
+opt_package_specification_element_list:
+ _empty
+ | package_specification_element_list
+ ;
+
+package_specification_element_list:
+ package_specification_element
+ | package_specification_element_list package_specification_element
+ ;
+
+package_specification_element:
+ FUNCTION_SYM package_specification_function ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (unlikely(pkg->add_routine_declaration($2)))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ }
+ | PROCEDURE_SYM package_specification_procedure ';'
+ {
+ sp_package *pkg= Lex->get_sp_package();
+ if (unlikely(pkg->add_routine_declaration($2)))
+ MYSQL_YYABORT;
+ pkg->m_current_routine= NULL;
+ }
+ ;
+
+sp_decl_variable_list_anchored:
+ sp_decl_idents_init_vars
+ optionally_qualified_column_ident PERCENT_ORACLE_SYM TYPE_SYM
+ sp_opt_default
+ {
+ if (unlikely(Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $2, $5)))
+ MYSQL_YYABORT;
+ $$.init_using_vars($1);
+ }
+ | sp_decl_idents_init_vars
+ optionally_qualified_column_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
+ sp_opt_default
+ {
+ if (unlikely(Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $2, $5)))
+ MYSQL_YYABORT;
+ $$.init_using_vars($1);
+ }
+ ;
+
+sp_param_name_and_type_anchored:
+ sp_param_name sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
+ {
+ if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
+ {
+ if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4, $6)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_decl_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
+ {
+ if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2)))
+ MYSQL_YYABORT;
+ }
+ | sp_param_name sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
+ {
+ if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2, $4)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+sf_c_chistics_and_body_standalone:
+ sp_c_chistics
+ {
+ LEX *lex= thd->lex;
+ lex->sphead->set_c_chistics(lex->sp_chistics);
+ lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_tail_is
+ sp_body
+ {
+ if (unlikely(Lex->sp_body_finalize_function(thd)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_tail_standalone:
+ sp_name
+ {
+ if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1,
+ &sp_handler_procedure,
+ DEFAULT_AGGREGATE)))
+ MYSQL_YYABORT;
+ }
+ opt_sp_parenthesized_pdparam_list
+ sp_c_chistics
+ {
+ Lex->sphead->set_c_chistics(Lex->sp_chistics);
+ Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
+ }
+ sp_tail_is
+ sp_body
+ opt_sp_name
+ {
+ if (unlikely(Lex->sp_body_finalize_procedure_standalone(thd, $8)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+drop_routine:
+ DROP FUNCTION_SYM opt_if_exists ident '.' ident
+ {
+ if (Lex->stmt_drop_function($3, $4, $6))
+ MYSQL_YYABORT;
+ }
+ | DROP FUNCTION_SYM opt_if_exists ident
+ {
+ if (Lex->stmt_drop_function($3, $4))
+ MYSQL_YYABORT;
+ }
+ | DROP PROCEDURE_SYM opt_if_exists sp_name
+ {
+ if (Lex->stmt_drop_procedure($3, $4))
+ MYSQL_YYABORT;
+ }
+ | DROP PACKAGE_ORACLE_SYM opt_if_exists sp_name
+ {
+ LEX *lex= Lex;
+ lex->set_command(SQLCOM_DROP_PACKAGE, $3);
+ if (unlikely(lex->sphead))
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE"));
+ lex->spname= $4;
+ }
+ | DROP PACKAGE_ORACLE_SYM BODY_ORACLE_SYM opt_if_exists sp_name
+ {
+ LEX *lex= Lex;
+ lex->set_command(SQLCOM_DROP_PACKAGE_BODY, $4);
+ if (unlikely(lex->sphead))
+ my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE BODY"));
+ lex->spname= $5;
+ }
+ ;
+
+
+create_routine:
+ create_or_replace definer_opt PROCEDURE_SYM opt_if_not_exists
+ {
+ if (Lex->stmt_create_procedure_start($1 | $4))
+ MYSQL_YYABORT;
+ }
+ sp_tail_standalone
+ {
+ Lex->stmt_create_routine_finalize();
+ }
+ | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ opt_sp_parenthesized_fdparam_list
+ RETURN_ORACLE_SYM sf_return_type
+ sf_c_chistics_and_body_standalone
+ opt_sp_name
+ {
+ if (Lex->stmt_create_stored_function_finalize_standalone($12))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ sp_name
+ {
+ if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
+ MYSQL_YYABORT;
+ }
+ opt_sp_parenthesized_fdparam_list
+ RETURN_ORACLE_SYM sf_return_type
+ sf_c_chistics_and_body_standalone
+ opt_sp_name
+ {
+ if (Lex->stmt_create_stored_function_finalize_standalone($12))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
+ ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
+ {
+ if (Lex->stmt_create_udf_function($1 | $5, $3, $6,
+ (Item_result) $8, $10))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace definer_opt PACKAGE_ORACLE_SYM
+ opt_if_not_exists sp_name opt_create_package_chistics_init
+ sp_tail_is
+ remember_name
+ {
+ sp_package *pkg;
+ if (unlikely(!(pkg= Lex->
+ create_package_start(thd,
+ SQLCOM_CREATE_PACKAGE,
+ &sp_handler_package_spec,
+ $5, $1 | $4))))
+ MYSQL_YYABORT;
+ pkg->set_c_chistics(Lex->sp_chistics);
+ }
+ opt_package_specification_element_list END
+ remember_end_opt opt_sp_name
+ {
+ if (unlikely(Lex->create_package_finalize(thd, $5, $13, $8, $12)))
+ MYSQL_YYABORT;
+ }
+ | create_or_replace definer_opt PACKAGE_ORACLE_SYM BODY_ORACLE_SYM
+ opt_if_not_exists sp_name opt_create_package_chistics_init
+ sp_tail_is
+ remember_name
+ {
+ sp_package *pkg;
+ if (unlikely(!(pkg= Lex->
+ create_package_start(thd,
+ SQLCOM_CREATE_PACKAGE_BODY,
+ &sp_handler_package_body,
+ $6, $1 | $5))))
+ MYSQL_YYABORT;
+ pkg->set_c_chistics(Lex->sp_chistics);
+ Lex->sp_block_init(thd);
+ }
+ package_implementation_declare_section
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
+ MYSQL_YYABORT;
+ }
+ package_implementation_executable_section
+ {
+ $11.hndlrs+= $13.hndlrs;
+ if (unlikely(Lex->sp_block_finalize(thd, $11)))
+ MYSQL_YYABORT;
+ }
+ remember_end_opt opt_sp_name
+ {
+ if (unlikely(Lex->create_package_finalize(thd, $6, $16, $9, $15)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+opt_sp_decl_body_list:
+ _empty
+ {
+ $$.init();
+ }
+ | sp_decl_body_list { $$= $1; }
+ ;
+
+sp_decl_body_list:
+ sp_decl_non_handler_list
+ {
+ if (unlikely(Lex->sphead->sp_add_instr_cpush_for_cursors(thd, Lex->spcont)))
+ MYSQL_YYABORT;
+ }
+ opt_sp_decl_handler_list
+ {
+ $$.join($1, $3);
+ }
+ | sp_decl_handler_list
+ ;
+
+sp_decl_non_handler_list:
+ sp_decl_non_handler ';' { $$= $1; }
+ | sp_decl_non_handler_list sp_decl_non_handler ';'
+ {
+ $$.join($1, $2);
+ }
+ ;
+
+sp_decl_handler_list:
+ sp_decl_handler ';' { $$= $1; }
+ | sp_decl_handler_list sp_decl_handler ';'
+ {
+ $$.join($1, $2);
+ }
+ ;
+
+opt_sp_decl_handler_list:
+ _empty { $$.init(); }
+ | sp_decl_handler_list
+ ;
+
+sp_decl_non_handler:
+ sp_decl_variable_list
+ | ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
+ {
+ if (unlikely(Lex->spcont->declare_condition(thd, &$1, $4)))
+ MYSQL_YYABORT;
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | ident_directly_assignable EXCEPTION_ORACLE_SYM
+ {
+ sp_condition_value *spcond= new (thd->mem_root)
+ sp_condition_value_user_defined();
+ if (unlikely(!spcond) ||
+ unlikely(Lex->spcont->declare_condition(thd, &$1, spcond)))
+ MYSQL_YYABORT;
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | CURSOR_SYM ident_directly_assignable
+ {
+ Lex->sp_block_init(thd);
+ }
+ opt_parenthesized_cursor_formal_parameters
+ IS sp_cursor_stmt
+ {
+ sp_pcontext *param_ctx= Lex->spcont;
+ if (unlikely(Lex->sp_block_finalize(thd)))
+ MYSQL_YYABORT;
+ if (unlikely(Lex->sp_declare_cursor(thd, &$2, $6, param_ctx, false)))
+ MYSQL_YYABORT;
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
+
+
+sp_proc_stmt:
+ sp_labeled_block
+ | sp_unlabeled_block
+ | sp_labeled_control
+ | sp_unlabeled_control
+ | sp_labelable_stmt
+ | labels_declaration_oracle sp_labelable_stmt {}
+ ;
+
+sp_labelable_stmt:
+ sp_proc_stmt_statement
+ | sp_proc_stmt_continue_oracle
+ | sp_proc_stmt_exit_oracle
+ | sp_proc_stmt_leave
+ | sp_proc_stmt_iterate
+ | sp_proc_stmt_goto_oracle
+ | sp_proc_stmt_with_cursor
+ | sp_proc_stmt_return
+ | sp_proc_stmt_if
+ | case_stmt_specification
+ | NULL_SYM { }
+ ;
+
+sp_proc_stmt_compound_ok:
+ sp_proc_stmt_if
+ | case_stmt_specification
+ | sp_unlabeled_block
+ | sp_unlabeled_control
+ ;
+
+
+sp_labeled_block:
+ sp_block_label
+ BEGIN_ORACLE_SYM
+ {
+ Lex->sp_block_init(thd, &$1);
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
+ MYSQL_YYABORT;
+ }
+ sp_block_statements_and_exceptions
+ END
+ sp_opt_label
+ {
+ if (unlikely(Lex->sp_block_finalize(thd, Lex_spblock($4), &$6)))
+ MYSQL_YYABORT;
+ }
+ | sp_block_label
+ DECLARE_ORACLE_SYM
+ {
+ Lex->sp_block_init(thd, &$1);
+ }
+ opt_sp_decl_body_list
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
+ MYSQL_YYABORT;
+ }
+ BEGIN_ORACLE_SYM
+ sp_block_statements_and_exceptions
+ END
+ sp_opt_label
+ {
+ $4.hndlrs+= $7.hndlrs;
+ if (unlikely(Lex->sp_block_finalize(thd, $4, &$9)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+opt_not_atomic:
+ _empty
+ | not ATOMIC_SYM // TODO: BEGIN ATOMIC (not -> opt_not)
+ ;
+
+sp_unlabeled_block:
+ BEGIN_ORACLE_SYM opt_not_atomic
+ {
+ if (unlikely(Lex->maybe_start_compound_statement(thd)))
+ MYSQL_YYABORT;
+ Lex->sp_block_init(thd);
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
+ MYSQL_YYABORT;
+ }
+ sp_block_statements_and_exceptions
+ END
+ {
+ if (unlikely(Lex->sp_block_finalize(thd, Lex_spblock($4))))
+ MYSQL_YYABORT;
+ }
+ | DECLARE_ORACLE_SYM
+ {
+ if (unlikely(Lex->maybe_start_compound_statement(thd)))
+ MYSQL_YYABORT;
+ Lex->sp_block_init(thd);
+ }
+ opt_sp_decl_body_list
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
+ MYSQL_YYABORT;
+ }
+ BEGIN_ORACLE_SYM
+ sp_block_statements_and_exceptions
+ END
+ {
+ $3.hndlrs+= $6.hndlrs;
+ if (unlikely(Lex->sp_block_finalize(thd, $3)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+sp_block_statements_and_exceptions:
+ sp_instr_addr
+ sp_proc_stmts
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_executable_section(thd, $1)))
+ MYSQL_YYABORT;
+ }
+ opt_exception_clause
+ {
+ if (unlikely(Lex->sp_block_with_exceptions_finalize_exceptions(thd, $1, $4)))
+ MYSQL_YYABORT;
+ $$.init($4);
+ }
+ ;
+
+End SQL_MODE_ORACLE_SPECIFIC */
+
/**
@} (end of group Parser)
*/
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
deleted file mode 100644
index a7b1b880943..00000000000
--- a/sql/sql_yacc_ora.yy
+++ /dev/null
@@ -1,18429 +0,0 @@
-/*
- Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2020, 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
- 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-1335 USA */
-
-/* sql_yacc.yy */
-
-/**
- @defgroup Parser Parser
- @{
-*/
-
-%{
-#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
-#define YYMAXDEPTH 3200 /* Because of 64K stack */
-#define Lex (thd->lex)
-
-#define Select Lex->current_select
-#include "mariadb.h"
-#include "sql_priv.h"
-#include "sql_parse.h" /* comp_*_creator */
-#include "sql_table.h" /* primary_key_name */
-#include "sql_partition.h" /* partition_info, HASH_PARTITION */
-#include "sql_acl.h" /* *_ACL */
-#include "sql_class.h" /* Key_part_spec, enum_filetype, Diag_condition_item_name */
-#include "slave.h"
-#include "lex_symbol.h"
-#include "item_create.h"
-#include "sp_head.h"
-#include "sp_rcontext.h"
-#include "sp.h"
-#include "sql_show.h"
-#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 "sql_cte.h"
-#include "sql_window.h"
-#include "item_windowfunc.h"
-#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"
-#include "sql_lex.h"
-#include "sql_sequence.h"
-#include "my_base.h"
-#include "sql_type_json.h"
-
-/* this is to get the bison compilation windows warnings out */
-#ifdef _MSC_VER
-/* warning C4065: switch statement contains 'default' but no 'case' labels */
-#pragma warning (disable : 4065)
-#endif
-
-int yylex(void *yylval, void *yythd);
-
-#define yyoverflow(A,B,C,D,E,F) \
- { \
- size_t val= *(F); \
- if (unlikely(my_yyoverflow((B), (D), &val))) \
- { \
- yyerror(thd, (char*) (A)); \
- return 2; \
- } \
- else \
- { \
- *(F)= (YYSIZE_T)val; \
- } \
- }
-
-#define MYSQL_YYABORT \
- do \
- { \
- LEX::cleanup_lex_after_parse_error(thd); \
- YYABORT; \
- } while (0)
-
-#define MYSQL_YYABORT_UNLESS(A) \
- if (unlikely(!(A))) \
- { \
- thd->parse_error(); \
- MYSQL_YYABORT; \
- }
-
-#define my_yyabort_error(A) \
- do { my_error A; MYSQL_YYABORT; } while(0)
-
-#ifndef DBUG_OFF
-#define YYDEBUG 1
-#else
-#define YYDEBUG 0
-#endif
-
-
-static Item* escape(THD *thd)
-{
- thd->lex->escape_used= false;
- const char *esc= thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES ? "" : "\\";
- return new (thd->mem_root) Item_string_ascii(thd, esc, MY_TEST(esc[0]));
-}
-
-
-/**
- @brief Bison callback to report a syntax/OOM error
-
- This function is invoked by the bison-generated parser
- when a syntax error, a parse error or an out-of-memory
- condition occurs. This function is not invoked when the
- parser is requested to abort by semantic action code
- by means of YYABORT or YYACCEPT macros. This is why these
- macros should not be used (use MYSQL_YYABORT/MYSQL_YYACCEPT
- instead).
-
- The parser will abort immediately after invoking this callback.
-
- This function is not for use in semantic actions and is internal to
- the parser, as it performs some pre-return cleanup.
- In semantic actions, please use thd->parse_error() or my_error to
- push an error into the error stack and MYSQL_YYABORT
- to abort from the parser.
-*/
-
-void ORAerror(THD *thd, const char *s)
-{
- /*
- Restore the original LEX if it was replaced when parsing
- a stored procedure. We must ensure that a parsing error
- does not leave any side effects in the THD.
- */
- LEX::cleanup_lex_after_parse_error(thd);
-
- /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */
- if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0)
- s= ER_THD(thd, ER_SYNTAX_ERROR);
- thd->parse_error(s, 0);
-}
-
-
-
-
-#define bincmp_collation(X,Y) \
- do \
- { \
- if (unlikely(Lex->set_bincmp(X,Y))) \
- MYSQL_YYABORT; \
- } while(0)
-
-%}
-%union {
- int num;
- ulong ulong_num;
- ulonglong ulonglong_number;
- longlong longlong_number;
- uint sp_instr_addr;
-
- /* structs */
- LEX_CSTRING lex_str;
- Lex_ident_cli_st kwd;
- Lex_ident_cli_st ident_cli;
- Lex_ident_sys_st ident_sys;
- Lex_string_with_metadata_st lex_string_with_metadata;
- Lex_spblock_st spblock;
- Lex_spblock_handlers_st spblock_handlers;
- Lex_length_and_dec_st Lex_length_and_dec;
- Lex_cast_type_st Lex_cast_type;
- Lex_field_type_st Lex_field_type;
- Lex_dyncol_type_st Lex_dyncol_type;
- Lex_for_loop_st for_loop;
- Lex_for_loop_bounds_st for_loop_bounds;
- Lex_trim_st trim;
- vers_history_point_t vers_history_point;
- struct
- {
- enum sub_select_type unit_type;
- bool distinct;
- } unit_operation;
- struct
- {
- SELECT_LEX *first;
- SELECT_LEX *prev_last;
- } select_list;
- SQL_I_List<ORDER> *select_order;
- Lex_select_lock select_lock;
- Lex_select_limit select_limit;
- Lex_order_limit_lock *order_limit_lock;
-
- /* pointers */
- Create_field *create_field;
- Spvar_definition *spvar_definition;
- Row_definition_list *spvar_definition_list;
- const Type_handler *type_handler;
- CHARSET_INFO *charset;
- Condition_information_item *cond_info_item;
- DYNCALL_CREATE_DEF *dyncol_def;
- Diagnostics_information *diag_info;
- Item *item;
- Item_num *item_num;
- Item_param *item_param;
- Item_basic_constant *item_basic_constant;
- Key_part_spec *key_part;
- LEX *lex;
- sp_assignment_lex *assignment_lex;
- class sp_lex_cursor *sp_cursor_stmt;
- LEX_CSTRING *lex_str_ptr;
- LEX_USER *lex_user;
- USER_AUTH *user_auth;
- List<Condition_information_item> *cond_info_list;
- List<DYNCALL_CREATE_DEF> *dyncol_def_list;
- List<Item> *item_list;
- List<sp_assignment_lex> *sp_assignment_lex_list;
- List<Statement_information_item> *stmt_info_list;
- List<String> *string_list;
- List<LEX_CSTRING> *lex_str_list;
- Statement_information_item *stmt_info_item;
- String *string;
- TABLE_LIST *table_list;
- Table_ident *table;
- Qualified_column_ident *qualified_column_ident;
- char *simple_string;
- const char *const_simple_string;
- chooser_compare_func_creator boolfunc2creator;
- class my_var *myvar;
- class sp_condition_value *spcondvalue;
- class sp_head *sphead;
- class sp_name *spname;
- class sp_variable *spvar;
- class With_clause *with_clause;
- class Virtual_column_info *virtual_column;
-
- handlerton *db_type;
- st_select_lex *select_lex;
- st_select_lex_unit *select_lex_unit;
- struct p_elem_val *p_elem_value;
- class Window_frame *window_frame;
- class Window_frame_bound *window_frame_bound;
- udf_func *udf;
- st_trg_execution_order trg_execution_order;
-
- /* enums */
- enum enum_sp_suid_behaviour sp_suid;
- enum enum_sp_aggregate_type sp_aggregate_type;
- enum enum_view_suid view_suid;
- enum Condition_information_item::Name cond_info_item_name;
- enum enum_diag_condition_item_name diag_condition_item_name;
- enum Diagnostics_information::Which_area diag_area;
- enum Field::geometry_type geom_type;
- enum enum_fk_option m_fk_option;
- enum Item_udftype udf_type;
- enum Key::Keytype key_type;
- enum Statement_information_item::Name stmt_info_item_name;
- enum enum_filetype filetype;
- enum enum_tx_isolation tx_isolation;
- enum enum_var_type var_type;
- enum enum_yes_no_unknown m_yes_no_unk;
- enum ha_choice choice;
- enum ha_key_alg key_alg;
- enum ha_rkey_function ha_rkey_mode;
- enum index_hint_type index_hint;
- enum interval_type interval, interval_time_st;
- enum row_type row_type;
- enum sp_variable::enum_mode spvar_mode;
- enum thr_lock_type lock_type;
- enum enum_mysql_timestamp_type date_time_type;
- enum Window_frame_bound::Bound_precedence_type bound_precedence_type;
- enum Window_frame::Frame_units frame_units;
- enum Window_frame::Frame_exclusion frame_exclusion;
- enum trigger_order_type trigger_action_order_type;
- DDL_options_st object_ddl_options;
- enum vers_sys_type_t vers_range_unit;
- enum Column_definition::enum_column_versioning vers_column_versioning;
- enum plsql_cursor_attr_t plsql_cursor_attr;
-}
-
-%{
-bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
-%}
-
-%pure-parser /* We have threads */
-%parse-param { THD *thd }
-%lex-param { THD *thd }
-/*
- We should not introduce any further shift/reduce conflicts.
-*/
-%expect 57
-
-/*
- Comments for TOKENS.
- For each token, please include in the same line a comment that contains
- the following tags:
- SQL-2011-R : Reserved keyword as per SQL-2011
- SQL-2011-N : Non Reserved keyword as per SQL-2011
- SQL-2003-R : Reserved keyword as per SQL-2003
- SQL-2003-N : Non Reserved keyword as per SQL-2003
- SQL-1999-R : Reserved keyword as per SQL-1999
- SQL-1999-N : Non Reserved keyword as per SQL-1999
- MYSQL : MySQL extention (unspecified)
- MYSQL-FUNC : MySQL extention, function
- INTERNAL : Not a real token, lex optimization
- OPERATOR : SQL operator
- FUTURE-USE : Reserved for future use
-
- This makes the code grep-able, and helps maintenance.
-*/
-
-
-/*
- Reserved keywords and operators
-*/
-%token ABORT_SYM /* INTERNAL (used in lex) */
-%token ACCESSIBLE_SYM
-%token ADD /* SQL-2003-R */
-%token ALL /* SQL-2003-R */
-%token ALTER /* SQL-2003-R */
-%token ANALYZE_SYM
-%token AND_AND_SYM /* OPERATOR */
-%token AND_SYM /* SQL-2003-R */
-%token AS /* SQL-2003-R */
-%token ASC /* SQL-2003-N */
-%token ASENSITIVE_SYM /* FUTURE-USE */
-%token BEFORE_SYM /* SQL-2003-N */
-%token BETWEEN_SYM /* SQL-2003-R */
-%token BIGINT /* SQL-2003-R */
-%token BINARY /* SQL-2003-R */
-%token BIN_NUM
-%token BIT_AND /* MYSQL-FUNC */
-%token BIT_OR /* MYSQL-FUNC */
-%token BIT_XOR /* MYSQL-FUNC */
-%token BLOB_MARIADB_SYM /* SQL-2003-R */
-%token BLOB_ORACLE_SYM /* Oracle-R */
-%token BODY_ORACLE_SYM /* Oracle-R */
-%token BOTH /* SQL-2003-R */
-%token BY /* SQL-2003-R */
-%token CALL_SYM /* SQL-2003-R */
-%token CASCADE /* SQL-2003-N */
-%token CASE_SYM /* SQL-2003-R */
-%token CAST_SYM /* SQL-2003-R */
-%token CHANGE
-%token CHAR_SYM /* SQL-2003-R */
-%token CHECK_SYM /* SQL-2003-R */
-%token COLLATE_SYM /* SQL-2003-R */
-%token CONDITION_SYM /* SQL-2003-R, SQL-2008-R */
-%token CONSTRAINT /* SQL-2003-R */
-%token CONTINUE_MARIADB_SYM /* SQL-2003-R, Oracle-R */
-%token CONTINUE_ORACLE_SYM /* SQL-2003-R, Oracle-R */
-%token CONVERT_SYM /* SQL-2003-N */
-%token COUNT_SYM /* SQL-2003-N */
-%token CREATE /* SQL-2003-R */
-%token CROSS /* SQL-2003-R */
-%token CUME_DIST_SYM
-%token CURDATE /* MYSQL-FUNC */
-%token CURRENT_USER /* SQL-2003-R */
-%token CURRENT_ROLE /* SQL-2003-R */
-%token CURSOR_SYM /* SQL-2003-R */
-%token CURTIME /* MYSQL-FUNC */
-%token DATABASE
-%token DATABASES
-%token DATE_ADD_INTERVAL /* MYSQL-FUNC */
-%token DATE_SUB_INTERVAL /* MYSQL-FUNC */
-%token DAY_HOUR_SYM
-%token DAY_MICROSECOND_SYM
-%token DAY_MINUTE_SYM
-%token DAY_SECOND_SYM
-%token DECIMAL_NUM
-%token DECIMAL_SYM /* SQL-2003-R */
-%token DECLARE_MARIADB_SYM /* SQL-2003-R */
-%token DECLARE_ORACLE_SYM /* Oracle-R */
-%token DEFAULT /* SQL-2003-R */
-%token DELETE_DOMAIN_ID_SYM
-%token DELETE_SYM /* SQL-2003-R */
-%token DENSE_RANK_SYM
-%token DESC /* SQL-2003-N */
-%token DESCRIBE /* SQL-2003-R */
-%token DETERMINISTIC_SYM /* SQL-2003-R */
-%token DISTINCT /* SQL-2003-R */
-%token DIV_SYM
-%token DOUBLE_SYM /* SQL-2003-R */
-%token DO_DOMAIN_IDS_SYM
-%token DOT_DOT_SYM
-%token DROP /* SQL-2003-R */
-%token DUAL_SYM
-%token EACH_SYM /* SQL-2003-R */
-%token ELSE /* SQL-2003-R */
-%token ELSEIF_MARIADB_SYM
-%token ELSIF_ORACLE_SYM /* PLSQL-R */
-%token ENCLOSED
-%token END_OF_INPUT /* INTERNAL */
-%token EQUAL_SYM /* OPERATOR */
-%token ESCAPED
-%token EXCEPT_SYM /* SQL-2003-R */
-%token EXISTS /* SQL-2003-R */
-%token EXTRACT_SYM /* SQL-2003-N */
-%token FALSE_SYM /* SQL-2003-R */
-%token FETCH_SYM /* SQL-2003-R */
-%token FIRST_VALUE_SYM /* SQL-2011 */
-%token FLOAT_NUM
-%token FLOAT_SYM /* SQL-2003-R */
-%token FOREIGN /* SQL-2003-R */
-%token FOR_SYM /* SQL-2003-R */
-%token FOR_SYSTEM_TIME_SYM /* INTERNAL */
-%token FROM
-%token FULLTEXT_SYM
-%token GE
-%token GOTO_ORACLE_SYM /* Oracle-R */
-%token GRANT /* SQL-2003-R */
-%token GROUP_SYM /* SQL-2003-R */
-%token GROUP_CONCAT_SYM
-%token LAG_SYM /* SQL-2011 */
-%token LEAD_SYM /* SQL-2011 */
-%token HAVING /* SQL-2003-R */
-%token HEX_NUM
-%token HEX_STRING
-%token HOUR_MICROSECOND_SYM
-%token HOUR_MINUTE_SYM
-%token HOUR_SECOND_SYM
-%token IDENT
-%token IDENT_QUOTED
-%token IF_SYM
-%token IGNORE_DOMAIN_IDS_SYM
-%token IGNORE_SYM
-%token INDEX_SYM
-%token INFILE
-%token INNER_SYM /* SQL-2003-R */
-%token INOUT_SYM /* SQL-2003-R */
-%token INSENSITIVE_SYM /* SQL-2003-R */
-%token INSERT /* SQL-2003-R */
-%token INTERSECT_SYM /* SQL-2003-R */
-%token INTERVAL_SYM /* SQL-2003-R */
-%token INTO /* SQL-2003-R */
-%token INT_SYM /* SQL-2003-R */
-%token IN_SYM /* SQL-2003-R */
-%token IS /* SQL-2003-R */
-%token ITERATE_SYM
-%token JOIN_SYM /* SQL-2003-R */
-%token KEYS
-%token KEY_SYM /* SQL-2003-N */
-%token KILL_SYM
-%token LE /* OPERATOR */
-%token LEADING /* SQL-2003-R */
-%token LEAVE_SYM
-%token LEFT /* SQL-2003-R */
-%token LEFT_PAREN_ALT /* INTERNAL */
-%token LEFT_PAREN_WITH /* INTERNAL */
-%token LEFT_PAREN_LIKE /* INTERNAL */
-%token LEX_HOSTNAME
-%token LIKE /* SQL-2003-R */
-%token LIMIT
-%token LINEAR_SYM
-%token LINES
-%token LOAD
-%token LOCATOR_SYM /* SQL-2003-N */
-%token LOCK_SYM
-%token LONGBLOB
-%token LONGTEXT
-%token LONG_NUM
-%token LONG_SYM
-%token LOOP_SYM
-%token LOW_PRIORITY
-%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
-%token MATCH /* SQL-2003-R */
-%token MAX_SYM /* SQL-2003-N */
-%token MAXVALUE_SYM /* SQL-2003-N */
-%token MEDIAN_SYM
-%token MEDIUMBLOB
-%token MEDIUMINT
-%token MEDIUMTEXT
-%token MINUTE_MICROSECOND_SYM
-%token MINUTE_SECOND_SYM
-%token MIN_SYM /* SQL-2003-N */
-%token MODIFIES_SYM /* SQL-2003-R */
-%token MOD_SYM /* SQL-2003-N */
-%token MYSQL_CONCAT_SYM /* OPERATOR */
-%token NATURAL /* SQL-2003-R */
-%token NCHAR_STRING
-%token NE /* OPERATOR */
-%token NEG
-%token NOT2_SYM
-%token NOT_SYM /* SQL-2003-R */
-%token NOW_SYM
-%token NO_WRITE_TO_BINLOG
-%token NTILE_SYM
-%token NULL_SYM /* SQL-2003-R */
-%token NUM
-%token NUMERIC_SYM /* SQL-2003-R */
-%token NTH_VALUE_SYM /* SQL-2011 */
-%token ON /* SQL-2003-R */
-%token OPTIMIZE
-%token OPTIONALLY
-%token ORACLE_CONCAT_SYM /* INTERNAL */
-%token OR2_SYM
-%token ORDER_SYM /* SQL-2003-R */
-%token OR_SYM /* SQL-2003-R */
-%token OTHERS_ORACLE_SYM /* SQL-2011-N, PLSQL-R */
-%token OUTER
-%token OUTFILE
-%token OUT_SYM /* SQL-2003-R */
-%token OVER_SYM
-%token PACKAGE_ORACLE_SYM /* Oracle-R */
-%token PAGE_CHECKSUM_SYM
-%token PARAM_MARKER
-%token PARSE_VCOL_EXPR_SYM
-%token PARTITION_SYM /* SQL-2003-R */
-%token PERCENT_ORACLE_SYM /* INTERNAL */
-%token PERCENT_RANK_SYM
-%token PERCENTILE_CONT_SYM
-%token PERCENTILE_DISC_SYM
-%token PORTION_SYM /* SQL-2016-R */
-%token POSITION_SYM /* SQL-2003-N */
-%token PRECISION /* SQL-2003-R */
-%token PRIMARY_SYM /* SQL-2003-R */
-%token PROCEDURE_SYM /* SQL-2003-R */
-%token PURGE
-%token RAISE_ORACLE_SYM /* PLSQL-R */
-%token RANGE_SYM /* SQL-2003-R */
-%token RANK_SYM
-%token READS_SYM /* SQL-2003-R */
-%token READ_SYM /* SQL-2003-N */
-%token READ_WRITE_SYM
-%token REAL /* SQL-2003-R */
-%token RECURSIVE_SYM
-%token REF_SYSTEM_ID_SYM
-%token REFERENCES /* SQL-2003-R */
-%token REGEXP
-%token RELEASE_SYM /* SQL-2003-R */
-%token RENAME
-%token REPEAT_SYM /* MYSQL-FUNC */
-%token REPLACE /* MYSQL-FUNC */
-%token REQUIRE_SYM
-%token RESIGNAL_SYM /* SQL-2003-R */
-%token RESTRICT
-%token RETURNING_SYM
-%token RETURN_MARIADB_SYM /* SQL-2003-R, PLSQL-R */
-%token RETURN_ORACLE_SYM /* SQL-2003-R, PLSQL-R */
-%token REVOKE /* SQL-2003-R */
-%token RIGHT /* SQL-2003-R */
-%token ROWS_SYM /* SQL-2003-R */
-%token ROWTYPE_ORACLE_SYM /* PLSQL-R */
-%token ROW_NUMBER_SYM
-%token SECOND_MICROSECOND_SYM
-%token SELECT_SYM /* SQL-2003-R */
-%token SENSITIVE_SYM /* FUTURE-USE */
-%token SEPARATOR_SYM
-%token SERVER_OPTIONS
-%token SET /* SQL-2003-R */
-%token SET_VAR
-%token SHIFT_LEFT /* OPERATOR */
-%token SHIFT_RIGHT /* OPERATOR */
-%token SHOW
-%token SIGNAL_SYM /* SQL-2003-R */
-%token SMALLINT /* SQL-2003-R */
-%token SPATIAL_SYM
-%token SPECIFIC_SYM /* SQL-2003-R */
-%token SQLEXCEPTION_SYM /* SQL-2003-R */
-%token SQLSTATE_SYM /* SQL-2003-R */
-%token SQLWARNING_SYM /* SQL-2003-R */
-%token SQL_BIG_RESULT
-%token SQL_SMALL_RESULT
-%token SQL_SYM /* SQL-2003-R */
-%token SSL_SYM
-%token STARTING
-%token STATS_AUTO_RECALC_SYM
-%token STATS_PERSISTENT_SYM
-%token STATS_SAMPLE_PAGES_SYM
-%token STDDEV_SAMP_SYM /* SQL-2003-N */
-%token STD_SYM
-%token STRAIGHT_JOIN
-%token SUBSTRING /* SQL-2003-N */
-%token SUM_SYM /* SQL-2003-N */
-%token SYSDATE
-%token TABLE_REF_PRIORITY
-%token TABLE_SYM /* SQL-2003-R */
-%token TERMINATED
-%token TEXT_STRING
-%token THEN_SYM /* SQL-2003-R */
-%token TINYBLOB
-%token TINYINT
-%token TINYTEXT
-%token TO_SYM /* SQL-2003-R */
-%token TRAILING /* SQL-2003-R */
-%token TRIGGER_SYM /* SQL-2003-R */
-%token TRIM /* SQL-2003-N */
-%token TRUE_SYM /* SQL-2003-R */
-%token ULONGLONG_NUM
-%token UNDERSCORE_CHARSET
-%token UNDO_SYM /* FUTURE-USE */
-%token UNION_SYM /* SQL-2003-R */
-%token UNIQUE_SYM
-%token UNLOCK_SYM
-%token UNSIGNED
-%token UPDATE_SYM /* SQL-2003-R */
-%token USAGE /* SQL-2003-N */
-%token USE_SYM
-%token USING /* SQL-2003-R */
-%token UTC_DATE_SYM
-%token UTC_TIMESTAMP_SYM
-%token UTC_TIME_SYM
-%token VALUES /* SQL-2003-R */
-%token VALUES_IN_SYM
-%token VALUES_LESS_SYM
-%token VARBINARY
-%token VARCHAR /* SQL-2003-R */
-%token VARIANCE_SYM
-%token VARYING /* SQL-2003-R */
-%token VAR_SAMP_SYM
-%token WHEN_SYM /* SQL-2003-R */
-%token WHERE /* SQL-2003-R */
-%token WHILE_SYM
-%token WITH /* SQL-2003-R */
-%token WITH_CUBE_SYM /* INTERNAL */
-%token WITH_ROLLUP_SYM /* INTERNAL */
-%token WITH_SYSTEM_SYM /* INTERNAL */
-%token XOR
-%token YEAR_MONTH_SYM
-%token ZEROFILL
-
-%token IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */
-
-
-/*
- Keywords that have different reserved status in std/oracle modes.
-*/
-%token <kwd> BODY_MARIADB_SYM // Oracle-R
-%token <kwd> ELSEIF_ORACLE_SYM
-%token <kwd> ELSIF_MARIADB_SYM // PLSQL-R
-%token <kwd> EXCEPTION_ORACLE_SYM // SQL-2003-N, PLSQL-R
-%token <kwd> GOTO_MARIADB_SYM // Oracle-R
-%token <kwd> OTHERS_MARIADB_SYM // SQL-2011-N, PLSQL-R
-%token <kwd> PACKAGE_MARIADB_SYM // Oracle-R
-%token <kwd> RAISE_MARIADB_SYM // PLSQL-R
-%token <kwd> ROWTYPE_MARIADB_SYM // PLSQL-R
-
-/*
- Non-reserved keywords
-*/
-
-%token <kwd> ACCOUNT_SYM /* MYSQL */
-%token <kwd> ACTION /* SQL-2003-N */
-%token <kwd> ADMIN_SYM /* SQL-2003-N */
-%token <kwd> ADDDATE_SYM /* MYSQL-FUNC */
-%token <kwd> AFTER_SYM /* SQL-2003-N */
-%token <kwd> AGAINST
-%token <kwd> AGGREGATE_SYM
-%token <kwd> ALGORITHM_SYM
-%token <kwd> ALWAYS_SYM
-%token <kwd> ANY_SYM /* SQL-2003-R */
-%token <kwd> ASCII_SYM /* MYSQL-FUNC */
-%token <kwd> AT_SYM /* SQL-2003-R */
-%token <kwd> ATOMIC_SYM /* SQL-2003-R */
-%token <kwd> AUTHORS_SYM
-%token <kwd> AUTOEXTEND_SIZE_SYM
-%token <kwd> AUTO_INC
-%token <kwd> AUTO_SYM
-%token <kwd> AVG_ROW_LENGTH
-%token <kwd> AVG_SYM /* SQL-2003-N */
-%token <kwd> BACKUP_SYM
-%token <kwd> BEGIN_MARIADB_SYM /* SQL-2003-R, PLSQL-R */
-%token <kwd> BEGIN_ORACLE_SYM /* SQL-2003-R, PLSQL-R */
-%token <kwd> BINLOG_SYM
-%token <kwd> BIT_SYM /* MYSQL-FUNC */
-%token <kwd> BLOCK_SYM
-%token <kwd> BOOL_SYM
-%token <kwd> BOOLEAN_SYM /* SQL-2003-R, PLSQL-R */
-%token <kwd> BTREE_SYM
-%token <kwd> BYTE_SYM
-%token <kwd> CACHE_SYM
-%token <kwd> CASCADED /* SQL-2003-R */
-%token <kwd> CATALOG_NAME_SYM /* SQL-2003-N */
-%token <kwd> CHAIN_SYM /* SQL-2003-N */
-%token <kwd> CHANGED
-%token <kwd> CHARSET
-%token <kwd> CHECKPOINT_SYM
-%token <kwd> CHECKSUM_SYM
-%token <kwd> CIPHER_SYM
-%token <kwd> CLASS_ORIGIN_SYM /* SQL-2003-N */
-%token <kwd> CLIENT_SYM
-%token <kwd> CLOB_MARIADB_SYM /* SQL-2003-R */
-%token <kwd> CLOB_ORACLE_SYM /* Oracle-R */
-%token <kwd> CLOSE_SYM /* SQL-2003-R */
-%token <kwd> COALESCE /* SQL-2003-N */
-%token <kwd> CODE_SYM
-%token <kwd> COLLATION_SYM /* SQL-2003-N */
-%token <kwd> COLON_ORACLE_SYM /* INTERNAL */
-%token <kwd> COLUMNS
-%token <kwd> COLUMN_ADD_SYM
-%token <kwd> COLUMN_CHECK_SYM
-%token <kwd> COLUMN_CREATE_SYM
-%token <kwd> COLUMN_DELETE_SYM
-%token <kwd> COLUMN_GET_SYM
-%token <kwd> COLUMN_SYM /* SQL-2003-R */
-%token <kwd> COLUMN_NAME_SYM /* SQL-2003-N */
-%token <kwd> COMMENT_SYM /* Oracle-R */
-%token <kwd> COMMITTED_SYM /* SQL-2003-N */
-%token <kwd> COMMIT_SYM /* SQL-2003-R */
-%token <kwd> COMPACT_SYM
-%token <kwd> COMPLETION_SYM
-%token <kwd> COMPRESSED_SYM
-%token <kwd> CONCURRENT
-%token <kwd> CONNECTION_SYM
-%token <kwd> CONSISTENT_SYM
-%token <kwd> CONSTRAINT_CATALOG_SYM /* SQL-2003-N */
-%token <kwd> CONSTRAINT_NAME_SYM /* SQL-2003-N */
-%token <kwd> CONSTRAINT_SCHEMA_SYM /* SQL-2003-N */
-%token <kwd> CONTAINS_SYM /* SQL-2003-N */
-%token <kwd> CONTEXT_SYM
-%token <kwd> CONTRIBUTORS_SYM
-%token <kwd> CPU_SYM
-%token <kwd> CUBE_SYM /* SQL-2003-R */
-%token <kwd> CURRENT_SYM /* SQL-2003-R */
-%token <kwd> CURRENT_POS_SYM
-%token <kwd> CURSOR_NAME_SYM /* SQL-2003-N */
-%token <kwd> CYCLE_SYM
-%token <kwd> DATAFILE_SYM
-%token <kwd> DATA_SYM /* SQL-2003-N */
-%token <kwd> DATETIME
-%token <kwd> DATE_FORMAT_SYM /* MYSQL-FUNC */
-%token <kwd> DATE_SYM /* SQL-2003-R, Oracle-R, PLSQL-R */
-%token <kwd> DAY_SYM /* SQL-2003-R */
-%token <kwd> DEALLOCATE_SYM /* SQL-2003-R */
-%token <kwd> DECODE_MARIADB_SYM /* Function, non-reserved */
-%token <kwd> DECODE_ORACLE_SYM /* Function, non-reserved */
-%token <kwd> DEFINER_SYM
-%token <kwd> DELAYED_SYM
-%token <kwd> DELAY_KEY_WRITE_SYM
-%token <kwd> DES_KEY_FILE
-%token <kwd> DIAGNOSTICS_SYM /* SQL-2003-N */
-%token <kwd> DIRECTORY_SYM
-%token <kwd> DISABLE_SYM
-%token <kwd> DISCARD
-%token <kwd> DISK_SYM
-%token <kwd> DO_SYM
-%token <kwd> DUMPFILE
-%token <kwd> DUPLICATE_SYM
-%token <kwd> DYNAMIC_SYM /* SQL-2003-R */
-%token <kwd> ENABLE_SYM
-%token <kwd> END /* SQL-2003-R, PLSQL-R */
-%token <kwd> ENDS_SYM
-%token <kwd> ENGINES_SYM
-%token <kwd> ENGINE_SYM
-%token <kwd> ENUM
-%token <kwd> ERROR_SYM
-%token <kwd> ERRORS
-%token <kwd> ESCAPE_SYM /* SQL-2003-R */
-%token <kwd> EVENTS_SYM
-%token <kwd> EVENT_SYM
-%token <kwd> EVERY_SYM /* SQL-2003-N */
-%token <kwd> EXCHANGE_SYM
-%token <kwd> EXAMINED_SYM
-%token <kwd> EXCLUDE_SYM /* SQL-2011-N */
-%token <kwd> EXECUTE_SYM /* SQL-2003-R */
-%token <kwd> EXCEPTION_MARIADB_SYM /* SQL-2003-N, PLSQL-R */
-%token <kwd> EXIT_MARIADB_SYM /* PLSQL-R */
-%token <kwd> EXIT_ORACLE_SYM /* PLSQL-R */
-%token <kwd> EXPANSION_SYM
-%token <kwd> EXPIRE_SYM /* MySQL */
-%token <kwd> EXPORT_SYM
-%token <kwd> EXTENDED_SYM
-%token <kwd> EXTENT_SIZE_SYM
-%token <kwd> FAST_SYM
-%token <kwd> FAULTS_SYM
-%token <kwd> FILE_SYM
-%token <kwd> FIRST_SYM /* SQL-2003-N */
-%token <kwd> FIXED_SYM
-%token <kwd> FLUSH_SYM
-%token <kwd> FOLLOWS_SYM /* MYSQL trigger*/
-%token <kwd> FOLLOWING_SYM /* SQL-2011-N */
-%token <kwd> FORCE_SYM
-%token <kwd> FORMAT_SYM
-%token <kwd> FOUND_SYM /* SQL-2003-R */
-%token <kwd> FULL /* SQL-2003-R */
-%token <kwd> FUNCTION_SYM /* SQL-2003-R, Oracle-R */
-%token <kwd> GENERAL
-%token <kwd> GENERATED_SYM
-%token <kwd> GEOMETRYCOLLECTION
-%token <kwd> GEOMETRY_SYM
-%token <kwd> GET_FORMAT /* MYSQL-FUNC */
-%token <kwd> GET_SYM /* SQL-2003-R */
-%token <kwd> GLOBAL_SYM /* SQL-2003-R */
-%token <kwd> GRANTS
-%token <kwd> HANDLER_SYM
-%token <kwd> HARD_SYM
-%token <kwd> HASH_SYM
-%token <kwd> HELP_SYM
-%token <kwd> HIGH_PRIORITY
-%token <kwd> HISTORY_SYM /* MYSQL */
-%token <kwd> HOST_SYM
-%token <kwd> HOSTS_SYM
-%token <kwd> HOUR_SYM /* SQL-2003-R */
-%token <kwd> ID_SYM /* MYSQL */
-%token <kwd> IDENTIFIED_SYM
-%token <kwd> IGNORE_SERVER_IDS_SYM
-%token <kwd> IMMEDIATE_SYM /* SQL-2003-R */
-%token <kwd> IMPORT
-%token <kwd> INCREMENT_SYM
-%token <kwd> INDEXES
-%token <kwd> INITIAL_SIZE_SYM
-%token <kwd> INSERT_METHOD
-%token <kwd> INSTALL_SYM
-%token <kwd> INVOKER_SYM
-%token <kwd> IO_SYM
-%token <kwd> IPC_SYM
-%token <kwd> ISOLATION /* SQL-2003-R */
-%token <kwd> ISOPEN_SYM /* Oracle-N */
-%token <kwd> ISSUER_SYM
-%token <kwd> INVISIBLE_SYM
-%token <kwd> JSON_SYM
-%token <kwd> KEY_BLOCK_SIZE
-%token <kwd> LANGUAGE_SYM /* SQL-2003-R */
-%token <kwd> LAST_SYM /* SQL-2003-N */
-%token <kwd> LAST_VALUE
-%token <kwd> LASTVAL_SYM /* PostgreSQL sequence function */
-%token <kwd> LEAVES
-%token <kwd> LESS_SYM
-%token <kwd> LEVEL_SYM
-%token <kwd> LINESTRING
-%token <kwd> LIST_SYM
-%token <kwd> LOCAL_SYM /* SQL-2003-R */
-%token <kwd> LOCKS_SYM
-%token <kwd> LOGFILE_SYM
-%token <kwd> LOGS_SYM
-%token <kwd> MASTER_CONNECT_RETRY_SYM
-%token <kwd> MASTER_DELAY_SYM
-%token <kwd> MASTER_GTID_POS_SYM
-%token <kwd> MASTER_HOST_SYM
-%token <kwd> MASTER_LOG_FILE_SYM
-%token <kwd> MASTER_LOG_POS_SYM
-%token <kwd> MASTER_PASSWORD_SYM
-%token <kwd> MASTER_PORT_SYM
-%token <kwd> MASTER_SERVER_ID_SYM
-%token <kwd> MASTER_SSL_CAPATH_SYM
-%token <kwd> MASTER_SSL_CA_SYM
-%token <kwd> MASTER_SSL_CERT_SYM
-%token <kwd> MASTER_SSL_CIPHER_SYM
-%token <kwd> MASTER_SSL_CRL_SYM
-%token <kwd> MASTER_SSL_CRLPATH_SYM
-%token <kwd> MASTER_SSL_KEY_SYM
-%token <kwd> MASTER_SSL_SYM
-%token <kwd> MASTER_SYM
-%token <kwd> MASTER_USER_SYM
-%token <kwd> MASTER_USE_GTID_SYM
-%token <kwd> MASTER_HEARTBEAT_PERIOD_SYM
-%token <kwd> MAX_CONNECTIONS_PER_HOUR
-%token <kwd> MAX_QUERIES_PER_HOUR
-%token <kwd> MAX_ROWS
-%token <kwd> MAX_SIZE_SYM
-%token <kwd> MAX_UPDATES_PER_HOUR
-%token <kwd> MAX_STATEMENT_TIME_SYM
-%token <kwd> MAX_USER_CONNECTIONS_SYM
-%token <kwd> MEDIUM_SYM
-%token <kwd> MEMORY_SYM
-%token <kwd> MERGE_SYM /* SQL-2003-R */
-%token <kwd> MESSAGE_TEXT_SYM /* SQL-2003-N */
-%token <kwd> MICROSECOND_SYM /* MYSQL-FUNC */
-%token <kwd> MIGRATE_SYM
-%token <kwd> MINUTE_SYM /* SQL-2003-R */
-%token <kwd> MINVALUE_SYM
-%token <kwd> MIN_ROWS
-%token <kwd> MODE_SYM
-%token <kwd> MODIFY_SYM
-%token <kwd> MONTH_SYM /* SQL-2003-R */
-%token <kwd> MULTILINESTRING
-%token <kwd> MULTIPOINT
-%token <kwd> MULTIPOLYGON
-%token <kwd> MUTEX_SYM
-%token <kwd> MYSQL_SYM
-%token <kwd> MYSQL_ERRNO_SYM
-%token <kwd> NAMES_SYM /* SQL-2003-N */
-%token <kwd> NAME_SYM /* SQL-2003-N */
-%token <kwd> NATIONAL_SYM /* SQL-2003-R */
-%token <kwd> NCHAR_SYM /* SQL-2003-R */
-%token <kwd> NEVER_SYM /* MySQL */
-%token <kwd> NEW_SYM /* SQL-2003-R */
-%token <kwd> NEXT_SYM /* SQL-2003-N */
-%token <kwd> NEXTVAL_SYM /* PostgreSQL sequence function */
-%token <kwd> NOCACHE_SYM
-%token <kwd> NOCYCLE_SYM
-%token <kwd> NODEGROUP_SYM
-%token <kwd> NONE_SYM /* SQL-2003-R */
-%token <kwd> NOTFOUND_SYM /* Oracle-R */
-%token <kwd> NO_SYM /* SQL-2003-R */
-%token <kwd> NOMAXVALUE_SYM
-%token <kwd> NOMINVALUE_SYM
-%token <kwd> NO_WAIT_SYM
-%token <kwd> NOWAIT_SYM
-%token <kwd> NUMBER_MARIADB_SYM /* SQL-2003-N */
-%token <kwd> NUMBER_ORACLE_SYM /* Oracle-R, PLSQL-R */
-%token <kwd> NVARCHAR_SYM
-%token <kwd> OF_SYM /* SQL-1992-R, Oracle-R */
-%token <kwd> OFFSET_SYM
-%token <kwd> OLD_PASSWORD_SYM
-%token <kwd> ONE_SYM
-%token <kwd> ONLY_SYM /* SQL-2003-R */
-%token <kwd> ONLINE_SYM
-%token <kwd> OPEN_SYM /* SQL-2003-R */
-%token <kwd> OPTIONS_SYM
-%token <kwd> OPTION /* SQL-2003-N */
-%token <kwd> OWNER_SYM
-%token <kwd> PACK_KEYS_SYM
-%token <kwd> PAGE_SYM
-%token <kwd> PARSER_SYM
-%token <kwd> PARTIAL /* SQL-2003-N */
-%token <kwd> PARTITIONS_SYM
-%token <kwd> PARTITIONING_SYM
-%token <kwd> PASSWORD_SYM
-%token <kwd> PERIOD_SYM /* SQL-2011-R */
-%token <kwd> PERSISTENT_SYM
-%token <kwd> PHASE_SYM
-%token <kwd> PLUGINS_SYM
-%token <kwd> PLUGIN_SYM
-%token <kwd> POINT_SYM
-%token <kwd> POLYGON
-%token <kwd> PORT_SYM
-%token <kwd> PRECEDES_SYM /* MYSQL */
-%token <kwd> PRECEDING_SYM /* SQL-2011-N */
-%token <kwd> PREPARE_SYM /* SQL-2003-R */
-%token <kwd> PRESERVE_SYM
-%token <kwd> PREV_SYM
-%token <kwd> PREVIOUS_SYM
-%token <kwd> PRIVILEGES /* SQL-2003-N */
-%token <kwd> PROCESS
-%token <kwd> PROCESSLIST_SYM
-%token <kwd> PROFILE_SYM
-%token <kwd> PROFILES_SYM
-%token <kwd> PROXY_SYM
-%token <kwd> QUARTER_SYM
-%token <kwd> QUERY_SYM
-%token <kwd> QUICK
-%token <kwd> RAW_MARIADB_SYM
-%token <kwd> RAW_ORACLE_SYM /* Oracle-R */
-%token <kwd> READ_ONLY_SYM
-%token <kwd> REBUILD_SYM
-%token <kwd> RECOVER_SYM
-%token <kwd> REDOFILE_SYM
-%token <kwd> REDO_BUFFER_SIZE_SYM
-%token <kwd> REDUNDANT_SYM
-%token <kwd> RELAY
-%token <kwd> RELAYLOG_SYM
-%token <kwd> RELAY_LOG_FILE_SYM
-%token <kwd> RELAY_LOG_POS_SYM
-%token <kwd> RELAY_THREAD
-%token <kwd> RELOAD
-%token <kwd> REMOVE_SYM
-%token <kwd> REORGANIZE_SYM
-%token <kwd> REPAIR
-%token <kwd> REPEATABLE_SYM /* SQL-2003-N */
-%token <kwd> REPLICATION
-%token <kwd> RESET_SYM
-%token <kwd> RESTART_SYM
-%token <kwd> RESOURCES
-%token <kwd> RESTORE_SYM
-%token <kwd> RESUME_SYM
-%token <kwd> RETURNED_SQLSTATE_SYM /* SQL-2003-N */
-%token <kwd> RETURNS_SYM /* SQL-2003-R */
-%token <kwd> REUSE_SYM /* Oracle-R */
-%token <kwd> REVERSE_SYM
-%token <kwd> ROLE_SYM
-%token <kwd> ROLLBACK_SYM /* SQL-2003-R */
-%token <kwd> ROLLUP_SYM /* SQL-2003-R */
-%token <kwd> ROUTINE_SYM /* SQL-2003-N */
-%token <kwd> ROWCOUNT_SYM /* Oracle-N */
-%token <kwd> ROW_SYM /* SQL-2003-R */
-%token <kwd> ROW_COUNT_SYM /* SQL-2003-N */
-%token <kwd> ROW_FORMAT_SYM
-%token <kwd> RTREE_SYM
-%token <kwd> SAVEPOINT_SYM /* SQL-2003-R */
-%token <kwd> SCHEDULE_SYM
-%token <kwd> SCHEMA_NAME_SYM /* SQL-2003-N */
-%token <kwd> SECOND_SYM /* SQL-2003-R */
-%token <kwd> SECURITY_SYM /* SQL-2003-N */
-%token <kwd> SEQUENCE_SYM
-%token <kwd> SERIALIZABLE_SYM /* SQL-2003-N */
-%token <kwd> SERIAL_SYM
-%token <kwd> SESSION_SYM /* SQL-2003-N */
-%token <kwd> SERVER_SYM
-%token <kwd> SETVAL_SYM /* PostgreSQL sequence function */
-%token <kwd> SHARE_SYM
-%token <kwd> SHUTDOWN
-%token <kwd> SIGNED_SYM
-%token <kwd> SIMPLE_SYM /* SQL-2003-N */
-%token <kwd> SLAVE
-%token <kwd> SLAVES
-%token <kwd> SLAVE_POS_SYM
-%token <kwd> SLOW
-%token <kwd> SNAPSHOT_SYM
-%token <kwd> SOCKET_SYM
-%token <kwd> SOFT_SYM
-%token <kwd> SONAME_SYM
-%token <kwd> SOUNDS_SYM
-%token <kwd> SOURCE_SYM
-%token <kwd> SQL_BUFFER_RESULT
-%token <kwd> SQL_CACHE_SYM
-%token <kwd> SQL_CALC_FOUND_ROWS
-%token <kwd> SQL_NO_CACHE_SYM
-%token <kwd> SQL_THREAD
-%token <kwd> STAGE_SYM
-%token <kwd> STARTS_SYM
-%token <kwd> START_SYM /* SQL-2003-R */
-%token <kwd> STATEMENT_SYM
-%token <kwd> STATUS_SYM
-%token <kwd> STOP_SYM
-%token <kwd> STORAGE_SYM
-%token <kwd> STORED_SYM
-%token <kwd> STRING_SYM
-%token <kwd> SUBCLASS_ORIGIN_SYM /* SQL-2003-N */
-%token <kwd> SUBDATE_SYM
-%token <kwd> SUBJECT_SYM
-%token <kwd> SUBPARTITIONS_SYM
-%token <kwd> SUBPARTITION_SYM
-%token <kwd> SUPER_SYM
-%token <kwd> SUSPEND_SYM
-%token <kwd> SWAPS_SYM
-%token <kwd> SWITCHES_SYM
-%token <kwd> SYSTEM /* SQL-2011-R */
-%token <kwd> SYSTEM_TIME_SYM /* SQL-2011-R */
-%token <kwd> TABLES
-%token <kwd> TABLESPACE
-%token <kwd> TABLE_CHECKSUM_SYM
-%token <kwd> TABLE_NAME_SYM /* SQL-2003-N */
-%token <kwd> TEMPORARY /* SQL-2003-N */
-%token <kwd> TEMPTABLE_SYM
-%token <kwd> TEXT_SYM
-%token <kwd> THAN_SYM
-%token <kwd> TIES_SYM /* SQL-2011-N */
-%token <kwd> TIMESTAMP /* SQL-2003-R */
-%token <kwd> TIMESTAMP_ADD
-%token <kwd> TIMESTAMP_DIFF
-%token <kwd> TIME_SYM /* SQL-2003-R, Oracle-R */
-%token <kwd> TRANSACTION_SYM
-%token <kwd> TRANSACTIONAL_SYM
-%token <kwd> TRIGGERS_SYM
-%token <kwd> TRIM_ORACLE
-%token <kwd> TRUNCATE_SYM
-%token <kwd> TYPES_SYM
-%token <kwd> TYPE_SYM /* SQL-2003-N */
-%token <kwd> UDF_RETURNS_SYM
-%token <kwd> UNBOUNDED_SYM /* SQL-2011-N */
-%token <kwd> UNCOMMITTED_SYM /* SQL-2003-N */
-%token <kwd> UNDEFINED_SYM
-%token <kwd> UNDOFILE_SYM
-%token <kwd> UNDO_BUFFER_SIZE_SYM
-%token <kwd> UNICODE_SYM
-%token <kwd> UNINSTALL_SYM
-%token <kwd> UNKNOWN_SYM /* SQL-2003-R */
-%token <kwd> UNTIL_SYM
-%token <kwd> UPGRADE_SYM
-%token <kwd> USER_SYM /* SQL-2003-R */
-%token <kwd> USE_FRM
-%token <kwd> VALUE_SYM /* SQL-2003-R */
-%token <kwd> VARCHAR2_MARIADB_SYM
-%token <kwd> VARCHAR2_ORACLE_SYM /* Oracle-R, PLSQL-R */
-%token <kwd> VARIABLES
-%token <kwd> VERSIONING_SYM /* SQL-2011-R */
-%token <kwd> VIA_SYM
-%token <kwd> VIEW_SYM /* SQL-2003-N */
-%token <kwd> VIRTUAL_SYM
-%token <kwd> WAIT_SYM
-%token <kwd> WARNINGS
-%token <kwd> WEEK_SYM
-%token <kwd> WEIGHT_STRING_SYM
-%token <kwd> WINDOW_SYM /* SQL-2003-R */
-%token <kwd> WITHIN
-%token <kwd> WITHOUT /* SQL-2003-R */
-%token <kwd> WORK_SYM /* SQL-2003-N */
-%token <kwd> WRAPPER_SYM
-%token <kwd> WRITE_SYM /* SQL-2003-N */
-%token <kwd> X509_SYM
-%token <kwd> XA_SYM
-%token <kwd> XML_SYM
-%token <kwd> YEAR_SYM /* SQL-2003-R */
-
-/* A dummy token to force the priority of table_ref production in a join. */
-%left CONDITIONLESS_JOIN
-%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT ON_SYM USING
-
-%left SET_VAR
-%left OR_SYM OR2_SYM
-%left XOR
-%left AND_SYM AND_AND_SYM
-
-%left PREC_BELOW_NOT
-
-%nonassoc NOT_SYM
-%left '=' EQUAL_SYM GE '>' LE '<' NE
-%nonassoc IS
-%right BETWEEN_SYM
-%left LIKE SOUNDS_SYM REGEXP IN_SYM
-%left '|'
-%left '&'
-%left SHIFT_LEFT SHIFT_RIGHT
-%left '-' '+' ORACLE_CONCAT_SYM
-%left '*' '/' '%' DIV_SYM MOD_SYM
-%left '^'
-%left MYSQL_CONCAT_SYM
-%nonassoc NEG '~' NOT2_SYM BINARY
-%nonassoc COLLATE_SYM
-%nonassoc SUBQUERY_AS_EXPR
-
-/*
- Tokens that can change their meaning from identifier to something else
- in certain context.
-
- - TRANSACTION: identifier, history unit:
- SELECT transaction FROM t1;
- SELECT * FROM t1 FOR SYSTEM_TIME AS OF TRANSACTION @var;
-
- - TIMESTAMP: identifier, literal, history unit:
- SELECT timestamp FROM t1;
- SELECT TIMESTAMP '2001-01-01 10:20:30';
- SELECT * FROM t1 FOR SYSTEM_TIME AS OF TIMESTAMP CONCAT(@date,' ',@time);
-
- - PERIOD: identifier, period for system time:
- SELECT period FROM t1;
- ALTER TABLE DROP PERIOD FOR SYSTEM TIME;
-
- - SYSTEM: identifier, system versioning:
- SELECT system FROM t1;
- ALTER TABLE DROP SYSTEM VERSIONIONG;
-
- - USER: identifier, user:
- SELECT user FROM t1;
- KILL USER foo;
-
- Note, we need here only tokens that cause shift/reduce conflicts
- with keyword identifiers. For example:
- opt_clause1: %empty | KEYWORD ... ;
- clause2: opt_clause1 ident;
- KEYWORD can appear both in opt_clause1 and in "ident" through the "keyword"
- rule. So the parser reports a conflict on how to interpret KEYWORD:
- - as a start of non-empty branch in opt_clause1, or
- - as an identifier which follows the empty branch in opt_clause1.
-
- Example#1:
- alter_list_item:
- DROP opt_column opt_if_exists_table_element field_ident
- | DROP SYSTEM VERSIONING_SYM
- SYSTEM can be a keyword in field_ident, or can be a start of
- SYSTEM VERSIONING.
-
- Example#2:
- system_time_expr: AS OF_SYM history_point
- history_point: opt_history_unit bit_expr
- opt_history_unit: | TRANSACTION_SYM
- TRANSACTION can be a non-empty history unit, or can be an identifier
- in bit_expr.
-
- In the grammar below we use %prec to explicitely tell Bison to go
- through the empty branch in the optional rule only when the lookahead
- token does not belong to a small set of selected tokens.
-
- Tokens NEXT_SYM and PREVIOUS_SYM also change their meaning from
- identifiers to sequence operations when followed by VALUE_SYM:
- SELECT NEXT VALUE FOR s1, PREVIOUS VALUE FOR s1;
- but we don't need to list them here as they do not seem to cause
- conflicts (according to bison -v), as both meanings
- (as identifier, and as a sequence operation) are parts of the same target
- column_default_non_parenthesized_expr, and there are no any optional
- clauses between the start of column_default_non_parenthesized_expr
- and until NEXT_SYM / PREVIOUS_SYM.
-*/
-%left PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE
-%left TRANSACTION_SYM TIMESTAMP PERIOD_SYM SYSTEM USER
-
-
-/*
- Tokens that can appear in a token contraction on the second place
- and change the meaning of the previous token.
-
- - TEXT_STRING: changes the meaning of TIMESTAMP/TIME/DATE
- from identifier to literal:
- SELECT timestamp FROM t1;
- SELECT TIMESTAMP'2001-01-01 00:00:00' FROM t1;
-
- - Parenthesis: changes the meaning of TIMESTAMP/TIME/DATE
- from identifiers to CAST-alike functions:
- SELECT timestamp FROM t1;
- SELECT timestamp(1) FROM t1;
-
- - VALUE: changes NEXT and PREVIOUS from identifier to sequence operation:
- SELECT next, previous FROM t1;
- SELECT NEXT VALUE FOR s1, PREVIOUS VALUE FOR s1;
-
- - VERSIONING: changes SYSTEM from identifier to SYSTEM VERSIONING
- SELECT system FROM t1;
- ALTER TABLE t1 ADD SYSTEM VERSIONING;
-*/
-%left PREC_BELOW_CONTRACTION_TOKEN2
-%left TEXT_STRING '(' ')' VALUE_SYM VERSIONING_SYM
-%left EMPTY_FROM_CLAUSE
-%right INTO
-
-%type <lex_str>
- DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
- HEX_NUM HEX_STRING
- LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident_or_text
- TEXT_STRING_sys TEXT_STRING_literal
- key_cache_name
- sp_opt_label BIN_NUM TEXT_STRING_filesystem
- opt_constraint constraint opt_ident
- opt_package_routine_end_name
- sp_block_label opt_place opt_db
-
-%type <lex_str>
- label_declaration_oracle
- labels_declaration_oracle
-
-%type <ident_sys>
- IDENT_sys
- ident
- label_ident
- sp_decl_ident
- ident_set_usual_case
- ident_or_empty
- ident_table_alias
- ident_sysvar_name
- ident_directly_assignable
-
-%type <lex_string_with_metadata>
- TEXT_STRING
- NCHAR_STRING
-
-%type <lex_str_ptr>
- opt_table_alias_clause
- table_alias_clause
-
-%type <ident_cli>
- IDENT
- IDENT_QUOTED
- IDENT_cli
- ident_cli
-
-%type <kwd>
- keyword_data_type
- keyword_ident
- keyword_label
- keyword_set_special_case
- keyword_set_usual_case
- keyword_sp_block_section
- keyword_sp_decl
- keyword_sp_head
- keyword_sp_var_and_label
- keyword_sp_var_not_label
- keyword_sysvar_name
- keyword_sysvar_type
- keyword_table_alias
- keyword_verb_clause
- keyword_directly_assignable
-
-%type <table>
- table_ident table_ident_nodb references xid
- table_ident_opt_wild create_like
-
-%type <qualified_column_ident>
- optionally_qualified_column_ident
-
-%type <simple_string>
- remember_name remember_end remember_end_opt
- remember_tok_start
- wild_and_where
-
-%type <const_simple_string>
- field_length opt_field_length opt_field_length_default_1
- opt_compression_method
-
-%type <string>
- text_string hex_or_bin_String opt_gconcat_separator
-
-%type <type_handler> int_type real_type
-
-%type <Lex_field_type> type_with_opt_collate field_type
- qualified_field_type
- sp_param_type_with_opt_collate
- sp_param_field_type
- sp_param_field_type_string
- field_type_numeric
- field_type_string
- field_type_lob
- field_type_temporal
- field_type_misc
-
-%type <Lex_dyncol_type> opt_dyncol_type dyncol_type
- numeric_dyncol_type temporal_dyncol_type string_dyncol_type
-
-%type <create_field> field_spec column_def
-
-%type <geom_type> spatial_type
-
-%type <num>
- order_dir lock_option
- udf_type opt_local opt_no_write_to_binlog
- opt_temporary all_or_any opt_distinct opt_glimit_clause
- opt_ignore_leaves fulltext_options union_option
- opt_not
- 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_time_precision kill_type kill_option int_num
- opt_default_time_precision
- case_stmt_body opt_bin_mod opt_for_system_time_clause
- opt_if_exists_table_element opt_if_not_exists_table_element
- opt_recursive opt_format_xid opt_for_portion_of_time_clause
-
-%type <object_ddl_options>
- create_or_replace
- opt_if_not_exists
- opt_if_exists
-
-/*
- 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
-
-%type <m_fk_option>
- delete_option
-
-%type <ulong_num>
- ulong_num real_ulong_num merge_insert_types
- ws_nweights opt_versioning_interval_start
- 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 bool
-
-%type <ulonglong_number>
- ulonglong_num real_ulonglong_num size_number
-
-%type <longlong_number>
- longlong_num
-
-%type <choice> choice
-
-%type <lock_type>
- replace_lock_option opt_low_priority insert_lock_option load_data_lock
-
-%type <item>
- literal insert_ident order_ident temporal_literal
- simple_ident expr sum_expr in_sum_expr
- variable variable_aux bool_pri
- predicate bit_expr parenthesized_expr
- table_wild simple_expr column_default_non_parenthesized_expr udf_expr
- primary_expr string_factor_expr mysql_concatenation_expr
- select_sublist_qualified_asterisk
- expr_or_default set_expr_or_default
- geometry_function signed_literal expr_or_literal
- sp_opt_default
- simple_ident_nospvar
- field_or_var limit_option
- part_func_expr
- window_func_expr
- window_func
- simple_window_func
- inverse_distribution_function
- percentile_function
- inverse_distribution_function_def
- explicit_cursor_attr
- function_call_keyword
- function_call_keyword_timestamp
- function_call_nonkeyword
- function_call_generic
- function_call_conflict kill_expr
- signal_allowed_expr
- simple_target_specification
- condition_number
- reset_lex_expr
-
-%type <item_param> param_marker
-
-%type <item_num>
- NUM_literal
-
-%type <item_basic_constant> text_literal
-
-%type <item_list>
- expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else
- ident_list ident_list_arg opt_expr_list
- decode_when_list_oracle
- execute_using
- execute_params
-
-%type <sp_cursor_stmt>
- sp_cursor_stmt_lex
- sp_cursor_stmt
-
-%type <assignment_lex>
- assignment_source_lex
- assignment_source_expr
- for_loop_bound_expr
-
-%type <sp_assignment_lex_list>
- cursor_actual_parameters
- opt_parenthesized_cursor_actual_parameters
-
-%type <var_type>
- option_type opt_var_type opt_var_ident_type
-
-%type <key_type>
- opt_unique constraint_key_type fulltext spatial
-
-%type <key_alg>
- btree_or_rtree opt_key_algorithm_clause opt_USING_key_algorithm
-
-%type <string_list>
- using_list opt_use_partition use_partition
-
-%type <key_part>
- key_part
-
-%type <table_list>
- join_table_list join_table
- table_factor table_ref esc_table_ref
- table_primary_ident table_primary_ident_opt_parens
- table_primary_derived table_primary_derived_opt_parens
- derived_table_list table_reference_list_parens
- nested_table_reference_list join_table_parens
- update_table_list
-%type <date_time_type> date_time_type;
-%type <interval> interval
-
-%type <interval_time_st> interval_time_stamp
-
-%type <db_type> storage_engines known_storage_engines
-
-%type <row_type> row_types
-
-%type <tx_isolation> isolation_types
-
-%type <ha_rkey_mode> handler_rkey_mode
-
-%type <Lex_cast_type> cast_type cast_type_numeric cast_type_temporal
-
-%type <Lex_length_and_dec> precision opt_precision float_options
- opt_field_length_default_sp_param_varchar
- opt_field_length_default_sp_param_char
-
-%type <lex_user> user grant_user grant_role user_or_role current_role
- admin_option_for_role user_maybe_role
-
-%type <user_auth> opt_auth_str auth_expression auth_token
-
-%type <charset>
- opt_collate
- charset_name
- charset_or_alias
- charset_name_or_default
- old_or_new_charset_name
- old_or_new_charset_name_or_default
- collation_name
- collation_name_or_default
- opt_load_data_charset
- UNDERSCORE_CHARSET
-
-%type <select_lex> subselect
- query_specification
- table_value_constructor
- simple_table
- query_simple
- query_primary
- subquery
- select_into_query_specification
-
-%type <select_lex_unit>
- query_expression
- query_expression_no_with_clause
- query_expression_body_ext
- query_expression_body_ext_parens
- query_expression_body
- query_specification_start
-
-%type <boolfunc2creator> comp_op
-
-%type <dyncol_def> dyncall_create_element
-
-%type <dyncol_def_list> dyncall_create_list
-
-%type <myvar> select_outvar
-
-%type <virtual_column> opt_check_constraint check_constraint virtual_column_func
- column_default_expr
-
-%type <unit_operation> unit_type_decl
-
-%type <select_lock>
- opt_procedure_or_into
- opt_select_lock_type
- select_lock_type
- opt_lock_wait_timeout_new
-
-%type <select_limit> opt_limit_clause limit_clause limit_options
-
-%type <order_limit_lock>
- query_expression_tail
- opt_query_expression_tail
- order_or_limit
- order_limit_lock
- opt_order_limit_lock
-
-%type <select_order> opt_order_clause order_clause order_list
-
-%type <NONE>
- analyze_stmt_command backup backup_statements
- query verb_clause create change select select_into
- do drop insert replace insert2
- insert_values update delete truncate rename compound_statement
- show describe load alter optimize keycache preload flush
- reset purge begin_stmt_mariadb commit rollback savepoint release
- slave master_def master_defs master_file_def slave_until_opts
- 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 kill key_def constraint_def
- keycache_list keycache_list_or_parts assign_to_keycache
- assign_to_keycache_parts
- preload_list preload_list_or_parts preload_keys preload_keys_parts
- select_item_list select_item values_list no_braces
- delete_limit_clause fields opt_values values
- no_braces_with_names opt_values_with_names values_with_names
- procedure_list procedure_list2 procedure_item
- field_def handler opt_generated_always
- opt_ignore opt_column opt_restrict
- grant revoke set lock unlock string_list field_options
- opt_binary table_lock_list table_lock
- 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
- attribute attribute_list
- compressed_deprecated_data_type_attribute
- compressed_deprecated_column_attribute
- column_list column_list_id
- opt_column_list grant_privileges grant_ident grant_list grant_option
- object_privilege object_privilege_list user_list user_and_role_list
- rename_list table_or_tables
- clear_privileges flush_options flush_option
- opt_flush_lock flush_lock flush_options_list
- equal optional_braces
- 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
- opt_and charset
- select_var_list select_var_list_init help
- opt_extended_describe shutdown
- opt_format_json
- prepare execute deallocate
- statement
- 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_list_opt view_list view_select
- trigger_tail event_tail
- install uninstall partition_entry binlog_base64_event
- normal_key_options normal_key_opts all_key_opt
- spatial_key_options fulltext_key_options normal_key_opt
- fulltext_key_opt spatial_key_opt fulltext_key_opts spatial_key_opts
- keep_gcc_happy
- key_using_alg
- part_column_list
- period_for_system_time
- period_for_application_time
- server_def server_options_list server_option
- definer_opt no_definer definer get_diagnostics
- parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
- vcol_opt_attribute_list vcol_attribute
- opt_serial_attribute opt_serial_attribute_list serial_attribute
- explainable_command
- opt_lock_wait_timeout
- opt_delete_gtid_domain
- asrow_attribute
- set_assign
- sp_tail_standalone
- opt_constraint_no_id
-END_OF_INPUT
-
-%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
-%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return
-%type <NONE> sp_proc_stmt_compound_ok
-%type <NONE> sp_proc_stmt_if
-%type <NONE> sp_labeled_control sp_unlabeled_control
-%type <NONE> sp_labeled_block sp_unlabeled_block
-%type <NONE> sp_labelable_stmt
-%type <NONE> sp_proc_stmt_continue_oracle
-%type <NONE> sp_proc_stmt_exit_oracle
-%type <NONE> sp_proc_stmt_leave
-%type <NONE> sp_proc_stmt_iterate
-%type <NONE> sp_proc_stmt_goto_oracle
-%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
-%type <NONE> case_stmt_specification
-%type <NONE> loop_body while_body repeat_body
-
-%type <num> view_algorithm view_check_option
-%type <view_suid> view_suid opt_view_suid
-
-%type <plsql_cursor_attr> plsql_cursor_attr
-%type <sp_suid> sp_suid
-%type <sp_aggregate_type> opt_aggregate
-
-%type <num> sp_decl_idents sp_decl_idents_init_vars
-%type <num> sp_handler_type sp_hcond_list
-%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
-%type <spblock> sp_decl_body_list opt_sp_decl_body_list
-%type <spblock> sp_decl_vars
-%type <spblock> sp_decl_non_handler sp_decl_non_handler_list
-%type <spblock> sp_decl_handler sp_decl_handler_list opt_sp_decl_handler_list
-%type <spblock> package_implementation_routine_definition
-%type <spblock> package_implementation_item_declaration
-%type <spblock> package_implementation_declare_section
-%type <spblock> package_implementation_declare_section_list1
-%type <spblock> package_implementation_declare_section_list2
-%type <spblock_handlers> sp_block_statements_and_exceptions
-%type <spblock_handlers> package_implementation_executable_section
-%type <sp_instr_addr> sp_instr_addr
-%type <num> opt_exception_clause exception_handlers
-%type <lex> remember_lex package_routine_lex
- package_specification_function
- package_specification_procedure
-%type <spname> sp_name opt_sp_name
-%type <spvar> sp_param_name sp_param_name_and_type
-%type <for_loop> sp_for_loop_index_and_bounds
-%type <for_loop_bounds> sp_for_loop_bounds
-%type <trim> trim_operands
-%type <num> opt_sp_for_loop_direction
-%type <spvar_mode> sp_opt_inout
-%type <index_hint> index_hint_type
-%type <num> index_hint_clause normal_join inner_join
-%type <filetype> data_or_xml
-
-%type <NONE> signal_stmt resignal_stmt raise_stmt_oracle
-%type <diag_condition_item_name> signal_condition_information_item_name
-
-%type <trg_execution_order> trigger_follows_precedes_clause;
-%type <trigger_action_order_type> trigger_action_order;
-
-%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 <spvar_definition> row_field_name row_field_definition
-%type <spvar_definition_list> row_field_definition_list row_type_body
-
-%type <NONE> opt_window_clause window_def_list window_def window_spec
-%type <lex_str_ptr> window_name
-%type <NONE> opt_window_ref opt_window_frame_clause
-%type <frame_units> window_frame_units;
-%type <NONE> window_frame_extent;
-%type <frame_exclusion> opt_window_frame_exclusion;
-%type <window_frame_bound> window_frame_start window_frame_bound;
-
-%type <kwd>
- '-' '+' '*' '/' '%' '(' ')'
- ',' '!' '{' '}' '&' '|'
-
-%type <NONE>
- AND_SYM OR_SYM BETWEEN_SYM CASE_SYM
- THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM
- MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM
-
-%type <with_clause> with_clause
-
-%type <lex_str_ptr> query_name
-
-%type <lex_str_list> opt_with_column_list
-
-%type <vers_range_unit> opt_history_unit
-%type <vers_history_point> history_point
-%type <vers_column_versioning> with_or_without_system
-%%
-
-
-/*
- Indentation of grammar rules:
-
-rule: <-- starts at col 1
- rule1a rule1b rule1c <-- starts at col 11
- { <-- starts at col 11
- code <-- starts at col 13, indentation is 2 spaces
- }
- | rule2a rule2b
- {
- code
- }
- ; <-- on a line by itself, starts at col 9
-
- Also, please do not use any <TAB>, but spaces.
- Having a uniform indentation in this file helps
- code reviews, patches, merges, and make maintenance easier.
- Tip: grep [[:cntrl:]] sql_yacc.yy
- Thanks.
-*/
-
-query:
- END_OF_INPUT
- {
- if (!thd->bootstrap &&
- (!(thd->lex->lex_options & OPTION_LEX_FOUND_COMMENT)))
- my_yyabort_error((ER_EMPTY_QUERY, MYF(0)));
-
- thd->lex->sql_command= SQLCOM_EMPTY_QUERY;
- YYLIP->found_semicolon= NULL;
- }
- | verb_clause
- {
- Lex_input_stream *lip = YYLIP;
-
- if ((thd->client_capabilities & CLIENT_MULTI_QUERIES) &&
- lip->multi_statements &&
- ! lip->eof())
- {
- /*
- We found a well formed query, and multi queries are allowed:
- - force the parser to stop after the ';'
- - mark the start of the next query for the next invocation
- of the parser.
- */
- lip->next_state= MY_LEX_END;
- lip->found_semicolon= lip->get_ptr();
- }
- else
- {
- /* Single query, terminated. */
- lip->found_semicolon= NULL;
- }
- }
- ';'
- opt_end_of_input
- | verb_clause END_OF_INPUT
- {
- /* Single query, not terminated. */
- YYLIP->found_semicolon= NULL;
- }
- ;
-
-opt_end_of_input:
- /* empty */
- | END_OF_INPUT
- ;
-
-verb_clause:
- statement
- | begin_stmt_mariadb
- | compound_statement
- ;
-
-/* Verb clauses, except begin and compound_statement */
-statement:
- alter
- | analyze
- | analyze_stmt_command
- | backup
- | binlog_base64_event
- | call
- | change
- | check
- | checksum
- | commit
- | create
- | deallocate
- | delete
- | describe
- | do
- | drop
- | execute
- | flush
- | get_diagnostics
- | grant
- | handler
- | help
- | insert
- | install
- | keep_gcc_happy
- | keycache
- | kill
- | load
- | lock
- | optimize
- | parse_vcol_expr
- | partition_entry
- | preload
- | prepare
- | purge
- | raise_stmt_oracle
- | release
- | rename
- | repair
- | replace
- | reset
- | resignal_stmt
- | revoke
- | rollback
- | savepoint
- | select
- | select_into
- | set
- | set_assign
- | signal_stmt
- | show
- | shutdown
- | slave
- | start
- | truncate
- | uninstall
- | unlock
- | update
- | use
- | xa
- ;
-
-deallocate:
- deallocate_or_drop PREPARE_SYM ident
- {
- Lex->stmt_deallocate_prepare($3);
- }
- ;
-
-deallocate_or_drop:
- DEALLOCATE_SYM
- | DROP
- ;
-
-prepare:
- PREPARE_SYM ident FROM
- { Lex->clause_that_disallows_subselect= "PREPARE..FROM"; }
- expr
- {
- Lex->clause_that_disallows_subselect= NULL;
- if (Lex->stmt_prepare($2, $5))
- MYSQL_YYABORT;
- }
- ;
-
-execute:
- EXECUTE_SYM ident execute_using
- {
- if (Lex->stmt_execute($2, $3))
- MYSQL_YYABORT;
- }
- | EXECUTE_SYM IMMEDIATE_SYM
- { Lex->clause_that_disallows_subselect= "EXECUTE IMMEDIATE"; }
- expr
- { Lex->clause_that_disallows_subselect= NULL; }
- execute_using
- {
- if (Lex->stmt_execute_immediate($4, $6))
- MYSQL_YYABORT;
- }
- ;
-
-execute_using:
- /* nothing */ { $$= NULL; }
- | USING
- { Lex->clause_that_disallows_subselect= "EXECUTE..USING"; }
- execute_params
- {
- $$= $3;
- Lex->clause_that_disallows_subselect= NULL;
- }
- ;
-
-execute_params:
- expr_or_default
- {
- if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
- MYSQL_YYABORT;
- }
- | execute_params ',' expr_or_default
- {
- if (($$= $1)->push_back($3, thd->mem_root))
- MYSQL_YYABORT;
- }
- ;
-
-
-/* help */
-
-help:
- HELP_SYM
- {
- if (unlikely(Lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HELP"));
- }
- ident_or_text
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_HELP;
- lex->help_arg= $3.str;
- }
- ;
-
-/* change master */
-
-change:
- CHANGE MASTER_SYM optional_connection_name TO_SYM
- {
- Lex->sql_command = SQLCOM_CHANGE_MASTER;
- }
- master_defs
- {}
- ;
-
-master_defs:
- master_def
- | master_defs ',' master_def
- ;
-
-master_def:
- MASTER_HOST_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.host = $3.str;
- }
- | MASTER_USER_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.user = $3.str;
- }
- | MASTER_PASSWORD_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.password = $3.str;
- }
- | MASTER_PORT_SYM '=' ulong_num
- {
- Lex->mi.port = $3;
- }
- | MASTER_CONNECT_RETRY_SYM '=' ulong_num
- {
- Lex->mi.connect_retry = $3;
- }
- | MASTER_DELAY_SYM '=' ulong_num
- {
- if ($3 > MASTER_DELAY_MAX)
- {
- my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0),
- (ulong) $3, (ulong) MASTER_DELAY_MAX);
- }
- else
- Lex->mi.sql_delay = $3;
- }
- | MASTER_SSL_SYM '=' ulong_num
- {
- Lex->mi.ssl= $3 ?
- LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE;
- }
- | MASTER_SSL_CA_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_ca= $3.str;
- }
- | MASTER_SSL_CAPATH_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_capath= $3.str;
- }
- | MASTER_SSL_CERT_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_cert= $3.str;
- }
- | MASTER_SSL_CIPHER_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_cipher= $3.str;
- }
- | MASTER_SSL_KEY_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_key= $3.str;
- }
- | MASTER_SSL_VERIFY_SERVER_CERT_SYM '=' ulong_num
- {
- Lex->mi.ssl_verify_server_cert= $3 ?
- LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE;
- }
- | MASTER_SSL_CRL_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_crl= $3.str;
- }
- | MASTER_SSL_CRLPATH_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.ssl_crlpath= $3.str;
- }
-
- | MASTER_HEARTBEAT_PERIOD_SYM '=' NUM_literal
- {
- Lex->mi.heartbeat_period= (float) $3->val_real();
- if (unlikely(Lex->mi.heartbeat_period >
- SLAVE_MAX_HEARTBEAT_PERIOD) ||
- unlikely(Lex->mi.heartbeat_period < 0.0))
- my_yyabort_error((ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE, MYF(0),
- SLAVE_MAX_HEARTBEAT_PERIOD));
-
- if (unlikely(Lex->mi.heartbeat_period > slave_net_timeout))
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX,
- ER_THD(thd, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX));
- }
- if (unlikely(Lex->mi.heartbeat_period < 0.001))
- {
- if (unlikely(Lex->mi.heartbeat_period != 0.0))
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN,
- ER_THD(thd, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN));
- Lex->mi.heartbeat_period= 0.0;
- }
- Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_DISABLE;
- }
- Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_ENABLE;
- }
- | IGNORE_SERVER_IDS_SYM '=' '(' ignore_server_id_list ')'
- {
- Lex->mi.repl_ignore_server_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE;
- }
- | DO_DOMAIN_IDS_SYM '=' '(' do_domain_id_list ')'
- {
- Lex->mi.repl_do_domain_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE;
- }
- | IGNORE_DOMAIN_IDS_SYM '=' '(' ignore_domain_id_list ')'
- {
- Lex->mi.repl_ignore_domain_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE;
- }
- |
- master_file_def
- ;
-
-ignore_server_id_list:
- /* Empty */
- | ignore_server_id
- | ignore_server_id_list ',' ignore_server_id
- ;
-
-ignore_server_id:
- ulong_num
- {
- insert_dynamic(&Lex->mi.repl_ignore_server_ids, (uchar*) &($1));
- }
- ;
-
-do_domain_id_list:
- /* Empty */
- | do_domain_id
- | do_domain_id_list ',' do_domain_id
- ;
-
-do_domain_id:
- ulong_num
- {
- insert_dynamic(&Lex->mi.repl_do_domain_ids, (uchar*) &($1));
- }
- ;
-
-ignore_domain_id_list:
- /* Empty */
- | ignore_domain_id
- | ignore_domain_id_list ',' ignore_domain_id
- ;
-
-ignore_domain_id:
- ulong_num
- {
- insert_dynamic(&Lex->mi.repl_ignore_domain_ids, (uchar*) &($1));
- }
- ;
-
-master_file_def:
- MASTER_LOG_FILE_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.log_file_name = $3.str;
- }
- | MASTER_LOG_POS_SYM '=' ulonglong_num
- {
- /*
- If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it
- instead of causing subsequent errors.
- We need to do it in this file, because only there we know that
- MASTER_LOG_POS has been explicitly specified. On the contrary
- in change_master() (sql_repl.cc) we cannot distinguish between 0
- (MASTER_LOG_POS explicitly specified as 0) and 0 (unspecified),
- whereas we want to distinguish (specified 0 means "read the binlog
- from 0" (4 in fact), unspecified means "don't change the position
- (keep the preceding value)").
- */
- Lex->mi.pos= MY_MAX(BIN_LOG_HEADER_SIZE, $3);
- }
- | RELAY_LOG_FILE_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.relay_log_name = $3.str;
- }
- | RELAY_LOG_POS_SYM '=' ulong_num
- {
- Lex->mi.relay_log_pos = $3;
- /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
- Lex->mi.relay_log_pos= MY_MAX(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
- }
- | MASTER_USE_GTID_SYM '=' CURRENT_POS_SYM
- {
- if (unlikely(Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid"));
- Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_CURRENT_POS;
- }
- | MASTER_USE_GTID_SYM '=' SLAVE_POS_SYM
- {
- if (unlikely(Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid"));
- Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_SLAVE_POS;
- }
- | MASTER_USE_GTID_SYM '=' NO_SYM
- {
- if (unlikely(Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid"));
- Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_NO;
- }
- ;
-
-optional_connection_name:
- /* empty */
- {
- LEX *lex= thd->lex;
- lex->mi.connection_name= null_clex_str;
- }
- | connection_name
- ;
-
-connection_name:
- TEXT_STRING_sys
- {
- Lex->mi.connection_name= $1;
-#ifdef HAVE_REPLICATION
- if (unlikely(check_master_connection_name(&$1)))
- my_yyabort_error((ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME"));
-#endif
- }
- ;
-
-/* create a table */
-
-create:
- create_or_replace opt_temporary TABLE_SYM opt_if_not_exists
- {
- LEX *lex= thd->lex;
- if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_table()))
- MYSQL_YYABORT;
- lex->create_info.init();
- if (lex->main_select_push())
- MYSQL_YYABORT;
- lex->current_select->parsing_place= BEFORE_OPT_LIST;
- if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4))
- MYSQL_YYABORT;
- }
- table_ident
- {
- LEX *lex= thd->lex;
- if (!lex->first_select_lex()->
- add_table_to_list(thd, $6, NULL, TL_OPTION_UPDATING,
- TL_WRITE, MDL_SHARED_UPGRADABLE))
- MYSQL_YYABORT;
- lex->alter_info.reset();
- /*
- 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.default_table_charset= NULL;
- lex->name= null_clex_str;
- lex->create_last_non_select_table= lex->last_table();
- }
- create_body
- {
- LEX *lex= thd->lex;
- create_table_set_open_action_and_adjust_tables(lex);
- Lex->pop_select(); //main select
- }
- | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident
- {
- LEX *lex= thd->lex;
- if (lex->main_select_push())
- MYSQL_YYABORT;
- if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_create_sequence()))
- MYSQL_YYABORT;
- lex->create_info.init();
- if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2,
- $1 | $4)))
- MYSQL_YYABORT;
-
- if (!lex->first_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->alter_info.reset();
- lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
- lex->name= null_clex_str;
- lex->create_last_non_select_table= lex->last_table();
- if (unlikely(!(lex->create_info.seq_create_info=
- new (thd->mem_root) sequence_definition())))
- MYSQL_YYABORT;
- }
- opt_sequence opt_create_table_options
- {
- LEX *lex= thd->lex;
-
- if (unlikely(lex->create_info.seq_create_info->check_and_adjust(1)))
- {
- my_error(ER_SEQUENCE_INVALID_DATA, MYF(0),
- lex->first_select_lex()->table_list.first->db.str,
- lex->first_select_lex()->table_list.first->
- table_name.str);
- MYSQL_YYABORT;
- }
-
- /* No fields specified, generate them */
- if (unlikely(prepare_sequence_fields(thd,
- &lex->alter_info.create_list)))
- MYSQL_YYABORT;
-
- /* CREATE SEQUENCE always creates a sequence */
- Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE;
- Lex->create_info.sequence= 1;
-
- create_table_set_open_action_and_adjust_tables(lex);
- Lex->pop_select(); //main select
- }
- | create_or_replace opt_unique INDEX_SYM opt_if_not_exists
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- ident
- opt_key_algorithm_clause
- ON table_ident
- {
- if (Lex->add_create_index_prepare($9))
- MYSQL_YYABORT;
- if (Lex->add_create_index($2, &$6, $7, $1 | $4))
- MYSQL_YYABORT;
- }
- '(' key_list ')' opt_lock_wait_timeout normal_key_options
- opt_index_lock_algorithm
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace fulltext INDEX_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- opt_if_not_exists ident
- ON table_ident
- {
- if (Lex->add_create_index_prepare($8))
- MYSQL_YYABORT;
- if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5))
- MYSQL_YYABORT;
- }
- '(' key_list ')' opt_lock_wait_timeout fulltext_key_options
- opt_index_lock_algorithm
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace spatial INDEX_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- opt_if_not_exists ident
- ON table_ident
- {
- if (Lex->add_create_index_prepare($8))
- MYSQL_YYABORT;
- if (Lex->add_create_index($2, &$6, HA_KEY_ALG_UNDEF, $1 | $5))
- MYSQL_YYABORT;
- }
- '(' key_list ')' opt_lock_wait_timeout spatial_key_options
- opt_index_lock_algorithm
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace DATABASE opt_if_not_exists ident
- {
- Lex->create_info.default_table_charset= NULL;
- Lex->create_info.used_fields= 0;
- }
- opt_create_database_options
- {
- LEX *lex=Lex;
- if (unlikely(lex->set_command_with_check(SQLCOM_CREATE_DB, 0,
- $1 | $3)))
- MYSQL_YYABORT;
- lex->name= $4;
- }
- | create_or_replace definer_opt opt_view_suid VIEW_SYM
- opt_if_not_exists table_ident
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- if (Lex->add_create_view(thd, $1 | $5,
- DTYPE_ALGORITHM_UNDEFINED, $3, $6))
- MYSQL_YYABORT;
- }
- view_list_opt AS view_select
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM
- opt_if_not_exists table_ident
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- if (Lex->add_create_view(thd, $1 | $6, $2, $4, $7))
- MYSQL_YYABORT;
- }
- view_list_opt AS view_select
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace definer_opt TRIGGER_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- Lex->create_info.set($1);
- }
- trigger_tail
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace definer_opt PROCEDURE_SYM opt_if_not_exists
- {
- if (Lex->stmt_create_procedure_start($1 | $4))
- MYSQL_YYABORT;
- }
- sp_tail_standalone
- {
- Lex->stmt_create_routine_finalize();
- }
- | create_or_replace definer_opt EVENT_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- Lex->create_info.set($1);
- }
- event_tail
- {
- Lex->pop_select(); //main select
- }
- | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- sp_name RETURN_ORACLE_SYM
- {
- if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
- MYSQL_YYABORT;
- }
- sf_return_type
- sf_c_chistics_and_body_standalone
- opt_sp_name
- {
- if (Lex->stmt_create_stored_function_finalize_standalone($11))
- MYSQL_YYABORT;
- }
- | create_or_replace definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- sp_name '('
- {
- if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
- MYSQL_YYABORT;
- }
- sp_fdparam_list ')'
- RETURN_ORACLE_SYM sf_return_type
- sf_c_chistics_and_body_standalone
- opt_sp_name
- {
- if (Lex->stmt_create_stored_function_finalize_standalone($14))
- MYSQL_YYABORT;
- }
- | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- sp_name RETURN_ORACLE_SYM
- {
- if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
- MYSQL_YYABORT;
- }
- sf_return_type
- sf_c_chistics_and_body_standalone
- opt_sp_name
- {
- if (Lex->stmt_create_stored_function_finalize_standalone($11))
- MYSQL_YYABORT;
- }
- | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- sp_name '('
- {
- if (Lex->stmt_create_stored_function_start($1 | $5, $3, $6))
- MYSQL_YYABORT;
- }
- sp_fdparam_list ')'
- RETURN_ORACLE_SYM sf_return_type
- sf_c_chistics_and_body_standalone
- opt_sp_name
- {
- if (Lex->stmt_create_stored_function_finalize_standalone($14))
- MYSQL_YYABORT;
- }
- | create_or_replace no_definer opt_aggregate FUNCTION_SYM opt_if_not_exists
- ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys
- {
- if (Lex->stmt_create_udf_function($1 | $5, $3, $6,
- (Item_result) $8, $10))
- MYSQL_YYABORT;
- }
- | create_or_replace USER_SYM opt_if_not_exists clear_privileges
- grant_list opt_require_clause opt_resource_options opt_account_locking opt_password_expiration
- {
- if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER,
- $1 | $3)))
- MYSQL_YYABORT;
- }
- | create_or_replace ROLE_SYM opt_if_not_exists
- clear_privileges role_list opt_with_admin
- {
- if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_ROLE,
- $1 | $3)))
- MYSQL_YYABORT;
- }
- | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info
- {
- Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
- }
- | CREATE TABLESPACE tablespace_info
- {
- Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE;
- }
- | create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); }
- server_def
- { }
- | create_or_replace definer_opt PACKAGE_ORACLE_SYM
- opt_if_not_exists sp_name opt_create_package_chistics_init
- sp_tail_is
- remember_name
- {
- sp_package *pkg;
- if (unlikely(!(pkg= Lex->
- create_package_start(thd,
- SQLCOM_CREATE_PACKAGE,
- &sp_handler_package_spec,
- $5, $1 | $4))))
- MYSQL_YYABORT;
- pkg->set_c_chistics(Lex->sp_chistics);
- }
- opt_package_specification_element_list END
- remember_end_opt opt_sp_name
- {
- if (unlikely(Lex->create_package_finalize(thd, $5, $13, $8, $12)))
- MYSQL_YYABORT;
- }
- | create_or_replace definer_opt PACKAGE_ORACLE_SYM BODY_ORACLE_SYM
- opt_if_not_exists sp_name opt_create_package_chistics_init
- sp_tail_is
- remember_name
- {
- sp_package *pkg;
- if (unlikely(!(pkg= Lex->
- create_package_start(thd,
- SQLCOM_CREATE_PACKAGE_BODY,
- &sp_handler_package_body,
- $6, $1 | $5))))
- MYSQL_YYABORT;
- pkg->set_c_chistics(Lex->sp_chistics);
- Lex->sp_block_init(thd);
- }
- package_implementation_declare_section
- {
- if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
- MYSQL_YYABORT;
- }
- package_implementation_executable_section
- {
- $11.hndlrs+= $13.hndlrs;
- if (unlikely(Lex->sp_block_finalize(thd, $11)))
- MYSQL_YYABORT;
- }
- remember_end_opt opt_sp_name
- {
- if (unlikely(Lex->create_package_finalize(thd, $6, $16, $9, $15)))
- MYSQL_YYABORT;
- }
- ;
-
-package_implementation_executable_section:
- END
- {
- if (unlikely(Lex->sp_block_with_exceptions_add_empty(thd)))
- MYSQL_YYABORT;
- $$.init(0);
- }
- | BEGIN_ORACLE_SYM sp_block_statements_and_exceptions END { $$= $2; }
- ;
-
-/*
- Inside CREATE PACKAGE BODY, package-wide items (e.g. variables)
- must be declared before routine definitions.
-*/
-package_implementation_declare_section:
- package_implementation_declare_section_list1
- | package_implementation_declare_section_list2
- | package_implementation_declare_section_list1
- package_implementation_declare_section_list2
- { $$.join($1, $2); }
- ;
-
-package_implementation_declare_section_list1:
- package_implementation_item_declaration
- | package_implementation_declare_section_list1
- package_implementation_item_declaration
- { $$.join($1, $2); }
- ;
-
-package_implementation_declare_section_list2:
- package_implementation_routine_definition
- | package_implementation_declare_section_list2
- package_implementation_routine_definition
- { $$.join($1, $2); }
- ;
-
-package_routine_lex:
- {
- if (unlikely(!($$= new (thd->mem_root)
- sp_lex_local(thd, thd->lex))))
- MYSQL_YYABORT;
- thd->m_parser_state->m_yacc.reset_before_substatement();
- }
- ;
-
-
-package_specification_function:
- remember_lex package_routine_lex ident
- {
- DBUG_ASSERT($1->sphead->get_package());
- $2->sql_command= SQLCOM_CREATE_FUNCTION;
- sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
- if (unlikely(!spname))
- MYSQL_YYABORT;
- thd->lex= $2;
- if (unlikely(!$2->make_sp_head_no_recursive(thd, spname,
- &sp_handler_package_function,
- NOT_AGGREGATE)))
- MYSQL_YYABORT;
- $1->sphead->get_package()->m_current_routine= $2;
- (void) is_native_function_with_warn(thd, &$3);
- }
- opt_sp_parenthesized_fdparam_list
- RETURN_ORACLE_SYM sf_return_type
- sp_c_chistics
- {
- sp_head *sp= thd->lex->sphead;
- sp->restore_thd_mem_root(thd);
- thd->lex= $1;
- $$= $2;
- }
- ;
-
-package_specification_procedure:
- remember_lex package_routine_lex ident
- {
- DBUG_ASSERT($1->sphead->get_package());
- $2->sql_command= SQLCOM_CREATE_PROCEDURE;
- sp_name *spname= $1->make_sp_name_package_routine(thd, &$3);
- if (unlikely(!spname))
- MYSQL_YYABORT;
- thd->lex= $2;
- if (unlikely(!$2->make_sp_head_no_recursive(thd, spname,
- &sp_handler_package_procedure,
- DEFAULT_AGGREGATE)))
- MYSQL_YYABORT;
- $1->sphead->get_package()->m_current_routine= $2;
- }
- opt_sp_parenthesized_pdparam_list
- sp_c_chistics
- {
- sp_head *sp= thd->lex->sphead;
- sp->restore_thd_mem_root(thd);
- thd->lex= $1;
- $$= $2;
-
- }
- ;
-
-
-package_implementation_routine_definition:
- FUNCTION_SYM package_specification_function
- package_implementation_function_body ';'
- {
- sp_package *pkg= Lex->get_sp_package();
- if (unlikely(pkg->add_routine_implementation($2)))
- MYSQL_YYABORT;
- pkg->m_current_routine= NULL;
- $$.init();
- }
- | PROCEDURE_SYM package_specification_procedure
- package_implementation_procedure_body ';'
- {
- sp_package *pkg= Lex->get_sp_package();
- if (unlikely(pkg->add_routine_implementation($2)))
- MYSQL_YYABORT;
- pkg->m_current_routine= NULL;
- $$.init();
- }
- | package_specification_element { $$.init(); }
- ;
-
-
-package_implementation_function_body:
- sp_tail_is remember_lex
- {
- sp_package *pkg= Lex->get_sp_package();
- sp_head *sp= pkg->m_current_routine->sphead;
- thd->lex= pkg->m_current_routine;
- sp->reset_thd_mem_root(thd);
- sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
- }
- sp_body opt_package_routine_end_name
- {
- if (unlikely(thd->lex->sp_body_finalize_function(thd) ||
- thd->lex->sphead->check_package_routine_end_name($5)))
- MYSQL_YYABORT;
- thd->lex= $2;
- }
- ;
-
-package_implementation_procedure_body:
- sp_tail_is remember_lex
- {
- sp_package *pkg= Lex->get_sp_package();
- sp_head *sp= pkg->m_current_routine->sphead;
- thd->lex= pkg->m_current_routine;
- sp->reset_thd_mem_root(thd);
- sp->set_body_start(thd, YYLIP->get_cpp_tok_start());
- }
- sp_body opt_package_routine_end_name
- {
- if (unlikely(thd->lex->sp_body_finalize_procedure(thd) ||
- thd->lex->sphead->check_package_routine_end_name($5)))
- MYSQL_YYABORT;
- thd->lex= $2;
- }
- ;
-
-
-package_implementation_item_declaration:
- sp_decl_vars ';'
- ;
-
-opt_package_specification_element_list:
- /* Empty */
- | package_specification_element_list
- ;
-
-package_specification_element_list:
- package_specification_element
- | package_specification_element_list package_specification_element
- ;
-
-package_specification_element:
- FUNCTION_SYM package_specification_function ';'
- {
- sp_package *pkg= Lex->get_sp_package();
- if (unlikely(pkg->add_routine_declaration($2)))
- MYSQL_YYABORT;
- pkg->m_current_routine= NULL;
- }
- | PROCEDURE_SYM package_specification_procedure ';'
- {
- sp_package *pkg= Lex->get_sp_package();
- if (unlikely(pkg->add_routine_declaration($2)))
- MYSQL_YYABORT;
- pkg->m_current_routine= NULL;
- }
- ;
-
-
-opt_sequence:
- /* empty */ { }
- | sequence_defs
- ;
-
-sequence_defs:
- sequence_def
- | sequence_defs sequence_def
- ;
-
-sequence_def:
- MINVALUE_SYM opt_equal longlong_num
- {
- Lex->create_info.seq_create_info->min_value= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value;
- }
- | NO_SYM MINVALUE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MINVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value;
- }
- | NOMINVALUE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MINVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value;
- }
- | MAXVALUE_SYM opt_equal longlong_num
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_max_value))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MAXVALUE"));
- Lex->create_info.seq_create_info->max_value= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value;
- }
- | NO_SYM MAXVALUE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MAXVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value;
- }
- | NOMAXVALUE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MAXVALUE"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value;
- }
- | START_SYM opt_with longlong_num
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_start))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "START"));
- Lex->create_info.seq_create_info->start= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_start;
- }
- | INCREMENT_SYM opt_by longlong_num
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_increment))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "INCREMENT"));
- Lex->create_info.seq_create_info->increment= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_increment;
- }
- | CACHE_SYM opt_equal longlong_num
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_cache))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CACHE"));
- Lex->create_info.seq_create_info->cache= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache;
- }
- | NOCACHE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_cache))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CACHE"));
- Lex->create_info.seq_create_info->cache= 0;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache;
- }
- | CYCLE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_cycle))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CYCLE"));
- Lex->create_info.seq_create_info->cycle= 1;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle;
- }
- | NOCYCLE_SYM
- {
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_cycle))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CYCLE"));
- Lex->create_info.seq_create_info->cycle= 0;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle;
- }
- | RESTART_SYM
- {
- if (unlikely(Lex->sql_command != SQLCOM_ALTER_SEQUENCE))
- {
- thd->parse_error(ER_SYNTAX_ERROR, "RESTART");
- YYABORT;
- }
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_restart))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "RESTART"));
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart;
- }
- | RESTART_SYM opt_with longlong_num
- {
- if (unlikely(Lex->sql_command != SQLCOM_ALTER_SEQUENCE))
- {
- thd->parse_error(ER_SYNTAX_ERROR, "RESTART");
- YYABORT;
- }
- if (unlikely(Lex->create_info.seq_create_info->used_fields &
- seq_field_used_restart))
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "RESTART"));
- Lex->create_info.seq_create_info->restart= $3;
- Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart | seq_field_used_restart_value;
- }
- ;
-
-server_def:
- SERVER_SYM opt_if_not_exists ident_or_text
- {
- if (unlikely(Lex->add_create_options_with_check($2)))
- MYSQL_YYABORT;
- Lex->server_options.reset($3);
- }
- FOREIGN DATA_SYM WRAPPER_SYM ident_or_text
- OPTIONS_SYM '(' server_options_list ')'
- { Lex->server_options.scheme= $8; }
- ;
-
-server_options_list:
- server_option
- | server_options_list ',' server_option
- ;
-
-server_option:
- USER_SYM TEXT_STRING_sys
- {
- MYSQL_YYABORT_UNLESS(Lex->server_options.username.str == 0);
- Lex->server_options.username= $2;
- }
- | HOST_SYM TEXT_STRING_sys
- {
- MYSQL_YYABORT_UNLESS(Lex->server_options.host.str == 0);
- Lex->server_options.host= $2;
- }
- | DATABASE TEXT_STRING_sys
- {
- MYSQL_YYABORT_UNLESS(Lex->server_options.db.str == 0);
- Lex->server_options.db= $2;
- }
- | OWNER_SYM TEXT_STRING_sys
- {
- MYSQL_YYABORT_UNLESS(Lex->server_options.owner.str == 0);
- Lex->server_options.owner= $2;
- }
- | PASSWORD_SYM TEXT_STRING_sys
- {
- MYSQL_YYABORT_UNLESS(Lex->server_options.password.str == 0);
- Lex->server_options.password= $2;
- }
- | SOCKET_SYM TEXT_STRING_sys
- {
- MYSQL_YYABORT_UNLESS(Lex->server_options.socket.str == 0);
- Lex->server_options.socket= $2;
- }
- | PORT_SYM ulong_num
- {
- Lex->server_options.port= $2;
- }
- ;
-
-event_tail:
- remember_name opt_if_not_exists sp_name
- {
- LEX *lex=Lex;
-
- lex->stmt_definition_begin= $1;
- if (unlikely(lex->add_create_options_with_check($2)))
- MYSQL_YYABORT;
- if (unlikely(!(lex->event_parse_data=
- Event_parse_data::new_instance(thd))))
- MYSQL_YYABORT;
- lex->event_parse_data->identifier= $3;
- lex->event_parse_data->on_completion=
- Event_parse_data::ON_COMPLETION_DROP;
-
- lex->sql_command= SQLCOM_CREATE_EVENT;
- /* We need that for disallowing subqueries */
- }
- ON SCHEDULE_SYM ev_schedule_time
- opt_ev_on_completion
- opt_ev_status
- opt_ev_comment
- DO_SYM ev_sql_stmt
- {
- /*
- sql_command is set here because some rules in ev_sql_stmt
- can overwrite it
- */
- Lex->sql_command= SQLCOM_CREATE_EVENT;
- }
- ;
-
-ev_schedule_time:
- EVERY_SYM expr interval
- {
- Lex->event_parse_data->item_expression= $2;
- Lex->event_parse_data->interval= $3;
- }
- ev_starts
- ev_ends
- | AT_SYM expr
- {
- Lex->event_parse_data->item_execute_at= $2;
- }
- ;
-
-opt_ev_status:
- /* empty */ { $$= 0; }
- | ENABLE_SYM
- {
- Lex->event_parse_data->status= Event_parse_data::ENABLED;
- Lex->event_parse_data->status_changed= true;
- $$= 1;
- }
- | DISABLE_SYM ON SLAVE
- {
- Lex->event_parse_data->status= Event_parse_data::SLAVESIDE_DISABLED;
- Lex->event_parse_data->status_changed= true;
- $$= 1;
- }
- | DISABLE_SYM
- {
- Lex->event_parse_data->status= Event_parse_data::DISABLED;
- Lex->event_parse_data->status_changed= true;
- $$= 1;
- }
- ;
-
-ev_starts:
- /* empty */
- {
- Item *item= new (thd->mem_root) Item_func_now_local(thd, 0);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- Lex->event_parse_data->item_starts= item;
- }
- | STARTS_SYM expr
- {
- Lex->event_parse_data->item_starts= $2;
- }
- ;
-
-ev_ends:
- /* empty */
- | ENDS_SYM expr
- {
- Lex->event_parse_data->item_ends= $2;
- }
- ;
-
-opt_ev_on_completion:
- /* empty */ { $$= 0; }
- | ev_on_completion
- ;
-
-ev_on_completion:
- ON COMPLETION_SYM opt_not PRESERVE_SYM
- {
- Lex->event_parse_data->on_completion= $3
- ? Event_parse_data::ON_COMPLETION_DROP
- : Event_parse_data::ON_COMPLETION_PRESERVE;
- $$= 1;
- }
- ;
-
-opt_ev_comment:
- /* empty */ { $$= 0; }
- | COMMENT_SYM TEXT_STRING_sys
- {
- Lex->comment= Lex->event_parse_data->comment= $2;
- $$= 1;
- }
- ;
-
-ev_sql_stmt:
- {
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- /*
- This stops the following :
- - CREATE EVENT ... DO CREATE EVENT ...;
- - ALTER EVENT ... DO CREATE EVENT ...;
- - CREATE EVENT ... DO ALTER EVENT DO ....;
- - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END|
- This allows:
- - CREATE EVENT ... DO DROP EVENT yyy;
- - CREATE EVENT ... DO ALTER EVENT yyy;
- (the nested ALTER EVENT can have anything but DO clause)
- - ALTER EVENT ... DO ALTER EVENT yyy;
- (the nested ALTER EVENT can have anything but DO clause)
- - ALTER EVENT ... DO DROP EVENT yyy;
- - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END|
- (the nested ALTER EVENT can have anything but DO clause)
- - CREATE PROCEDURE ... BEGIN DROP EVENT ... END|
- */
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0)));
-
- if (unlikely(!lex->make_sp_head(thd,
- lex->event_parse_data->identifier,
- &sp_handler_procedure,
- DEFAULT_AGGREGATE)))
- MYSQL_YYABORT;
-
- lex->sphead->set_body_start(thd, lip->get_cpp_ptr());
- }
- sp_proc_stmt
- {
- /* return back to the original memory root ASAP */
- if (Lex->sp_body_finalize_event(thd))
- MYSQL_YYABORT;
- }
- ;
-
-clear_privileges:
- /* Nothing */
- {
- LEX *lex=Lex;
- lex->users_list.empty();
- lex->columns.empty();
- lex->grant= lex->grant_tot_col= 0;
- lex->all_privileges= 0;
- lex->first_select_lex()->db= null_clex_str;
- lex->account_options.reset();
- }
- ;
-
-opt_aggregate:
- /* Empty */ { $$= NOT_AGGREGATE; }
- | AGGREGATE_SYM { $$= GROUP_AGGREGATE; }
- ;
-
-sp_name:
- ident '.' ident
- {
- if (unlikely(!($$= Lex->make_sp_name(thd, &$1, &$3))))
- MYSQL_YYABORT;
- }
- | ident
- {
- if (unlikely(!($$= Lex->make_sp_name(thd, &$1))))
- MYSQL_YYABORT;
- }
- ;
-
-opt_sp_name:
- /* Empty */ { $$= NULL; }
- | sp_name { $$= $1; }
- ;
-
-sp_a_chistics:
- /* Empty */ {}
- | sp_a_chistics sp_chistic {}
- ;
-
-sp_c_chistics:
- /* Empty */ {}
- | sp_c_chistics sp_c_chistic {}
- ;
-
-/* Characteristics for both create and alter */
-sp_chistic:
- COMMENT_SYM TEXT_STRING_sys
- { Lex->sp_chistics.comment= $2; }
- | LANGUAGE_SYM SQL_SYM
- { /* Just parse it, we only have one language for now. */ }
- | NO_SYM SQL_SYM
- { Lex->sp_chistics.daccess= SP_NO_SQL; }
- | CONTAINS_SYM SQL_SYM
- { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; }
- | READS_SYM SQL_SYM DATA_SYM
- { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; }
- | MODIFIES_SYM SQL_SYM DATA_SYM
- { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; }
- | sp_suid
- { Lex->sp_chistics.suid= $1; }
- ;
-
-create_package_chistic:
- COMMENT_SYM TEXT_STRING_sys
- { Lex->sp_chistics.comment= $2; }
- | sp_suid
- { Lex->sp_chistics.suid= $1; }
- ;
-
-create_package_chistics:
- create_package_chistic {}
- | create_package_chistics create_package_chistic { }
- ;
-
-opt_create_package_chistics:
- /* Empty */
- | create_package_chistics { }
- ;
-
-opt_create_package_chistics_init:
- { Lex->sp_chistics.init(); }
- opt_create_package_chistics
- ;
-
-/* Create characteristics */
-sp_c_chistic:
- sp_chistic { }
- | opt_not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= ! $1; }
- ;
-
-sp_suid:
- SQL_SYM SECURITY_SYM DEFINER_SYM { $$= SP_IS_SUID; }
- | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= SP_IS_NOT_SUID; }
- ;
-
-call:
- CALL_SYM sp_name
- {
- if (unlikely(Lex->call_statement_start(thd, $2)))
- MYSQL_YYABORT;
- }
- opt_sp_cparam_list {}
- ;
-
-/* CALL parameters */
-opt_sp_cparam_list:
- /* Empty */
- | '(' opt_sp_cparams ')'
- ;
-
-opt_sp_cparams:
- /* Empty */
- | sp_cparams
- ;
-
-sp_cparams:
- sp_cparams ',' expr
- {
- Lex->value_list.push_back($3, thd->mem_root);
- }
- | expr
- {
- Lex->value_list.push_back($1, thd->mem_root);
- }
- ;
-
-/* Stored FUNCTION parameter declaration list */
-sp_fdparam_list:
- /* Empty */
- {
- Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start();
- Lex->sphead->m_param_end= Lex->sphead->m_param_begin;
- }
- |
- {
- Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start();
- }
- sp_fdparams
- {
- Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
- }
- ;
-
-sp_fdparams:
- sp_fdparams ',' sp_param_name_and_type
- | sp_param_name_and_type
- ;
-
-sp_param_name:
- ident
- {
- if (unlikely(!($$= Lex->sp_param_init(&$1))))
- MYSQL_YYABORT;
- }
- ;
-
-sp_param_name_and_type:
- sp_param_name sp_param_type_with_opt_collate
- {
- if (unlikely(Lex->sp_param_fill_definition($$= $1)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
- {
- if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
- {
- if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4, $6)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_decl_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
- {
- if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
- {
- if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2, $4)))
- MYSQL_YYABORT;
- }
- | sp_param_name ROW_SYM row_type_body
- {
- if (unlikely(Lex->sphead->spvar_fill_row(thd, $$= $1, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-/* Stored PROCEDURE parameter declaration list */
-sp_pdparam_list:
- /* Empty */
- | sp_pdparams
- ;
-
-sp_pdparams:
- sp_pdparams ',' sp_pdparam
- | sp_pdparam
- ;
-
-sp_pdparam:
- sp_param_name sp_opt_inout sp_param_type_with_opt_collate
- {
- $1->mode= $2;
- if (unlikely(Lex->sp_param_fill_definition($1)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_opt_inout sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
- {
- $1->mode= $2;
- if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $1, $3, $5)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_opt_inout sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
- {
- $1->mode= $2;
- if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $1, $3, $5, $7)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_opt_inout sp_decl_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
- {
- $1->mode= $2;
- if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $1, $3)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_opt_inout sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
- {
- $1->mode= $2;
- if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $1, $3, $5)))
- MYSQL_YYABORT;
- }
- | sp_param_name sp_opt_inout ROW_SYM row_type_body
- {
- $1->mode= $2;
- if (unlikely(Lex->sphead->spvar_fill_row(thd, $1, $4)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_opt_inout:
- /* Empty */ { $$= sp_variable::MODE_IN; }
- | IN_SYM { $$= sp_variable::MODE_IN; }
- | OUT_SYM { $$= sp_variable::MODE_OUT; }
- | INOUT_SYM { $$= sp_variable::MODE_INOUT; }
- | IN_SYM OUT_SYM { $$= sp_variable::MODE_INOUT; }
- ;
-
-sp_parenthesized_pdparam_list:
- '('
- {
- Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1;
- }
- sp_pdparam_list
- ')'
- {
- Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start();
- }
- ;
-
-sp_no_param:
- /* Empty */
- {
- Lex->sphead->m_param_begin= Lex->sphead->m_param_end=
- YYLIP->get_cpp_tok_start() + 1;
- }
- ;
-
-opt_sp_parenthesized_fdparam_list:
- sp_no_param
- | '(' sp_fdparam_list ')'
- ;
-
-opt_sp_parenthesized_pdparam_list:
- sp_no_param
- | sp_parenthesized_pdparam_list
- ;
-
-sp_proc_stmts:
- /* Empty */ {}
- | sp_proc_stmts sp_proc_stmt ';'
- ;
-
-sp_proc_stmts1:
- sp_proc_stmt ';' {}
- | sp_proc_stmts1 sp_proc_stmt ';'
- ;
-
-sp_proc_stmts1_implicit_block:
- {
- Lex->sp_block_init(thd);
- }
- sp_proc_stmts1
- {
- if (unlikely(Lex->sp_block_finalize(thd)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_sp_decl_body_list:
- /* Empty */
- {
- $$.init();
- }
- | sp_decl_body_list { $$= $1; }
- ;
-
-sp_decl_body_list:
- sp_decl_non_handler_list
- {
- if (unlikely(Lex->sphead->sp_add_instr_cpush_for_cursors(thd, Lex->spcont)))
- MYSQL_YYABORT;
- }
- opt_sp_decl_handler_list
- {
- $$.join($1, $3);
- }
- | sp_decl_handler_list
- ;
-
-sp_decl_non_handler_list:
- sp_decl_non_handler ';' { $$= $1; }
- | sp_decl_non_handler_list sp_decl_non_handler ';'
- {
- $$.join($1, $2);
- }
- ;
-
-sp_decl_handler_list:
- sp_decl_handler ';' { $$= $1; }
- | sp_decl_handler_list sp_decl_handler ';'
- {
- $$.join($1, $2);
- }
- ;
-
-opt_sp_decl_handler_list:
- /* Empty*/ { $$.init(); }
- | sp_decl_handler_list
- ;
-
-optionally_qualified_column_ident:
- sp_decl_ident
- {
- if (unlikely(!($$= new (thd->mem_root)
- Qualified_column_ident(&$1))))
- MYSQL_YYABORT;
- }
- | sp_decl_ident '.' ident
- {
- if (unlikely(!($$= new (thd->mem_root)
- Qualified_column_ident(&$1, &$3))))
- MYSQL_YYABORT;
- }
- | sp_decl_ident '.' ident '.' ident
- {
- if (unlikely(!($$= new (thd->mem_root)
- Qualified_column_ident(thd, &$1, &$3, &$5))))
- MYSQL_YYABORT;
- }
- ;
-
-row_field_name:
- ident_directly_assignable
- {
- if (!($$= Lex->row_field_name(thd, $1)))
- MYSQL_YYABORT;
- }
- ;
-
-row_field_definition:
- row_field_name type_with_opt_collate
- ;
-
-row_field_definition_list:
- row_field_definition
- {
- if (!($$= Row_definition_list::make(thd->mem_root, $1)))
- MYSQL_YYABORT;
- }
- | row_field_definition_list ',' row_field_definition
- {
- if (($$= $1)->append_uniq(thd->mem_root, $3))
- MYSQL_YYABORT;
- }
- ;
-
-row_type_body:
- '(' row_field_definition_list ')' { $$= $2; }
- ;
-
-sp_decl_idents_init_vars:
- sp_decl_idents
- {
- Lex->sp_variable_declarations_init(thd, $1);
- }
- ;
-
-sp_decl_vars:
- sp_decl_idents_init_vars
- type_with_opt_collate
- sp_opt_default
- {
- if (unlikely(Lex->sp_variable_declarations_finalize(thd, $1,
- &Lex->last_field[0],
- $3)))
- MYSQL_YYABORT;
- $$.init_using_vars($1);
- }
- | sp_decl_idents_init_vars
- optionally_qualified_column_ident PERCENT_ORACLE_SYM TYPE_SYM
- sp_opt_default
- {
- if (unlikely(Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $2, $5)))
- MYSQL_YYABORT;
- $$.init_using_vars($1);
- }
- | sp_decl_idents_init_vars
- optionally_qualified_column_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
- sp_opt_default
- {
- if (unlikely(Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $2, $5)))
- MYSQL_YYABORT;
- $$.init_using_vars($1);
- }
- | sp_decl_idents_init_vars
- ROW_SYM row_type_body
- sp_opt_default
- {
- if (unlikely(Lex->sp_variable_declarations_row_finalize(thd, $1, $3, $4)))
- MYSQL_YYABORT;
- $$.init_using_vars($1);
- }
- ;
-
-sp_decl_non_handler:
- sp_decl_vars
- | ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
- {
- if (unlikely(Lex->spcont->declare_condition(thd, &$1, $4)))
- MYSQL_YYABORT;
- $$.vars= $$.hndlrs= $$.curs= 0;
- $$.conds= 1;
- }
- | ident_directly_assignable EXCEPTION_ORACLE_SYM
- {
- sp_condition_value *spcond= new (thd->mem_root)
- sp_condition_value_user_defined();
- if (unlikely(!spcond) ||
- unlikely(Lex->spcont->declare_condition(thd, &$1, spcond)))
- MYSQL_YYABORT;
- $$.vars= $$.hndlrs= $$.curs= 0;
- $$.conds= 1;
- }
- | CURSOR_SYM ident_directly_assignable
- {
- Lex->sp_block_init(thd);
- }
- opt_parenthesized_cursor_formal_parameters
- IS sp_cursor_stmt
- {
- sp_pcontext *param_ctx= Lex->spcont;
- if (unlikely(Lex->sp_block_finalize(thd)))
- MYSQL_YYABORT;
- if (unlikely(Lex->sp_declare_cursor(thd, &$2, $6, param_ctx, false)))
- MYSQL_YYABORT;
- $$.vars= $$.conds= $$.hndlrs= 0;
- $$.curs= 1;
- }
- ;
-
-sp_decl_handler:
- sp_handler_type HANDLER_SYM FOR_SYM
- {
- if (unlikely(Lex->sp_handler_declaration_init(thd, $1)))
- MYSQL_YYABORT;
- }
- sp_hcond_list sp_proc_stmt
- {
- if (unlikely(Lex->sp_handler_declaration_finalize(thd, $1)))
- MYSQL_YYABORT;
- $$.vars= $$.conds= $$.curs= 0;
- $$.hndlrs= 1;
- }
- ;
-
-opt_parenthesized_cursor_formal_parameters:
- /* Empty */
- | '(' sp_fdparams ')'
- ;
-
-
-sp_cursor_stmt_lex:
- {
- DBUG_ASSERT(thd->lex->sphead);
- if (unlikely(!($$= new (thd->mem_root)
- sp_lex_cursor(thd, thd->lex))))
- MYSQL_YYABORT;
- }
- ;
-
-sp_cursor_stmt:
- sp_cursor_stmt_lex
- {
- DBUG_ASSERT(thd->free_list == NULL);
- Lex->sphead->reset_lex(thd, $1);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- select
- {
- DBUG_ASSERT(Lex == $1);
- Lex->pop_select(); //main select
- if (unlikely($1->stmt_finalize(thd)) ||
- unlikely($1->sphead->restore_lex(thd)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-sp_handler_type:
- EXIT_MARIADB_SYM { $$= sp_handler::EXIT; }
- | CONTINUE_MARIADB_SYM { $$= sp_handler::CONTINUE; }
- | EXIT_ORACLE_SYM { $$= sp_handler::EXIT; }
- | CONTINUE_ORACLE_SYM { $$= sp_handler::CONTINUE; }
- /*| UNDO_SYM { QQ No yet } */
- ;
-
-sp_hcond_list:
- sp_hcond_element
- { $$= 1; }
- | sp_hcond_list ',' sp_hcond_element
- { $$+= 1; }
- ;
-
-sp_hcond_element:
- sp_hcond
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont->parent_context();
-
- if (unlikely(ctx->check_duplicate_handler($1)))
- my_yyabort_error((ER_SP_DUP_HANDLER, MYF(0)));
-
- sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
- i->add_condition($1);
- }
- ;
-
-sp_cond:
- ulong_num
- { /* mysql errno */
- if (unlikely($1 == 0))
- my_yyabort_error((ER_WRONG_VALUE, MYF(0), "CONDITION", "0"));
- $$= new (thd->mem_root) sp_condition_value($1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | sqlstate
- ;
-
-sqlstate:
- SQLSTATE_SYM opt_value TEXT_STRING_literal
- { /* SQLSTATE */
-
- /*
- 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 (unlikely(!is_sqlstate_valid(&$3) ||
- is_sqlstate_completion($3.str)))
- my_yyabort_error((ER_SP_BAD_SQLSTATE, MYF(0), $3.str));
- $$= new (thd->mem_root) sp_condition_value($3.str);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_value:
- /* Empty */ {}
- | VALUE_SYM {}
- ;
-
-sp_hcond:
- sp_cond
- {
- $$= $1;
- }
- | ident /* CONDITION name */
- {
- $$= Lex->spcont->find_declared_or_predefined_condition(thd, &$1);
- if (unlikely($$ == NULL))
- my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
- }
- | SQLWARNING_SYM /* SQLSTATEs 01??? */
- {
- $$= new (thd->mem_root) sp_condition_value(sp_condition_value::WARNING);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | not FOUND_SYM /* SQLSTATEs 02??? */
- {
- $$= new (thd->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SQLEXCEPTION_SYM /* All other SQLSTATEs */
- {
- $$= new (thd->mem_root) sp_condition_value(sp_condition_value::EXCEPTION);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | OTHERS_ORACLE_SYM /* All other SQLSTATEs */
- {
- $$= new (thd->mem_root) sp_condition_value(sp_condition_value::EXCEPTION);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-
-raise_stmt_oracle:
- RAISE_ORACLE_SYM opt_set_signal_information
- {
- if (unlikely(Lex->add_resignal_statement(thd, NULL)))
- MYSQL_YYABORT;
- }
- | RAISE_ORACLE_SYM signal_value opt_set_signal_information
- {
- if (unlikely(Lex->add_signal_statement(thd, $2)))
- MYSQL_YYABORT;
- }
- ;
-
-signal_stmt:
- SIGNAL_SYM signal_value opt_set_signal_information
- {
- if (Lex->add_signal_statement(thd, $2))
- MYSQL_YYABORT;
- }
- ;
-
-signal_value:
- ident
- {
- LEX *lex= Lex;
- sp_condition_value *cond;
-
- /* SIGNAL foo cannot be used outside of stored programs */
- if (unlikely(lex->spcont == NULL))
- my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
- cond= lex->spcont->find_declared_or_predefined_condition(thd, &$1);
- if (unlikely(cond == NULL))
- my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
- if (unlikely(!cond->has_sql_state()))
- my_yyabort_error((ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0)));
- $$= cond;
- }
- | sqlstate
- { $$= $1; }
- ;
-
-opt_signal_value:
- /* empty */
- { $$= NULL; }
- | signal_value
- { $$= $1; }
- ;
-
-opt_set_signal_information:
- /* empty */
- {
- thd->m_parser_state->m_yacc.m_set_signal_info.clear();
- }
- | SET signal_information_item_list
- ;
-
-signal_information_item_list:
- signal_condition_information_item_name '=' signal_allowed_expr
- {
- Set_signal_information *info;
- info= &thd->m_parser_state->m_yacc.m_set_signal_info;
- int index= (int) $1;
- info->clear();
- info->m_item[index]= $3;
- }
- | signal_information_item_list ','
- signal_condition_information_item_name '=' signal_allowed_expr
- {
- Set_signal_information *info;
- info= &thd->m_parser_state->m_yacc.m_set_signal_info;
- int index= (int) $3;
- if (unlikely(info->m_item[index] != NULL))
- my_yyabort_error((ER_DUP_SIGNAL_SET, MYF(0),
- Diag_condition_item_names[index].str));
- info->m_item[index]= $5;
- }
- ;
-
-/*
- Only a limited subset of <expr> are allowed in SIGNAL/RESIGNAL.
-*/
-signal_allowed_expr:
- literal
- { $$= $1; }
- | variable
- {
- if ($1->type() == Item::FUNC_ITEM)
- {
- Item_func *item= (Item_func*) $1;
- if (unlikely(item->functype() == Item_func::SUSERVAR_FUNC))
- {
- /*
- Don't allow the following syntax:
- SIGNAL/RESIGNAL ...
- SET <signal condition item name> = @foo := expr
- */
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- $$= $1;
- }
- | simple_ident
- { $$= $1; }
- ;
-
-/* conditions that can be set in signal / resignal */
-signal_condition_information_item_name:
- CLASS_ORIGIN_SYM
- { $$= DIAG_CLASS_ORIGIN; }
- | SUBCLASS_ORIGIN_SYM
- { $$= DIAG_SUBCLASS_ORIGIN; }
- | CONSTRAINT_CATALOG_SYM
- { $$= DIAG_CONSTRAINT_CATALOG; }
- | CONSTRAINT_SCHEMA_SYM
- { $$= DIAG_CONSTRAINT_SCHEMA; }
- | CONSTRAINT_NAME_SYM
- { $$= DIAG_CONSTRAINT_NAME; }
- | CATALOG_NAME_SYM
- { $$= DIAG_CATALOG_NAME; }
- | SCHEMA_NAME_SYM
- { $$= DIAG_SCHEMA_NAME; }
- | TABLE_NAME_SYM
- { $$= DIAG_TABLE_NAME; }
- | COLUMN_NAME_SYM
- { $$= DIAG_COLUMN_NAME; }
- | CURSOR_NAME_SYM
- { $$= DIAG_CURSOR_NAME; }
- | MESSAGE_TEXT_SYM
- { $$= DIAG_MESSAGE_TEXT; }
- | MYSQL_ERRNO_SYM
- { $$= DIAG_MYSQL_ERRNO; }
- ;
-
-resignal_stmt:
- RESIGNAL_SYM opt_signal_value opt_set_signal_information
- {
- if (unlikely(Lex->add_resignal_statement(thd, $2)))
- 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 (unlikely(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 (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | CONDITION_SYM condition_number condition_information
- {
- $$= new (thd->mem_root) Condition_information($2, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-statement_information:
- statement_information_item
- {
- $$= new (thd->mem_root) List<Statement_information_item>;
- if (unlikely($$ == NULL) ||
- unlikely($$->push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | statement_information ',' statement_information_item
- {
- if (unlikely($1->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-statement_information_item:
- simple_target_specification '=' statement_information_item_name
- {
- $$= new (thd->mem_root) Statement_information_item($3, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-simple_target_specification:
- ident_cli
- {
- if (unlikely(!($$= thd->lex->create_item_for_sp_var(&$1, NULL))))
- MYSQL_YYABORT;
- }
- | '@' ident_or_text
- {
- $$= new (thd->mem_root) Item_func_get_user_var(thd, &$2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-statement_information_item_name:
- NUMBER_MARIADB_SYM
- { $$= Statement_information_item::NUMBER; }
- | NUMBER_ORACLE_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 (unlikely($$ == NULL) ||
- unlikely($$->push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | condition_information ',' condition_information_item
- {
- if (unlikely($1->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-condition_information_item:
- simple_target_specification '=' condition_information_item_name
- {
- $$= new (thd->mem_root) Condition_information_item($3, $1);
- if (unlikely($$ == 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_ident:
- IDENT_sys
- | keyword_sp_decl
- {
- if (unlikely($$.copy_ident_cli(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_decl_idents:
- sp_decl_ident
- {
- /* NOTE: field definition is filled in sp_decl section. */
-
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (unlikely(spc->find_variable(&$1, TRUE)))
- my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $1.str));
- spc->add_variable(thd, &$1);
- $$= 1;
- }
- | sp_decl_idents ',' ident
- {
- /* NOTE: field definition is filled in sp_decl section. */
-
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
-
- if (unlikely(spc->find_variable(&$3, TRUE)))
- my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $3.str));
- spc->add_variable(thd, &$3);
- $$= $1 + 1;
- }
- ;
-
-sp_opt_default:
- /* Empty */ { $$ = NULL; }
- | DEFAULT expr { $$ = $2; }
- | SET_VAR expr { $$ = $2; }
- ;
-
-sp_proc_stmt:
- sp_labeled_block
- | sp_unlabeled_block
- | sp_labeled_control
- | sp_unlabeled_control
- | sp_labelable_stmt
- | labels_declaration_oracle sp_labelable_stmt {}
- ;
-
-sp_labelable_stmt:
- sp_proc_stmt_statement
- | sp_proc_stmt_continue_oracle
- | sp_proc_stmt_exit_oracle
- | sp_proc_stmt_leave
- | sp_proc_stmt_iterate
- | sp_proc_stmt_goto_oracle
- | sp_proc_stmt_open
- | sp_proc_stmt_fetch
- | sp_proc_stmt_close
- | sp_proc_stmt_return
- | sp_proc_stmt_if
- | case_stmt_specification
- | NULL_SYM { }
- ;
-
-sp_proc_stmt_compound_ok:
- sp_proc_stmt_if
- | case_stmt_specification
- | sp_unlabeled_block
- | sp_unlabeled_control
- ;
-
-sp_proc_stmt_if:
- IF_SYM
- {
- if (unlikely(Lex->maybe_start_compound_statement(thd)))
- MYSQL_YYABORT;
- Lex->sphead->new_cont_backpatch(NULL);
- }
- sp_if END IF_SYM
- { Lex->sphead->do_cont_backpatch(); }
- ;
-
-sp_statement:
- statement
- | ident_directly_assignable
- {
- // Direct procedure call (without the CALL keyword)
- if (unlikely(Lex->call_statement_start(thd, &$1)))
- MYSQL_YYABORT;
- }
- opt_sp_cparam_list
- | ident_directly_assignable '.' ident
- {
- if (unlikely(Lex->call_statement_start(thd, &$1, &$3)))
- MYSQL_YYABORT;
- }
- opt_sp_cparam_list
- ;
-
-sp_proc_stmt_statement:
- {
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- lex->sphead->reset_lex(thd);
- /*
- We should not push main select here, it will be done or not
- done by the statement, we just provide only new LEX for the
- statement here as if it is start of parsing new statement.
- */
- lex->sphead->m_tmp_query= lip->get_tok_start();
- }
- sp_statement
- {
- if (Lex->sp_proc_stmt_statement_finalize(thd, yychar == YYEMPTY) ||
- Lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- ;
-
-
-RETURN_ALLMODES_SYM:
- RETURN_MARIADB_SYM
- | RETURN_ORACLE_SYM
- ;
-
-sp_proc_stmt_return:
- RETURN_ALLMODES_SYM
- {
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- Lex->pop_select(); //main select
- if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, lex->spcont,
- $3, lex)) ||
- unlikely(sp->restore_lex(thd)))
- MYSQL_YYABORT;
- }
- | RETURN_ORACLE_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- if (unlikely(sp->m_handler->add_instr_preturn(thd, sp,
- lex->spcont)))
- MYSQL_YYABORT;
- }
- ;
-
-reset_lex_expr:
- {
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- $$= $2;
- Lex->pop_select(); //main select
- }
- ;
-
-sp_proc_stmt_exit_oracle:
- EXIT_ORACLE_SYM
- {
- if (unlikely(Lex->sp_exit_statement(thd, NULL)))
- MYSQL_YYABORT;
- }
- | EXIT_ORACLE_SYM label_ident
- {
- if (unlikely(Lex->sp_exit_statement(thd, &$2, NULL)))
- MYSQL_YYABORT;
- }
- | EXIT_ORACLE_SYM WHEN_SYM reset_lex_expr
- {
- if (Lex->sp_exit_statement(thd, $3) ||
- Lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- | EXIT_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
- {
- if (Lex->sp_exit_statement(thd, &$2, $4) ||
- Lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- ;
-
-sp_proc_stmt_continue_oracle:
- CONTINUE_ORACLE_SYM
- {
- if (unlikely(Lex->sp_continue_statement(thd, NULL)))
- MYSQL_YYABORT;
- }
- | CONTINUE_ORACLE_SYM label_ident
- {
- if (unlikely(Lex->sp_continue_statement(thd, &$2, NULL)))
- MYSQL_YYABORT;
- }
- | CONTINUE_ORACLE_SYM WHEN_SYM reset_lex_expr
- {
- if (Lex->sp_continue_statement(thd, $3) ||
- Lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- | CONTINUE_ORACLE_SYM label_ident WHEN_SYM reset_lex_expr
- {
- if (Lex->sp_continue_statement(thd, &$2, $4) ||
- Lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- ;
-
-
-sp_proc_stmt_leave:
- LEAVE_SYM label_ident
- {
- if (unlikely(Lex->sp_leave_statement(thd, &$2)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_proc_stmt_iterate:
- ITERATE_SYM label_ident
- {
- if (unlikely(Lex->sp_iterate_statement(thd, &$2)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_proc_stmt_goto_oracle:
- GOTO_ORACLE_SYM label_ident
- {
- if (unlikely(Lex->sp_goto_statement(thd, &$2)))
- MYSQL_YYABORT;
- }
- ;
-
-
-remember_lex:
- {
- $$= thd->lex;
- }
- ;
-
-assignment_source_lex:
- {
- DBUG_ASSERT(Lex->sphead);
- if (unlikely(!($$= new (thd->mem_root)
- sp_assignment_lex(thd, thd->lex))))
- MYSQL_YYABORT;
- }
- ;
-
-assignment_source_expr:
- assignment_source_lex
- {
- DBUG_ASSERT(thd->free_list == NULL);
- Lex->sphead->reset_lex(thd, $1);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- DBUG_ASSERT($1 == thd->lex);
- $$= $1;
- $$->sp_lex_in_use= true;
- $$->set_item_and_free_list($3, thd->free_list);
- thd->free_list= NULL;
- Lex->pop_select(); //main select
- if ($$->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- ;
-
-for_loop_bound_expr:
- assignment_source_lex
- {
- Lex->sphead->reset_lex(thd, $1);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- Lex->current_select->parsing_place= FOR_LOOP_BOUND;
- }
- expr
- {
- DBUG_ASSERT($1 == thd->lex);
- $$= $1;
- $$->sp_lex_in_use= true;
- $$->set_item_and_free_list($3, NULL);
- Lex->pop_select(); //main select
- if (unlikely($$->sphead->restore_lex(thd)))
- MYSQL_YYABORT;
- Lex->current_select->parsing_place= NO_MATTER;
- }
- ;
-
-cursor_actual_parameters:
- assignment_source_expr
- {
- if (unlikely(!($$= new (thd->mem_root) List<sp_assignment_lex>)))
- MYSQL_YYABORT;
- $$->push_back($1, thd->mem_root);
- }
- | cursor_actual_parameters ',' assignment_source_expr
- {
- $$= $1;
- $$->push_back($3, thd->mem_root);
- }
- ;
-
-opt_parenthesized_cursor_actual_parameters:
- /* Empty */ { $$= NULL; }
- | '(' cursor_actual_parameters ')' { $$= $2; }
- ;
-
-sp_proc_stmt_open:
- OPEN_SYM ident opt_parenthesized_cursor_actual_parameters
- {
- if (unlikely(Lex->sp_open_cursor(thd, &$2, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_proc_stmt_fetch_head:
- FETCH_SYM ident INTO
- {
- if (unlikely(Lex->sp_add_cfetch(thd, &$2)))
- MYSQL_YYABORT;
- }
- | FETCH_SYM FROM ident INTO
- {
- if (unlikely(Lex->sp_add_cfetch(thd, &$3)))
- MYSQL_YYABORT;
- }
- | FETCH_SYM NEXT_SYM FROM ident INTO
- {
- if (unlikely(Lex->sp_add_cfetch(thd, &$4)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_proc_stmt_fetch:
- sp_proc_stmt_fetch_head sp_fetch_list { }
- | FETCH_SYM GROUP_SYM NEXT_SYM ROW_SYM
- {
- if (unlikely(Lex->sp_add_agg_cfetch()))
- MYSQL_YYABORT;
- }
- ;
-
-sp_proc_stmt_close:
- CLOSE_SYM ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint offset;
- sp_instr_cclose *i;
-
- if (unlikely(!lex->spcont->find_cursor(&$2, &offset, false)))
- my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str));
- i= new (thd->mem_root)
- sp_instr_cclose(sp->instructions(), lex->spcont, offset);
- if (unlikely(i == NULL) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_fetch_list:
- ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *spc= lex->spcont;
- sp_variable *spv= likely(spc != NULL)
- ? spc->find_variable(&$1, false)
- : NULL;
- if (unlikely(!spv))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
-
- /* An SP local variable */
- sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
- i->add_to_varlist(spv);
- }
- | sp_fetch_list ',' ident
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *spc= lex->spcont;
- sp_variable *spv= likely(spc != NULL)
- ? spc->find_variable(&$3, false)
- : NULL;
- if (unlikely(!spv))
- my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $3.str));
-
- /* An SP local variable */
- sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
- i->add_to_varlist(spv);
- }
- ;
-
-sp_if:
- {
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr THEN_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i= new (thd->mem_root)
- sp_instr_jump_if_not(ip, ctx, $2, lex);
- if (unlikely(i == NULL) ||
- unlikely(sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0))) ||
- unlikely(sp->add_cont_backpatch(i)) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (unlikely(sp->restore_lex(thd)))
- MYSQL_YYABORT;
- }
- sp_proc_stmts1_implicit_block
- {
- sp_head *sp= Lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
- if (unlikely(i == NULL) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- sp->backpatch(ctx->pop_label());
- sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0));
- }
- sp_elseifs
- {
- LEX *lex= Lex;
-
- lex->sphead->backpatch(lex->spcont->pop_label());
- }
- ;
-
-sp_elseifs:
- /* Empty */
- | ELSIF_ORACLE_SYM sp_if
- | ELSE sp_proc_stmts1_implicit_block
- ;
-
-case_stmt_specification:
- CASE_SYM
- {
- if (unlikely(Lex->maybe_start_compound_statement(thd)))
- MYSQL_YYABORT;
-
- /**
- An example of the CASE statement in use is
- <pre>
- CREATE PROCEDURE proc_19194_simple(i int)
- BEGIN
- DECLARE str CHAR(10);
-
- CASE i
- WHEN 1 THEN SET str="1";
- WHEN 2 THEN SET str="2";
- WHEN 3 THEN SET str="3";
- ELSE SET str="unknown";
- END CASE;
-
- SELECT str;
- END
- </pre>
- The actions are used to generate the following code:
- <pre>
- SHOW PROCEDURE CODE proc_19194_simple;
- Pos Instruction
- 0 set str@1 NULL
- 1 set_case_expr (12) 0 i@0
- 2 jump_if_not 5(12) (case_expr@0 = 1)
- 3 set str@1 _latin1'1'
- 4 jump 12
- 5 jump_if_not 8(12) (case_expr@0 = 2)
- 6 set str@1 _latin1'2'
- 7 jump 12
- 8 jump_if_not 11(12) (case_expr@0 = 3)
- 9 set str@1 _latin1'3'
- 10 jump 12
- 11 set str@1 _latin1'unknown'
- 12 stmt 0 "SELECT str"
- </pre>
- */
-
- Lex->sphead->new_cont_backpatch(NULL);
-
- /*
- BACKPATCH: Creating target label for the jump to after END CASE
- (instruction 12 in the example)
- */
- Lex->spcont->push_label(thd, &empty_clex_str, Lex->sphead->instructions());
- }
- case_stmt_body
- else_clause_opt
- END
- CASE_SYM
- {
- /*
- BACKPATCH: Resolving forward jump from
- "case_stmt_action_then" to after END CASE
- (jump from instruction 4 to 12, 7 to 12 ... in the example)
- */
- Lex->sphead->backpatch(Lex->spcont->pop_label());
-
- if ($3)
- Lex->spcont->pop_case_expr_id();
-
- Lex->sphead->do_cont_backpatch();
- }
- ;
-
-case_stmt_body:
- {
- Lex->sphead->reset_lex(thd); /* For expr $2 */
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- if (unlikely(Lex->case_stmt_action_expr($2)))
- MYSQL_YYABORT;
-
- Lex->pop_select(); //main select
- if (Lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
- simple_when_clause_list
- { $$= 1; }
- | searched_when_clause_list
- { $$= 0; }
- ;
-
-simple_when_clause_list:
- simple_when_clause
- | simple_when_clause_list simple_when_clause
- ;
-
-searched_when_clause_list:
- searched_when_clause
- | searched_when_clause_list searched_when_clause
- ;
-
-simple_when_clause:
- WHEN_SYM
- {
- Lex->sphead->reset_lex(thd); /* For expr $3 */
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- /* Simple case: <caseval> = <whenval> */
-
- LEX *lex= Lex;
- if (unlikely(lex->case_stmt_action_when($3, true)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- /* For expr $3 */
- if (unlikely(lex->sphead->restore_lex(thd)))
- MYSQL_YYABORT;
- }
- THEN_SYM
- sp_proc_stmts1_implicit_block
- {
- if (unlikely(Lex->case_stmt_action_then()))
- MYSQL_YYABORT;
- }
- ;
-
-searched_when_clause:
- WHEN_SYM
- {
- Lex->sphead->reset_lex(thd); /* For expr $3 */
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr
- {
- LEX *lex= Lex;
- if (unlikely(lex->case_stmt_action_when($3, false)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- /* For expr $3 */
- if (unlikely(lex->sphead->restore_lex(thd)))
- MYSQL_YYABORT;
- }
- THEN_SYM
- sp_proc_stmts1_implicit_block
- {
- if (unlikely(Lex->case_stmt_action_then()))
- MYSQL_YYABORT;
- }
- ;
-
-else_clause_opt:
- /* empty */
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- uint ip= sp->instructions();
- sp_instr_error *i= new (thd->mem_root)
- sp_instr_error(ip, lex->spcont, ER_SP_CASE_NOT_FOUND);
- if (unlikely(i == NULL) ||
- unlikely(sp->add_instr(i)))
- MYSQL_YYABORT;
- }
- | ELSE sp_proc_stmts1_implicit_block
- ;
-
-sp_opt_label:
- /* Empty */ { $$= null_clex_str; }
- | label_ident { $$= $1; }
- ;
-
-sp_block_label:
- labels_declaration_oracle
- {
- if (unlikely(Lex->spcont->block_label_declare(&$1)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-sp_labeled_block:
- sp_block_label
- BEGIN_ORACLE_SYM
- {
- Lex->sp_block_init(thd, &$1);
- if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
- MYSQL_YYABORT;
- }
- sp_block_statements_and_exceptions
- END
- sp_opt_label
- {
- if (unlikely(Lex->sp_block_finalize(thd, Lex_spblock($4), &$6)))
- MYSQL_YYABORT;
- }
- | sp_block_label
- DECLARE_ORACLE_SYM
- {
- Lex->sp_block_init(thd, &$1);
- }
- opt_sp_decl_body_list
- {
- if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
- MYSQL_YYABORT;
- }
- BEGIN_ORACLE_SYM
- sp_block_statements_and_exceptions
- END
- sp_opt_label
- {
- $4.hndlrs+= $7.hndlrs;
- if (unlikely(Lex->sp_block_finalize(thd, $4, &$9)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_not_atomic:
- /* Empty */
- | not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */
- ;
-
-sp_unlabeled_block:
- BEGIN_ORACLE_SYM opt_not_atomic
- {
- if (unlikely(Lex->maybe_start_compound_statement(thd)))
- MYSQL_YYABORT;
- Lex->sp_block_init(thd);
- if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
- MYSQL_YYABORT;
- }
- sp_block_statements_and_exceptions
- END
- {
- if (unlikely(Lex->sp_block_finalize(thd, Lex_spblock($4))))
- MYSQL_YYABORT;
- }
- | DECLARE_ORACLE_SYM
- {
- if (unlikely(Lex->maybe_start_compound_statement(thd)))
- MYSQL_YYABORT;
- Lex->sp_block_init(thd);
- }
- opt_sp_decl_body_list
- {
- if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
- MYSQL_YYABORT;
- }
- BEGIN_ORACLE_SYM
- sp_block_statements_and_exceptions
- END
- {
- $3.hndlrs+= $6.hndlrs;
- if (unlikely(Lex->sp_block_finalize(thd, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_instr_addr:
- { $$= Lex->sphead->instructions(); }
- ;
-
-sp_body:
- {
- Lex->sp_block_init(thd);
- }
- opt_sp_decl_body_list
- {
- if (unlikely(Lex->sp_block_with_exceptions_finalize_declarations(thd)))
- MYSQL_YYABORT;
- }
- BEGIN_ORACLE_SYM
- sp_block_statements_and_exceptions
- {
- $2.hndlrs+= $5.hndlrs;
- if (unlikely(Lex->sp_block_finalize(thd, $2)))
- MYSQL_YYABORT;
- }
- END
- ;
-
-sp_block_statements_and_exceptions:
- sp_instr_addr
- sp_proc_stmts
- {
- if (unlikely(Lex->sp_block_with_exceptions_finalize_executable_section(thd, $1)))
- MYSQL_YYABORT;
- }
- opt_exception_clause
- {
- if (unlikely(Lex->sp_block_with_exceptions_finalize_exceptions(thd, $1, $4)))
- MYSQL_YYABORT;
- $$.init($4);
- }
- ;
-
-opt_exception_clause:
- /* Empty */ { $$= 0; }
- | EXCEPTION_ORACLE_SYM exception_handlers { $$= $2; }
- ;
-
-exception_handlers:
- exception_handler { $$= 1; }
- | exception_handlers exception_handler { $$= $1 + 1; }
- ;
-
-exception_handler:
- WHEN_SYM
- {
- if (unlikely(Lex->sp_handler_declaration_init(thd, sp_handler::EXIT)))
- MYSQL_YYABORT;
- }
- sp_hcond_list
- THEN_SYM
- sp_proc_stmts1_implicit_block
- {
- if (unlikely(Lex->sp_handler_declaration_finalize(thd, sp_handler::EXIT)))
- MYSQL_YYABORT;
- }
- ;
-
-/* This adds one shift/reduce conflict */
-opt_sp_for_loop_direction:
- /* Empty */ { $$= 1; }
- | REVERSE_SYM { $$= -1; }
- ;
-
-sp_for_loop_index_and_bounds:
- ident_directly_assignable
- {
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- sp_for_loop_bounds
- {
- Lex->pop_select(); //main select
- if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_for_loop_bounds:
- IN_SYM opt_sp_for_loop_direction for_loop_bound_expr
- DOT_DOT_SYM for_loop_bound_expr
- {
- $$= Lex_for_loop_bounds_intrange($2, $3, $5);
- }
- | IN_SYM opt_sp_for_loop_direction for_loop_bound_expr
- {
- $$.m_direction= $2;
- $$.m_index= $3;
- $$.m_target_bound= NULL;
- $$.m_implicit_cursor= false;
- }
- | IN_SYM opt_sp_for_loop_direction '(' sp_cursor_stmt ')'
- {
- if (unlikely(Lex->sp_for_loop_implicit_cursor_statement(thd, &$$,
- $4)))
- MYSQL_YYABORT;
- }
- ;
-
-loop_body:
- sp_proc_stmts1 END LOOP_SYM
- {
- LEX *lex= Lex;
- uint ip= lex->sphead->instructions();
- sp_label *lab= lex->spcont->last_label(); /* Jumping back */
- sp_instr_jump *i= new (thd->mem_root)
- sp_instr_jump(ip, lex->spcont, lab->ip);
- if (unlikely(i == NULL) ||
- unlikely(lex->sphead->add_instr(i)))
- MYSQL_YYABORT;
- }
- ;
-
-while_body:
- expr LOOP_SYM
- {
- LEX *lex= Lex;
- if (unlikely(lex->sp_while_loop_expression(thd, $1)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- if (lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- sp_proc_stmts1 END LOOP_SYM
- {
- if (unlikely(Lex->sp_while_loop_finalize(thd)))
- MYSQL_YYABORT;
- }
- ;
-
-repeat_body:
- sp_proc_stmts1 UNTIL_SYM
- {
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- expr END REPEAT_SYM
- {
- LEX *lex= Lex;
- uint ip= lex->sphead->instructions();
- sp_label *lab= lex->spcont->last_label(); /* Jumping back */
- sp_instr_jump_if_not *i= new (thd->mem_root)
- sp_instr_jump_if_not(ip, lex->spcont, $4, lab->ip, lex);
- if (unlikely(i == NULL) ||
- unlikely(lex->sphead->add_instr(i)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- /* We can shortcut the cont_backpatch here */
- i->m_cont_dest= ip+1;
- }
- ;
-
-pop_sp_loop_label:
- sp_opt_label
- {
- if (unlikely(Lex->sp_pop_loop_label(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_labeled_control:
- labels_declaration_oracle LOOP_SYM
- {
- if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
- MYSQL_YYABORT;
- }
- loop_body pop_sp_loop_label
- { }
- | labels_declaration_oracle WHILE_SYM
- {
- if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
- MYSQL_YYABORT;
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- while_body pop_sp_loop_label
- { }
- | labels_declaration_oracle FOR_SYM
- {
- // See "The FOR LOOP statement" comments in sql_lex.cc
- Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block
- }
- sp_for_loop_index_and_bounds
- {
- if (unlikely(Lex->sp_push_loop_label(thd, &$1))) // The inner WHILE block
- MYSQL_YYABORT;
- if (unlikely(Lex->sp_for_loop_condition_test(thd, $4)))
- MYSQL_YYABORT;
- }
- LOOP_SYM
- sp_proc_stmts1
- END LOOP_SYM
- {
- if (unlikely(Lex->sp_for_loop_finalize(thd, $4)))
- MYSQL_YYABORT;
- }
- pop_sp_loop_label // The inner WHILE block
- {
- if (unlikely(Lex->sp_for_loop_outer_block_finalize(thd, $4)))
- MYSQL_YYABORT;
- }
- | labels_declaration_oracle REPEAT_SYM
- {
- if (unlikely(Lex->sp_push_loop_label(thd, &$1)))
- MYSQL_YYABORT;
- }
- repeat_body pop_sp_loop_label
- { }
- ;
-
-sp_unlabeled_control:
- LOOP_SYM
- {
- if (unlikely(Lex->sp_push_loop_empty_label(thd)))
- MYSQL_YYABORT;
- }
- loop_body
- {
- Lex->sp_pop_loop_empty_label(thd);
- }
- | WHILE_SYM
- {
- if (unlikely(Lex->sp_push_loop_empty_label(thd)))
- MYSQL_YYABORT;
- Lex->sphead->reset_lex(thd);
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- while_body
- {
- Lex->sp_pop_loop_empty_label(thd);
- }
- | FOR_SYM
- {
- // See "The FOR LOOP statement" comments in sql_lex.cc
- if (unlikely(Lex->maybe_start_compound_statement(thd)))
- MYSQL_YYABORT;
- Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block
- }
- sp_for_loop_index_and_bounds
- {
- if (unlikely(Lex->sp_push_loop_empty_label(thd))) // The inner WHILE block
- MYSQL_YYABORT;
- if (unlikely(Lex->sp_for_loop_condition_test(thd, $3)))
- MYSQL_YYABORT;
- }
- LOOP_SYM
- sp_proc_stmts1
- END LOOP_SYM
- {
- if (unlikely(Lex->sp_for_loop_finalize(thd, $3)))
- MYSQL_YYABORT;
- Lex->sp_pop_loop_empty_label(thd); // The inner WHILE block
- if (unlikely(Lex->sp_for_loop_outer_block_finalize(thd, $3)))
- MYSQL_YYABORT;
- }
- | REPEAT_SYM
- {
- if (unlikely(Lex->sp_push_loop_empty_label(thd)))
- MYSQL_YYABORT;
- }
- repeat_body
- {
- Lex->sp_pop_loop_empty_label(thd);
- }
- ;
-
-trg_action_time:
- BEFORE_SYM
- { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; }
- | AFTER_SYM
- { Lex->trg_chistics.action_time= TRG_ACTION_AFTER; }
- ;
-
-trg_event:
- INSERT
- { Lex->trg_chistics.event= TRG_EVENT_INSERT; }
- | UPDATE_SYM
- { Lex->trg_chistics.event= TRG_EVENT_UPDATE; }
- | DELETE_SYM
- { Lex->trg_chistics.event= TRG_EVENT_DELETE; }
- ;
-/*
- This part of the parser contains common code for all TABLESPACE
- commands.
- CREATE TABLESPACE name ...
- ALTER TABLESPACE name CHANGE DATAFILE ...
- ALTER TABLESPACE name ADD DATAFILE ...
- ALTER TABLESPACE name access_mode
- CREATE LOGFILE GROUP_SYM name ...
- ALTER LOGFILE GROUP_SYM name ADD UNDOFILE ..
- ALTER LOGFILE GROUP_SYM name ADD REDOFILE ..
- DROP TABLESPACE name
- DROP LOGFILE GROUP_SYM name
-*/
-change_tablespace_access:
- tablespace_name
- ts_access_mode
- ;
-
-change_tablespace_info:
- tablespace_name
- CHANGE ts_datafile
- change_ts_option_list
- ;
-
-tablespace_info:
- tablespace_name
- ADD ts_datafile
- opt_logfile_group_name
- tablespace_option_list
- ;
-
-opt_logfile_group_name:
- /* empty */ {}
- | USE_SYM LOGFILE_SYM GROUP_SYM ident
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->logfile_group_name= $4.str;
- }
- ;
-
-alter_tablespace_info:
- tablespace_name
- ADD ts_datafile
- alter_tablespace_option_list
- {
- Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE;
- }
- | tablespace_name
- DROP ts_datafile
- alter_tablespace_option_list
- {
- Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE;
- }
- ;
-
-logfile_group_info:
- logfile_group_name
- add_log_file
- logfile_group_option_list
- ;
-
-alter_logfile_group_info:
- logfile_group_name
- add_log_file
- alter_logfile_group_option_list
- ;
-
-add_log_file:
- ADD lg_undofile
- | ADD lg_redofile
- ;
-
-change_ts_option_list:
- /* empty */ {}
- change_ts_options
- ;
-
-change_ts_options:
- change_ts_option
- | change_ts_options change_ts_option
- | change_ts_options ',' change_ts_option
- ;
-
-change_ts_option:
- opt_ts_initial_size
- | opt_ts_autoextend_size
- | opt_ts_max_size
- ;
-
-tablespace_option_list:
- tablespace_options
- ;
-
-tablespace_options:
- tablespace_option
- | tablespace_options tablespace_option
- | tablespace_options ',' tablespace_option
- ;
-
-tablespace_option:
- opt_ts_initial_size
- | opt_ts_autoextend_size
- | opt_ts_max_size
- | opt_ts_extent_size
- | opt_ts_nodegroup
- | opt_ts_engine
- | ts_wait
- | opt_ts_comment
- ;
-
-alter_tablespace_option_list:
- alter_tablespace_options
- ;
-
-alter_tablespace_options:
- alter_tablespace_option
- | alter_tablespace_options alter_tablespace_option
- | alter_tablespace_options ',' alter_tablespace_option
- ;
-
-alter_tablespace_option:
- opt_ts_initial_size
- | opt_ts_autoextend_size
- | opt_ts_max_size
- | opt_ts_engine
- | ts_wait
- ;
-
-logfile_group_option_list:
- logfile_group_options
- ;
-
-logfile_group_options:
- logfile_group_option
- | logfile_group_options logfile_group_option
- | logfile_group_options ',' logfile_group_option
- ;
-
-logfile_group_option:
- opt_ts_initial_size
- | opt_ts_undo_buffer_size
- | opt_ts_redo_buffer_size
- | opt_ts_nodegroup
- | opt_ts_engine
- | ts_wait
- | opt_ts_comment
- ;
-
-alter_logfile_group_option_list:
- alter_logfile_group_options
- ;
-
-alter_logfile_group_options:
- alter_logfile_group_option
- | alter_logfile_group_options alter_logfile_group_option
- | alter_logfile_group_options ',' alter_logfile_group_option
- ;
-
-alter_logfile_group_option:
- opt_ts_initial_size
- | opt_ts_engine
- | ts_wait
- ;
-
-
-ts_datafile:
- DATAFILE_SYM TEXT_STRING_sys
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->data_file_name= $2.str;
- }
- ;
-
-lg_undofile:
- UNDOFILE_SYM TEXT_STRING_sys
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->undo_file_name= $2.str;
- }
- ;
-
-lg_redofile:
- REDOFILE_SYM TEXT_STRING_sys
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->redo_file_name= $2.str;
- }
- ;
-
-tablespace_name:
- ident
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info= (new (thd->mem_root)
- st_alter_tablespace());
- if (unlikely(lex->alter_tablespace_info == NULL))
- MYSQL_YYABORT;
- lex->alter_tablespace_info->tablespace_name= $1.str;
- lex->sql_command= SQLCOM_ALTER_TABLESPACE;
- }
- ;
-
-logfile_group_name:
- ident
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info= (new (thd->mem_root)
- st_alter_tablespace());
- if (unlikely(lex->alter_tablespace_info == NULL))
- MYSQL_YYABORT;
- lex->alter_tablespace_info->logfile_group_name= $1.str;
- lex->sql_command= SQLCOM_ALTER_TABLESPACE;
- }
- ;
-
-ts_access_mode:
- READ_ONLY_SYM
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY;
- }
- | READ_WRITE_SYM
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE;
- }
- | NOT_SYM ACCESSIBLE_SYM
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_access_mode= TS_NOT_ACCESSIBLE;
- }
- ;
-
-opt_ts_initial_size:
- INITIAL_SIZE_SYM opt_equal size_number
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->initial_size= $3;
- }
- ;
-
-opt_ts_autoextend_size:
- AUTOEXTEND_SIZE_SYM opt_equal size_number
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->autoextend_size= $3;
- }
- ;
-
-opt_ts_max_size:
- MAX_SIZE_SYM opt_equal size_number
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->max_size= $3;
- }
- ;
-
-opt_ts_extent_size:
- EXTENT_SIZE_SYM opt_equal size_number
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->extent_size= $3;
- }
- ;
-
-opt_ts_undo_buffer_size:
- UNDO_BUFFER_SIZE_SYM opt_equal size_number
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->undo_buffer_size= $3;
- }
- ;
-
-opt_ts_redo_buffer_size:
- REDO_BUFFER_SIZE_SYM opt_equal size_number
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->redo_buffer_size= $3;
- }
- ;
-
-opt_ts_nodegroup:
- NODEGROUP_SYM opt_equal real_ulong_num
- {
- LEX *lex= Lex;
- if (unlikely(lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP))
- my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP"));
- lex->alter_tablespace_info->nodegroup_id= $3;
- }
- ;
-
-opt_ts_comment:
- COMMENT_SYM opt_equal TEXT_STRING_sys
- {
- LEX *lex= Lex;
- if (unlikely(lex->alter_tablespace_info->ts_comment != NULL))
- my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT"));
- lex->alter_tablespace_info->ts_comment= $3.str;
- }
- ;
-
-opt_ts_engine:
- opt_storage ENGINE_SYM opt_equal storage_engines
- {
- LEX *lex= Lex;
- if (unlikely(lex->alter_tablespace_info->storage_engine != NULL))
- my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE, MYF(0),
- "STORAGE ENGINE"));
- lex->alter_tablespace_info->storage_engine= $4;
- }
- ;
-
-opt_ts_wait:
- /* empty */
- | ts_wait
- ;
-
-ts_wait:
- WAIT_SYM
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->wait_until_completed= TRUE;
- }
- | NO_WAIT_SYM
- {
- LEX *lex= Lex;
- if (unlikely(!(lex->alter_tablespace_info->wait_until_completed)))
- my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT"));
- lex->alter_tablespace_info->wait_until_completed= FALSE;
- }
- ;
-
-size_number:
- real_ulonglong_num { $$= $1;}
- | IDENT_sys
- {
- if ($1.to_size_number(&$$))
- MYSQL_YYABORT;
- }
- ;
-
-/*
- End tablespace part
-*/
-
-create_body:
- create_field_list_parens
- { 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 {}
- | create_like
- {
-
- Lex->create_info.add(DDL_options_st::OPT_LIKE);
- TABLE_LIST *src_table= Lex->first_select_lex()->
- add_table_to_list(thd, $1, NULL, 0, TL_READ, MDL_SHARED_READ);
- if (unlikely(! src_table))
- MYSQL_YYABORT;
- /* CREATE TABLE ... LIKE is not allowed for views. */
- src_table->required_type= TABLE_TYPE_NORMAL;
- }
- ;
-
-create_like:
- LIKE table_ident { $$= $2; }
- | LEFT_PAREN_LIKE LIKE table_ident ')' { $$= $3; }
- ;
-
-opt_create_select:
- /* empty */ {}
- | opt_duplicate opt_as create_select_query_expression opt_versioning_option
- ;
-
-create_select_query_expression:
- query_expression
- {
- if (Lex->parsed_insert_select($1->first_select()))
- MYSQL_YYABORT;
- }
- | LEFT_PAREN_WITH with_clause query_expression_no_with_clause ')'
- {
- SELECT_LEX *first_select= $3->first_select();
- $3->set_with_clause($2);
- $2->attach_to(first_select);
- if (Lex->parsed_insert_select(first_select))
- MYSQL_YYABORT;
- }
- ;
-
-opt_create_partitioning:
- opt_partitioning
- {
- /*
- Remove all tables used in PARTITION clause from the global table
- list. Partitioning with subqueries is not allowed anyway.
- */
- TABLE_LIST *last_non_sel_table= Lex->create_last_non_select_table;
- last_non_sel_table->next_global= 0;
- Lex->query_tables_last= &last_non_sel_table->next_global;
- }
- ;
-
-/*
- This part of the parser is about handling of the partition information.
-
- It's first version was written by Mikael Ronstrm with lots of answers to
- questions provided by Antony Curtis.
-
- The partition grammar can be called from three places.
- 1) CREATE TABLE ... PARTITION ..
- 2) ALTER TABLE table_name PARTITION ...
- 3) PARTITION ...
-
- The first place is called when a new table is created from a MySQL client.
- The second place is called when a table is altered with the ALTER TABLE
- command from a MySQL client.
- The third place is called when opening an frm file and finding partition
- info in the .frm file. It is necessary to avoid allowing PARTITION to be
- an allowed entry point for SQL client queries. This is arranged by setting
- some state variables before arriving here.
-
- To be able to handle errors we will only set error code in this code
- and handle the error condition in the function calling the parser. This
- is necessary to ensure we can also handle errors when calling the parser
- from the openfrm function.
-*/
-opt_partitioning:
- /* empty */ {}
- | partitioning
- ;
-
-partitioning:
- PARTITION_SYM have_partitioning
- {
- LEX *lex= Lex;
- lex->part_info= new (thd->mem_root) partition_info();
- if (unlikely(!lex->part_info))
- MYSQL_YYABORT;
- if (lex->sql_command == SQLCOM_ALTER_TABLE)
- {
- lex->alter_info.partition_flags|= ALTER_PARTITION_INFO;
- }
- }
- partition
- ;
-
-have_partitioning:
- /* empty */
- {
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- LEX_CSTRING partition_name={STRING_WITH_LEN("partition")};
- if (unlikely(!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)))
- my_yyabort_error((ER_OPTION_PREVENTS_STATEMENT, MYF(0),
- "--skip-partition"));
-#else
- my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), "partitioning",
- "--with-plugin-partition"));
-#endif
- }
- ;
-
-partition_entry:
- PARTITION_SYM
- {
- if (unlikely(!Lex->part_info))
- {
- thd->parse_error(ER_PARTITION_ENTRY_ERROR);
- MYSQL_YYABORT;
- }
- DBUG_ASSERT(Lex->part_info->table);
- /*
- We enter here when opening the frm file to translate
- partition info string into part_info data structure.
- */
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- partition
- {
- Lex->pop_select(); //main select
- }
- ;
-
-partition:
- BY
- { Lex->safe_to_cache_query= 1; }
- part_type_def opt_num_parts opt_sub_part part_defs
- ;
-
-part_type_def:
- opt_linear KEY_SYM opt_key_algo '(' part_field_list ')'
- {
- partition_info *part_info= Lex->part_info;
- part_info->list_of_part_fields= TRUE;
- part_info->column_list= FALSE;
- part_info->part_type= HASH_PARTITION;
- }
- | opt_linear HASH_SYM
- { Lex->part_info->part_type= HASH_PARTITION; }
- part_func {}
- | RANGE_SYM part_func
- { Lex->part_info->part_type= RANGE_PARTITION; }
- | RANGE_SYM part_column_list
- { Lex->part_info->part_type= RANGE_PARTITION; }
- | LIST_SYM
- {
- Select->parsing_place= IN_PART_FUNC;
- }
- part_func
- {
- Lex->part_info->part_type= LIST_PARTITION;
- Select->parsing_place= NO_MATTER;
- }
- | LIST_SYM part_column_list
- { Lex->part_info->part_type= LIST_PARTITION; }
- | SYSTEM_TIME_SYM
- {
- if (unlikely(Lex->part_info->vers_init_info(thd)))
- MYSQL_YYABORT;
- }
- opt_versioning_rotation
- ;
-
-opt_linear:
- /* empty */ {}
- | LINEAR_SYM
- { Lex->part_info->linear_hash_ind= TRUE;}
- ;
-
-opt_key_algo:
- /* empty */
- { Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_NONE;}
- | ALGORITHM_SYM '=' real_ulong_num
- {
- switch ($3) {
- case 1:
- Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_51;
- break;
- case 2:
- Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55;
- break;
- default:
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- ;
-
-part_field_list:
- /* empty */ {}
- | part_field_item_list {}
- ;
-
-part_field_item_list:
- part_field_item {}
- | part_field_item_list ',' part_field_item {}
- ;
-
-part_field_item:
- ident
- {
- partition_info *part_info= Lex->part_info;
- part_info->num_columns++;
- if (unlikely(part_info->part_field_list.push_back($1.str,
- thd->mem_root)))
- MYSQL_YYABORT;
- if (unlikely(part_info->num_columns > MAX_REF_PARTS))
- my_yyabort_error((ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0),
- "list of partition fields"));
- }
- ;
-
-part_column_list:
- COLUMNS '(' part_field_list ')'
- {
- partition_info *part_info= Lex->part_info;
- part_info->column_list= TRUE;
- part_info->list_of_part_fields= TRUE;
- }
- ;
-
-
-part_func:
- '(' part_func_expr ')'
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->set_part_expr(thd, $2, FALSE)))
- MYSQL_YYABORT;
- part_info->num_columns= 1;
- part_info->column_list= FALSE;
- }
- ;
-
-sub_part_func:
- '(' part_func_expr ')'
- {
- if (unlikely(Lex->part_info->set_part_expr(thd, $2, TRUE)))
- MYSQL_YYABORT;
- }
- ;
-
-
-opt_num_parts:
- /* empty */ {}
- | PARTITIONS_SYM real_ulong_num
- {
- uint num_parts= $2;
- partition_info *part_info= Lex->part_info;
- if (unlikely(num_parts == 0))
- my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "partitions"));
-
- part_info->num_parts= num_parts;
- part_info->use_default_num_partitions= FALSE;
- }
- ;
-
-opt_sub_part:
- /* empty */ {}
- | SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func
- { Lex->part_info->subpart_type= HASH_PARTITION; }
- opt_num_subparts {}
- | SUBPARTITION_SYM BY opt_linear KEY_SYM opt_key_algo
- '(' sub_part_field_list ')'
- {
- partition_info *part_info= Lex->part_info;
- part_info->subpart_type= HASH_PARTITION;
- part_info->list_of_subpart_fields= TRUE;
- }
- opt_num_subparts {}
- ;
-
-sub_part_field_list:
- sub_part_field_item {}
- | sub_part_field_list ',' sub_part_field_item {}
- ;
-
-sub_part_field_item:
- ident
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->subpart_field_list.push_back($1.str,
- thd->mem_root)))
- MYSQL_YYABORT;
-
- if (unlikely(part_info->subpart_field_list.elements > MAX_REF_PARTS))
- my_yyabort_error((ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0),
- "list of subpartition fields"));
- }
- ;
-
-part_func_expr:
- bit_expr
- {
- if (unlikely(!Lex->safe_to_cache_query))
- {
- thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR);
- MYSQL_YYABORT;
- }
- $$=$1;
- }
- ;
-
-opt_num_subparts:
- /* empty */ {}
- | SUBPARTITIONS_SYM real_ulong_num
- {
- uint num_parts= $2;
- LEX *lex= Lex;
- if (unlikely(num_parts == 0))
- my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "subpartitions"));
- lex->part_info->num_subparts= num_parts;
- lex->part_info->use_default_num_subpartitions= FALSE;
- }
- ;
-
-part_defs:
- /* empty */
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->part_type == RANGE_PARTITION))
- my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0),
- "RANGE"));
- if (unlikely(part_info->part_type == LIST_PARTITION))
- my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0),
- "LIST"));
- }
- | '(' part_def_list ')'
- {
- partition_info *part_info= Lex->part_info;
- uint count_curr_parts= part_info->partitions.elements;
- if (part_info->num_parts != 0)
- {
- if (unlikely(part_info->num_parts !=
- count_curr_parts))
- {
- thd->parse_error(ER_PARTITION_WRONG_NO_PART_ERROR);
- MYSQL_YYABORT;
- }
- }
- else if (count_curr_parts > 0)
- {
- part_info->num_parts= count_curr_parts;
- }
- part_info->count_curr_subparts= 0;
- }
- ;
-
-part_def_list:
- part_definition {}
- | part_def_list ',' part_definition {}
- ;
-
-part_definition:
- PARTITION_SYM
- {
- partition_info *part_info= Lex->part_info;
- partition_element *p_elem= new (thd->mem_root) partition_element();
-
- if (unlikely(!p_elem) ||
- unlikely(part_info->partitions.push_back(p_elem, thd->mem_root)))
- MYSQL_YYABORT;
-
- p_elem->part_state= PART_NORMAL;
- p_elem->id= part_info->partitions.elements - 1;
- part_info->curr_part_elem= p_elem;
- part_info->current_partition= p_elem;
- part_info->use_default_partitions= FALSE;
- part_info->use_default_num_partitions= FALSE;
- }
- part_name
- opt_part_values
- opt_part_options
- opt_sub_partition
- {}
- ;
-
-part_name:
- ident
- {
- partition_info *part_info= Lex->part_info;
- partition_element *p_elem= part_info->curr_part_elem;
- if (unlikely(check_ident_length(&$1)))
- MYSQL_YYABORT;
- p_elem->partition_name= $1.str;
- }
- ;
-
-opt_part_values:
- /* empty */
- {
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- if (! lex->is_partition_management())
- {
- if (unlikely(part_info->error_if_requires_values()))
- MYSQL_YYABORT;
- if (unlikely(part_info->part_type == VERSIONING_PARTITION))
- my_yyabort_error((ER_VERS_WRONG_PARTS, MYF(0),
- lex->create_last_non_select_table->
- table_name.str));
- }
- else
- part_info->part_type= HASH_PARTITION;
- }
- | VALUES_LESS_SYM THAN_SYM
- {
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- if (! lex->is_partition_management())
- {
- if (unlikely(part_info->part_type != RANGE_PARTITION))
- my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
- "RANGE", "LESS THAN"));
- }
- else
- part_info->part_type= RANGE_PARTITION;
- }
- part_func_max {}
- | VALUES_IN_SYM
- {
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- if (! lex->is_partition_management())
- {
- if (unlikely(part_info->part_type != LIST_PARTITION))
- my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
- "LIST", "IN"));
- }
- else
- part_info->part_type= LIST_PARTITION;
- }
- part_values_in {}
- | CURRENT_SYM
- {
- if (Lex->part_values_current(thd))
- MYSQL_YYABORT;
- }
- | HISTORY_SYM
- {
- if (Lex->part_values_history(thd))
- MYSQL_YYABORT;
- }
- | DEFAULT
- {
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- if (! lex->is_partition_management())
- {
- if (unlikely(part_info->part_type != LIST_PARTITION))
- my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0),
- "LIST", "DEFAULT"));
- }
- else
- part_info->part_type= LIST_PARTITION;
- if (unlikely(part_info->init_column_part(thd)))
- MYSQL_YYABORT;
- if (unlikely(part_info->add_max_value(thd)))
- MYSQL_YYABORT;
- }
- ;
-
-part_func_max:
- MAXVALUE_SYM
- {
- partition_info *part_info= Lex->part_info;
-
- if (unlikely(part_info->num_columns &&
- part_info->num_columns != 1U))
- {
- part_info->print_debug("Kilroy II", NULL);
- thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR);
- MYSQL_YYABORT;
- }
- else
- part_info->num_columns= 1U;
- if (unlikely(part_info->init_column_part(thd)))
- MYSQL_YYABORT;
- if (unlikely(part_info->add_max_value(thd)))
- MYSQL_YYABORT;
- }
- | part_value_item {}
- ;
-
-part_values_in:
- part_value_item
- {
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- part_info->print_debug("part_values_in: part_value_item", NULL);
-
- if (part_info->num_columns != 1U)
- {
- if (unlikely(!lex->is_partition_management() ||
- part_info->num_columns == 0 ||
- part_info->num_columns > MAX_REF_PARTS))
- {
- part_info->print_debug("Kilroy III", NULL);
- thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR);
- MYSQL_YYABORT;
- }
- /*
- Reorganize the current large array into a list of small
- arrays with one entry in each array. This can happen
- in the first partition of an ALTER TABLE statement where
- we ADD or REORGANIZE partitions. Also can only happen
- for LIST partitions.
- */
- if (unlikely(part_info->reorganize_into_single_field_col_val(thd)))
- MYSQL_YYABORT;
- }
- }
- | '(' part_value_list ')'
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->num_columns < 2U))
- {
- thd->parse_error(ER_ROW_SINGLE_PARTITION_FIELD_ERROR);
- MYSQL_YYABORT;
- }
- }
- ;
-
-part_value_list:
- part_value_item {}
- | part_value_list ',' part_value_item {}
- ;
-
-part_value_item:
- '('
- {
- partition_info *part_info= Lex->part_info;
- part_info->print_debug("( part_value_item", NULL);
- /* Initialisation code needed for each list of value expressions */
- if (unlikely(!(part_info->part_type == LIST_PARTITION &&
- part_info->num_columns == 1U) &&
- part_info->init_column_part(thd)))
- MYSQL_YYABORT;
- }
- part_value_item_list {}
- ')'
- {
- partition_info *part_info= Lex->part_info;
- part_info->print_debug(") part_value_item", NULL);
- if (part_info->num_columns == 0)
- part_info->num_columns= part_info->curr_list_object;
- if (unlikely(part_info->num_columns != part_info->curr_list_object))
- {
- /*
- All value items lists must be of equal length, in some cases
- which is covered by the above if-statement we don't know yet
- how many columns is in the partition so the assignment above
- ensures that we only report errors when we know we have an
- error.
- */
- part_info->print_debug("Kilroy I", NULL);
- thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR);
- MYSQL_YYABORT;
- }
- part_info->curr_list_object= 0;
- }
- ;
-
-part_value_item_list:
- part_value_expr_item {}
- | part_value_item_list ',' part_value_expr_item {}
- ;
-
-part_value_expr_item:
- MAXVALUE_SYM
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->part_type == LIST_PARTITION))
- {
- thd->parse_error(ER_MAXVALUE_IN_VALUES_IN);
- MYSQL_YYABORT;
- }
- if (unlikely(part_info->add_max_value(thd)))
- MYSQL_YYABORT;
- }
- | bit_expr
- {
- LEX *lex= Lex;
- partition_info *part_info= lex->part_info;
- Item *part_expr= $1;
-
- if (unlikely(!lex->safe_to_cache_query))
- {
- thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR);
- MYSQL_YYABORT;
- }
- if (unlikely(part_info->add_column_list_value(thd, part_expr)))
- MYSQL_YYABORT;
- }
- ;
-
-
-opt_sub_partition:
- /* empty */
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->num_subparts != 0 &&
- !part_info->use_default_subpartitions))
- {
- /*
- We come here when we have defined subpartitions on the first
- partition but not on all the subsequent partitions.
- */
- thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
- MYSQL_YYABORT;
- }
- }
- | '(' sub_part_list ')'
- {
- partition_info *part_info= Lex->part_info;
- if (part_info->num_subparts != 0)
- {
- if (unlikely(part_info->num_subparts !=
- part_info->count_curr_subparts))
- {
- thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
- MYSQL_YYABORT;
- }
- }
- else if (part_info->count_curr_subparts > 0)
- {
- if (unlikely(part_info->partitions.elements > 1))
- {
- thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
- MYSQL_YYABORT;
- }
- part_info->num_subparts= part_info->count_curr_subparts;
- }
- part_info->count_curr_subparts= 0;
- }
- ;
-
-sub_part_list:
- sub_part_definition {}
- | sub_part_list ',' sub_part_definition {}
- ;
-
-sub_part_definition:
- SUBPARTITION_SYM
- {
- partition_info *part_info= Lex->part_info;
- partition_element *curr_part= part_info->current_partition;
- partition_element *sub_p_elem= new (thd->mem_root)
- partition_element(curr_part);
- if (unlikely(part_info->use_default_subpartitions &&
- part_info->partitions.elements >= 2))
- {
- /*
- create table t1 (a int)
- partition by list (a) subpartition by hash (a)
- (partition p0 values in (1),
- partition p1 values in (2) subpartition sp11);
- causes use to arrive since we are on the second
- partition, but still use_default_subpartitions
- is set. When we come here we're processing at least
- the second partition (the current partition processed
- have already been put into the partitions list.
- */
- thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR);
- MYSQL_YYABORT;
- }
- if (unlikely(!sub_p_elem) ||
- unlikely(curr_part->subpartitions.push_back(sub_p_elem, thd->mem_root)))
- MYSQL_YYABORT;
-
- sub_p_elem->id= curr_part->subpartitions.elements - 1;
- part_info->curr_part_elem= sub_p_elem;
- part_info->use_default_subpartitions= FALSE;
- part_info->use_default_num_subpartitions= FALSE;
- part_info->count_curr_subparts++;
- }
- sub_name opt_part_options {}
- ;
-
-sub_name:
- ident_or_text
- {
- if (unlikely(check_ident_length(&$1)))
- MYSQL_YYABORT;
- Lex->part_info->curr_part_elem->partition_name= $1.str;
- }
- ;
-
-opt_part_options:
- /* empty */ {}
- | opt_part_option_list {}
- ;
-
-opt_part_option_list:
- opt_part_option_list opt_part_option {}
- | opt_part_option {}
- ;
-
-opt_part_option:
- TABLESPACE opt_equal ident_or_text
- { Lex->part_info->curr_part_elem->tablespace_name= $3.str; }
- | opt_storage ENGINE_SYM opt_equal storage_engines
- {
- partition_info *part_info= Lex->part_info;
- part_info->curr_part_elem->engine_type= $4;
- part_info->default_engine_type= $4;
- }
- | CONNECTION_SYM opt_equal TEXT_STRING_sys
- {
- LEX *lex= Lex;
- lex->part_info->curr_part_elem->connect_string.str= $3.str;
- lex->part_info->curr_part_elem->connect_string.length= $3.length;
- }
- | NODEGROUP_SYM opt_equal real_ulong_num
- { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $3; }
- | MAX_ROWS opt_equal real_ulonglong_num
- { Lex->part_info->curr_part_elem->part_max_rows= (ha_rows) $3; }
- | MIN_ROWS opt_equal real_ulonglong_num
- { Lex->part_info->curr_part_elem->part_min_rows= (ha_rows) $3; }
- | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
- { Lex->part_info->curr_part_elem->data_file_name= $4.str; }
- | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
- { Lex->part_info->curr_part_elem->index_file_name= $4.str; }
- | COMMENT_SYM opt_equal TEXT_STRING_sys
- { Lex->part_info->curr_part_elem->part_comment= $3.str; }
- ;
-
-opt_versioning_rotation:
- /* empty */ {}
- | INTERVAL_SYM expr interval opt_versioning_interval_start
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->vers_set_interval(thd, $2, $3, $4)))
- MYSQL_YYABORT;
- }
- | LIMIT ulonglong_num
- {
- partition_info *part_info= Lex->part_info;
- if (unlikely(part_info->vers_set_limit($2)))
- {
- my_error(ER_PART_WRONG_VALUE, MYF(0),
- Lex->create_last_non_select_table->table_name.str,
- "LIMIT");
- MYSQL_YYABORT;
- }
- }
- ;
-
-
-opt_versioning_interval_start:
- /* empty */
- {
- $$= thd->query_start();
- }
- | STARTS_SYM ulong_num
- {
- /* only allowed from mysql_unpack_partition() */
- if (unlikely(!Lex->part_info->table))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $1.pos());
- MYSQL_YYABORT;
- }
- $$= (ulong)$2;
- }
- ;
-
-/*
- End of partition parser part
-*/
-
-opt_as:
- /* empty */ {}
- | AS {}
- ;
-
-opt_create_database_options:
- /* empty */ {}
- | create_database_options {}
- ;
-
-create_database_options:
- create_database_option {}
- | create_database_options create_database_option {}
- ;
-
-create_database_option:
- default_collation {}
- | default_charset {}
- ;
-
-opt_if_not_exists_table_element:
- /* empty */
- {
- Lex->check_exists= FALSE;
- }
- | IF_SYM not EXISTS
- {
- Lex->check_exists= TRUE;
- }
- ;
-
-opt_if_not_exists:
- /* empty */
- {
- $$.init();
- }
- | IF_SYM not EXISTS
- {
- $$.set(DDL_options_st::OPT_IF_NOT_EXISTS);
- }
- ;
-
-create_or_replace:
- CREATE /* empty */
- {
- $$.init();
- }
- | CREATE OR_SYM REPLACE
- {
- $$.set(DDL_options_st::OPT_OR_REPLACE);
- }
- ;
-
-opt_create_table_options:
- /* empty */
- | create_table_options
- ;
-
-create_table_options_space_separated:
- create_table_option
- | create_table_option create_table_options_space_separated
- ;
-
-create_table_options:
- create_table_option
- | create_table_option create_table_options
- | create_table_option ',' create_table_options
- ;
-
-create_table_option:
- ENGINE_SYM opt_equal ident_or_text
- {
- LEX *lex= Lex;
- if (!lex->m_sql_cmd)
- {
- DBUG_ASSERT(lex->sql_command == SQLCOM_ALTER_TABLE);
- if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table()))
- MYSQL_YYABORT;
- }
- Storage_engine_name *opt=
- lex->m_sql_cmd->option_storage_engine_name();
- DBUG_ASSERT(opt); // Expect a proper Sql_cmd
- *opt= Storage_engine_name($3);
- lex->create_info.used_fields|= HA_CREATE_USED_ENGINE;
- }
- | MAX_ROWS opt_equal ulonglong_num
- {
- Lex->create_info.max_rows= $3;
- Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;
- }
- | MIN_ROWS opt_equal ulonglong_num
- {
- Lex->create_info.min_rows= $3;
- Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;
- }
- | AVG_ROW_LENGTH opt_equal ulong_num
- {
- Lex->create_info.avg_row_length=$3;
- Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;
- }
- | PASSWORD_SYM opt_equal TEXT_STRING_sys
- {
- Lex->create_info.password=$3.str;
- Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD;
- }
- | COMMENT_SYM opt_equal TEXT_STRING_sys
- {
- Lex->create_info.comment=$3;
- Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT;
- }
- | AUTO_INC opt_equal ulonglong_num
- {
- Lex->create_info.auto_increment_value=$3;
- Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;
- }
- | PACK_KEYS_SYM opt_equal ulong_num
- {
- switch($3) {
- case 0:
- Lex->create_info.table_options|= HA_OPTION_NO_PACK_KEYS;
- break;
- case 1:
- Lex->create_info.table_options|= HA_OPTION_PACK_KEYS;
- break;
- default:
- thd->parse_error();
- MYSQL_YYABORT;
- }
- Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;
- }
- | PACK_KEYS_SYM opt_equal DEFAULT
- {
- Lex->create_info.table_options&=
- ~(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:
- thd->parse_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:
- thd->parse_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 (unlikely($3 == 0 || $3 > 0xffff))
- {
- thd->parse_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;
- Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM;
- }
- | TABLE_CHECKSUM_SYM opt_equal ulong_num
- {
- Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM;
- Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM;
- }
- | PAGE_CHECKSUM_SYM opt_equal choice
- {
- Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM;
- Lex->create_info.page_checksum= $3;
- }
- | DELAY_KEY_WRITE_SYM opt_equal ulong_num
- {
- Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE;
- Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE;
- }
- | ROW_FORMAT_SYM opt_equal row_types
- {
- Lex->create_info.row_type= $3;
- Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
- }
- | UNION_SYM opt_equal
- {
- Lex->first_select_lex()->table_list.save_and_clear(&Lex->save_list);
- }
- '(' opt_table_list ')'
- {
- /*
- Move the union list to the merge_list and exclude its tables
- from the global list.
- */
- LEX *lex=Lex;
- lex->create_info.merge_list= lex->first_select_lex()->table_list.first;
- lex->first_select_lex()->table_list= lex->save_list;
- /*
- When excluding union list from the global list we assume that
- elements of the former immediately follow elements which represent
- table being created/altered and parent tables.
- */
- TABLE_LIST *last_non_sel_table= lex->create_last_non_select_table;
- DBUG_ASSERT(last_non_sel_table->next_global ==
- lex->create_info.merge_list);
- last_non_sel_table->next_global= 0;
- Lex->query_tables_last= &last_non_sel_table->next_global;
-
- lex->create_info.used_fields|= HA_CREATE_USED_UNION;
- }
- | default_charset
- | default_collation
- | INSERT_METHOD opt_equal merge_insert_types
- {
- Lex->create_info.merge_insert_method= $3;
- Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;
- }
- | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
- {
- Lex->create_info.data_file_name= $4.str;
- Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR;
- }
- | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys
- {
- Lex->create_info.index_file_name= $4.str;
- Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR;
- }
- | TABLESPACE ident
- {Lex->create_info.tablespace= $2.str;}
- | STORAGE_SYM DISK_SYM
- {Lex->create_info.storage_media= HA_SM_DISK;}
- | STORAGE_SYM MEMORY_SYM
- {Lex->create_info.storage_media= HA_SM_MEMORY;}
- | CONNECTION_SYM opt_equal TEXT_STRING_sys
- {
- Lex->create_info.connect_string.str= $3.str;
- Lex->create_info.connect_string.length= $3.length;
- Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION;
- }
- | KEY_BLOCK_SIZE opt_equal ulong_num
- {
- Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE;
- Lex->create_info.key_block_size= $3;
- }
- | TRANSACTIONAL_SYM opt_equal choice
- {
- Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
- Lex->create_info.transactional= $3;
- }
- | IDENT_sys equal TEXT_STRING_sys
- {
- if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
- my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- (void) new (thd->mem_root)
- engine_option_value($1, $3, true,
- &Lex->create_info.option_list,
- &Lex->option_list_last);
- }
- | IDENT_sys equal ident
- {
- if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
- my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- (void) new (thd->mem_root)
- engine_option_value($1, $3, false,
- &Lex->create_info.option_list,
- &Lex->option_list_last);
- }
- | IDENT_sys equal real_ulonglong_num
- {
- (void) new (thd->mem_root)
- engine_option_value($1, $3, &Lex->create_info.option_list,
- &Lex->option_list_last, thd->mem_root);
- }
- | IDENT_sys equal DEFAULT
- {
- (void) new (thd->mem_root)
- engine_option_value($1, &Lex->create_info.option_list,
- &Lex->option_list_last);
- }
- | SEQUENCE_SYM opt_equal choice
- {
- Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE;
- Lex->create_info.sequence= ($3 == HA_CHOICE_YES);
- }
- | versioning_option
- ;
-
-opt_versioning_option:
- /* empty */
- | versioning_option
- ;
-
-versioning_option:
- WITH_SYSTEM_SYM VERSIONING_SYM
- {
- if (unlikely(Lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
- {
- if (DBUG_EVALUATE_IF("sysvers_force", 0, 1))
- {
- my_error(ER_VERS_NOT_SUPPORTED, MYF(0), "CREATE TEMPORARY TABLE");
- MYSQL_YYABORT;
- }
- }
- else
- {
- Lex->alter_info.flags|= ALTER_ADD_SYSTEM_VERSIONING;
- Lex->create_info.options|= HA_VERSIONED_TABLE;
- }
- }
- ;
-
-default_charset:
- opt_default charset opt_equal charset_name_or_default
- {
- if (unlikely(Lex->create_info.add_table_option_default_charset($4)))
- MYSQL_YYABORT;
- }
- ;
-
-default_collation:
- opt_default COLLATE_SYM opt_equal collation_name_or_default
- {
- HA_CREATE_INFO *cinfo= &Lex->create_info;
- if (unlikely((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
- cinfo->default_table_charset && $4 &&
- !($4= merge_charset_and_collation(cinfo->default_table_charset,
- $4))))
- MYSQL_YYABORT;
-
- Lex->create_info.default_table_charset= $4;
- Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
- }
- ;
-
-storage_engines:
- ident_or_text
- {
- if (Storage_engine_name($1).
- resolve_storage_engine_with_error(thd, &$$,
- thd->lex->create_info.tmp_table()))
- MYSQL_YYABORT;
- }
- ;
-
-known_storage_engines:
- ident_or_text
- {
- plugin_ref plugin;
- if (likely((plugin= ha_resolve_by_name(thd, &$1, false))))
- $$= plugin_hton(plugin);
- else
- my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str));
- }
- ;
-
-row_types:
- DEFAULT { $$= ROW_TYPE_DEFAULT; }
- | FIXED_SYM { $$= ROW_TYPE_FIXED; }
- | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; }
- | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }
- | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; }
- | COMPACT_SYM { $$= ROW_TYPE_COMPACT; }
- | PAGE_SYM { $$= ROW_TYPE_PAGE; }
- ;
-
-merge_insert_types:
- NO_SYM { $$= MERGE_INSERT_DISABLED; }
- | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; }
- | LAST_SYM { $$= MERGE_INSERT_TO_LAST; }
- ;
-
-udf_type:
- STRING_SYM {$$ = (int) STRING_RESULT; }
- | REAL {$$ = (int) REAL_RESULT; }
- | DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; }
- | INT_SYM {$$ = (int) INT_RESULT; }
- ;
-
-
-create_field_list:
- field_list
- {
- Lex->create_last_non_select_table= Lex->last_table();
- }
- ;
-
-create_field_list_parens:
- LEFT_PAREN_ALT field_list ')'
- {
- Lex->create_last_non_select_table= Lex->last_table();
- }
- ;
-
-field_list:
- field_list_item
- | field_list ',' field_list_item
- ;
-
-field_list_item:
- column_def { }
- | key_def
- | constraint_def
- | period_for_system_time
- | PERIOD_SYM period_for_application_time { }
- ;
-
-column_def:
- field_spec
- { $$= $1; }
- | field_spec references
- { $$= $1; }
- ;
-
-key_def:
- key_or_index opt_if_not_exists opt_ident opt_USING_key_algorithm
- {
- Lex->option_list= NULL;
- if (unlikely(Lex->add_key(Key::MULTIPLE, &$3, $4, $2)))
- MYSQL_YYABORT;
- }
- '(' key_list ')' normal_key_options { }
- | key_or_index opt_if_not_exists ident TYPE_SYM btree_or_rtree
- {
- Lex->option_list= NULL;
- if (unlikely(Lex->add_key(Key::MULTIPLE, &$3, $5, $2)))
- MYSQL_YYABORT;
- }
- '(' key_list ')' normal_key_options { }
- | fulltext opt_key_or_index opt_if_not_exists opt_ident
- {
- Lex->option_list= NULL;
- if (unlikely(Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)))
- MYSQL_YYABORT;
- }
- '(' key_list ')' fulltext_key_options { }
- | spatial opt_key_or_index opt_if_not_exists opt_ident
- {
- Lex->option_list= NULL;
- if (unlikely(Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)))
- MYSQL_YYABORT;
- }
- '(' key_list ')' spatial_key_options { }
- | opt_constraint constraint_key_type
- opt_if_not_exists opt_ident
- opt_USING_key_algorithm
- {
- Lex->option_list= NULL;
- if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $5, $3)))
- MYSQL_YYABORT;
- }
- '(' key_list ')' normal_key_options { }
- | opt_constraint constraint_key_type opt_if_not_exists ident
- TYPE_SYM btree_or_rtree
- {
- Lex->option_list= NULL;
- if (unlikely(Lex->add_key($2, $4.str ? &$4 : &$1, $6, $3)))
- MYSQL_YYABORT;
- }
- '(' key_list ')' normal_key_options { }
- | opt_constraint FOREIGN KEY_SYM opt_if_not_exists opt_ident
- {
- if (unlikely(Lex->check_add_key($4)) ||
- unlikely(!(Lex->last_key= (new (thd->mem_root)
- Key(Key::MULTIPLE,
- $1.str ? &$1 : &$5,
- HA_KEY_ALG_UNDEF, true, $4)))))
- MYSQL_YYABORT;
- Lex->option_list= NULL;
- }
- '(' key_list ')' references
- {
- LEX *lex=Lex;
- Key *key= (new (thd->mem_root)
- Foreign_key($5.str ? &$5 : &$1,
- &lex->last_key->columns,
- &$10->db,
- &$10->table,
- &lex->ref_list,
- lex->fk_delete_opt,
- lex->fk_update_opt,
- lex->fk_match_option,
- $4));
- if (unlikely(key == NULL))
- MYSQL_YYABORT;
- /*
- handle_if_exists_options() expectes the two keys in this order:
- the Foreign_key, followed by its auto-generated Key.
- */
- lex->alter_info.key_list.push_back(key, thd->mem_root);
- lex->alter_info.key_list.push_back(Lex->last_key, thd->mem_root);
- lex->option_list= NULL;
-
- /* Only used for ALTER TABLE. Ignored otherwise. */
- lex->alter_info.flags|= ALTER_ADD_FOREIGN_KEY;
- }
- ;
-
-constraint_def:
- opt_constraint check_constraint
- {
- Lex->add_constraint($1, $2, FALSE);
- }
- ;
-
-period_for_system_time:
- // If FOR_SYM is followed by SYSTEM_TIME_SYM then they are merged to: FOR_SYSTEM_TIME_SYM .
- PERIOD_SYM FOR_SYSTEM_TIME_SYM '(' ident ',' ident ')'
- {
- Vers_parse_info &info= Lex->vers_get_info();
- info.set_period($4, $6);
- }
- ;
-
-period_for_application_time:
- FOR_SYM ident '(' ident ',' ident ')'
- {
- if (Lex->add_period($2, $4, $6))
- MYSQL_YYABORT;
- }
- ;
-
-opt_check_constraint:
- /* empty */ { $$= (Virtual_column_info*) 0; }
- | check_constraint { $$= $1;}
- ;
-
-check_constraint:
- CHECK_SYM '(' expr ')'
- {
- Virtual_column_info *v= add_virtual_expression(thd, $3);
- if (unlikely(!v))
- MYSQL_YYABORT;
- $$= v;
- }
- ;
-
-opt_constraint_no_id:
- /* Empty */ {}
- | CONSTRAINT {}
- ;
-
-opt_constraint:
- /* empty */ { $$= null_clex_str; }
- | constraint { $$= $1; }
- ;
-
-constraint:
- CONSTRAINT opt_ident { $$=$2; }
- ;
-
-field_spec:
- field_ident
- {
- LEX *lex=Lex;
- Create_field *f= new (thd->mem_root) Create_field();
-
- if (unlikely(check_string_char_length(&$1, 0, NAME_CHAR_LEN,
- system_charset_info, 1)))
- my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str));
-
- if (unlikely(!f))
- MYSQL_YYABORT;
-
- lex->init_last_field(f, &$1, NULL);
- $<create_field>$= f;
- lex->parsing_options.lookup_keywords_after_qualifier= true;
- }
- field_type_or_serial opt_check_constraint
- {
- LEX *lex=Lex;
- lex->parsing_options.lookup_keywords_after_qualifier= false;
- $$= $<create_field>2;
-
- $$->check_constraint= $4;
-
- if (unlikely($$->check(thd)))
- MYSQL_YYABORT;
-
- lex->alter_info.create_list.push_back($$, thd->mem_root);
-
- $$->create_if_not_exists= Lex->check_exists;
- if ($$->flags & PRI_KEY_FLAG)
- lex->add_key_to_list(&$1, Key::PRIMARY, lex->check_exists);
- else if ($$->flags & UNIQUE_KEY_FLAG)
- lex->add_key_to_list(&$1, Key::UNIQUE, lex->check_exists);
- }
- ;
-
-field_type_or_serial:
- qualified_field_type { Lex->last_field->set_attributes($1, Lex->charset); }
- field_def
- | SERIAL_SYM
- {
- Lex->last_field->set_handler(&type_handler_longlong);
- Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG
- | UNSIGNED_FLAG | UNIQUE_KEY_FLAG;
- }
- opt_serial_attribute
- ;
-
-opt_serial_attribute:
- /* empty */ {}
- | opt_serial_attribute_list {}
- ;
-
-opt_serial_attribute_list:
- opt_serial_attribute_list serial_attribute {}
- | serial_attribute
- ;
-
-opt_asrow_attribute:
- /* empty */ {}
- | opt_asrow_attribute_list {}
- ;
-
-opt_asrow_attribute_list:
- opt_asrow_attribute_list asrow_attribute {}
- | asrow_attribute
- ;
-
-field_def:
- /* empty */ { }
- | attribute_list
- | attribute_list compressed_deprecated_column_attribute
- | attribute_list compressed_deprecated_column_attribute attribute_list
- | opt_generated_always AS virtual_column_func
- {
- Lex->last_field->vcol_info= $3;
- Lex->last_field->flags&= ~NOT_NULL_FLAG; // undo automatic NOT NULL for timestamps
- }
- vcol_opt_specifier vcol_opt_attribute
- | opt_generated_always AS ROW_SYM START_SYM opt_asrow_attribute
- {
- if (Lex->last_field_generated_always_as_row_start())
- MYSQL_YYABORT;
- }
- | opt_generated_always AS ROW_SYM END opt_asrow_attribute
- {
- if (Lex->last_field_generated_always_as_row_end())
- MYSQL_YYABORT;
- }
- ;
-
-opt_generated_always:
- /* empty */ {}
- | GENERATED_SYM ALWAYS_SYM {}
- ;
-
-vcol_opt_specifier:
- /* empty */
- {
- Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE);
- }
- | VIRTUAL_SYM
- {
- Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE);
- }
- | PERSISTENT_SYM
- {
- Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE);
- }
- | STORED_SYM
- {
- Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE);
- }
- ;
-
-vcol_opt_attribute:
- /* empty */ {}
- | vcol_opt_attribute_list {}
- ;
-
-vcol_opt_attribute_list:
- vcol_opt_attribute_list vcol_attribute {}
- | vcol_attribute
- ;
-
-vcol_attribute:
- UNIQUE_SYM
- {
- LEX *lex=Lex;
- lex->last_field->flags|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | UNIQUE_SYM KEY_SYM
- {
- LEX *lex=Lex;
- lex->last_field->flags|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; }
- | INVISIBLE_SYM
- {
- Lex->last_field->invisible= INVISIBLE_USER;
- }
- ;
-
-parse_vcol_expr:
- PARSE_VCOL_EXPR_SYM
- {
- /*
- "PARSE_VCOL_EXPR" can only be used by the SQL server
- when reading a '*.frm' file.
- Prevent the end user from invoking this command.
- */
- MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr);
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- expr
- {
- Virtual_column_info *v= add_virtual_expression(thd, $3);
- if (unlikely(!v))
- MYSQL_YYABORT;
- Lex->last_field->vcol_info= v;
- Lex->pop_select(); //main select
- }
- ;
-
-parenthesized_expr:
- expr
- | expr ',' expr_list
- {
- $3->push_front($1, thd->mem_root);
- $$= new (thd->mem_root) Item_row(thd, *$3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-virtual_column_func:
- '(' parenthesized_expr ')'
- {
- Virtual_column_info *v=
- add_virtual_expression(thd, $2);
- if (unlikely(!v))
- MYSQL_YYABORT;
- $$= v;
- }
- | subquery
- {
- Item *item;
- if (!(item= new (thd->mem_root) Item_singlerow_subselect(thd, $1)))
- MYSQL_YYABORT;
- Virtual_column_info *v= add_virtual_expression(thd, item);
- if (unlikely(!v))
- MYSQL_YYABORT;
- $$= v;
- }
- ;
-
-expr_or_literal: column_default_non_parenthesized_expr | signed_literal ;
-
-column_default_expr:
- virtual_column_func
- | expr_or_literal
- {
- if (unlikely(!($$= add_virtual_expression(thd, $1))))
- MYSQL_YYABORT;
- }
- ;
-
-qualified_field_type:
- field_type
- {
- Lex->map_data_type(Lex_ident_sys(), &($$= $1));
- }
- | sp_decl_ident '.' field_type
- {
- if (Lex->map_data_type($1, &($$= $3)))
- MYSQL_YYABORT;
- }
- ;
-
-field_type:
- field_type_numeric
- | field_type_temporal
- | field_type_string
- | field_type_lob
- | field_type_misc
- ;
-
-
-sp_param_field_type:
- field_type_numeric
- | field_type_temporal
- | sp_param_field_type_string
- | field_type_lob
- | field_type_misc
- ;
-
-
-field_type_numeric:
- int_type opt_field_length field_options { $$.set($1, $2); }
- | real_type opt_precision field_options { $$.set($1, $2); }
- | FLOAT_SYM float_options field_options
- {
- $$.set(&type_handler_float, $2);
- if ($2.length() && !$2.dec())
- {
- int err;
- ulonglong tmp_length= my_strtoll10($2.length(), NULL, &err);
- if (unlikely(err || tmp_length > PRECISION_FOR_DOUBLE))
- my_yyabort_error((ER_WRONG_FIELD_SPEC, MYF(0),
- Lex->last_field->field_name.str));
- if (tmp_length > PRECISION_FOR_FLOAT)
- $$.set(&type_handler_double);
- else
- $$.set(&type_handler_float);
- }
- }
- | BIT_SYM opt_field_length_default_1
- {
- $$.set(&type_handler_bit, $2);
- }
- | BOOL_SYM
- {
- $$.set(&type_handler_tiny, "1");
- }
- | BOOLEAN_SYM
- {
- $$.set(&type_handler_tiny, "1");
- }
- | DECIMAL_SYM float_options field_options
- { $$.set(&type_handler_newdecimal, $2);}
- | NUMBER_ORACLE_SYM float_options field_options
- {
- if ($2.length() != 0)
- $$.set(&type_handler_newdecimal, $2);
- else
- $$.set(&type_handler_double);
- }
- | NUMERIC_SYM float_options field_options
- { $$.set(&type_handler_newdecimal, $2);}
- | FIXED_SYM float_options field_options
- { $$.set(&type_handler_newdecimal, $2);}
- ;
-
-
-opt_binary_and_compression:
- /* empty */
- | binary
- | binary compressed_deprecated_data_type_attribute
- | compressed opt_binary
- ;
-
-field_type_string:
- char opt_field_length_default_1 opt_binary
- {
- $$.set(&type_handler_string, $2);
- }
- | nchar opt_field_length_default_1 opt_bin_mod
- {
- $$.set(&type_handler_string, $2);
- bincmp_collation(national_charset_info, $3);
- }
- | BINARY opt_field_length_default_1
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_string, $2);
- }
- | varchar field_length opt_binary_and_compression
- {
- $$.set(&type_handler_varchar, $2);
- }
- | VARCHAR2_ORACLE_SYM field_length opt_binary_and_compression
- {
- $$.set(&type_handler_varchar, $2);
- }
- | nvarchar field_length opt_compressed opt_bin_mod
- {
- $$.set(&type_handler_varchar, $2);
- bincmp_collation(national_charset_info, $4);
- }
- | VARBINARY field_length opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_varchar, $2);
- }
- | RAW_ORACLE_SYM field_length opt_compressed
- {
- Lex->charset= &my_charset_bin;
- $$.set(&type_handler_varchar, $2);
- }
- ;
-
-
-sp_param_field_type_string:
- char opt_field_length_default_sp_param_char opt_binary
- {
- $$.set(&type_handler_varchar, $2);
- }
- | nchar opt_field_length_default_sp_param_char opt_bin_mod
- {
- $$.set(&type_handler_varchar, $2);
- bincmp_collation(national_charset_info, $3);
- }
- | BINARY opt_field_length_default_sp_param_char
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_varchar, $2);
- }
- | varchar opt_field_length_default_sp_param_varchar opt_binary
- {
- $$.set(&type_handler_varchar, $2);
- }
- | VARCHAR2_ORACLE_SYM opt_field_length_default_sp_param_varchar opt_binary
- {
- $$.set(&type_handler_varchar, $2);
- }
- | nvarchar opt_field_length_default_sp_param_varchar opt_bin_mod
- {
- $$.set(&type_handler_varchar, $2);
- bincmp_collation(national_charset_info, $3);
- }
- | VARBINARY opt_field_length_default_sp_param_varchar
- {
- Lex->charset= &my_charset_bin;
- $$.set(&type_handler_varchar, $2);
- }
- | RAW_ORACLE_SYM opt_field_length_default_sp_param_varchar
- {
- Lex->charset= &my_charset_bin;
- $$.set(&type_handler_varchar, $2);
- }
- ;
-
-
-field_type_temporal:
- YEAR_SYM opt_field_length field_options
- {
- if ($2)
- {
- errno= 0;
- ulong length= strtoul($2, NULL, 10);
- if (errno == 0 && length <= MAX_FIELD_BLOBLENGTH && length != 4)
- {
- char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
- my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_WARN_DEPRECATED_SYNTAX,
- ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
- buff, "YEAR(4)");
- }
- }
- $$.set(&type_handler_year, $2);
- }
- | DATE_SYM { $$.set(&type_handler_newdate); }
- | TIME_SYM opt_field_length
- {
- $$.set(opt_mysql56_temporal_format ?
- static_cast<const Type_handler*>(&type_handler_time2) :
- static_cast<const Type_handler*>(&type_handler_time),
- $2);
- }
- | TIMESTAMP opt_field_length
- {
- $$.set(opt_mysql56_temporal_format ?
- static_cast<const Type_handler*>(&type_handler_timestamp2):
- static_cast<const Type_handler*>(&type_handler_timestamp),
- $2);
- }
- | DATETIME opt_field_length
- {
- $$.set(thd->type_handler_for_datetime(), $2);
- }
- ;
-
-
-field_type_lob:
- TINYBLOB opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_tiny_blob);
- }
- | BLOB_MARIADB_SYM opt_field_length opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_blob, $2);
- }
- | BLOB_ORACLE_SYM field_length opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_blob, $2);
- }
- | BLOB_ORACLE_SYM opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_long_blob);
- }
- | spatial_type float_options srid_option
- {
-#ifdef HAVE_SPATIAL
- Lex->charset=&my_charset_bin;
- Lex->last_field->geom_type= $1;
- $$.set(&type_handler_geometry, $2);
-#else
- my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name,
- sym_group_geom.needed_define));
-#endif
- }
- | MEDIUMBLOB opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_medium_blob);
- }
- | LONGBLOB opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_long_blob);
- }
- | LONG_SYM VARBINARY opt_compressed
- {
- Lex->charset=&my_charset_bin;
- $$.set(&type_handler_medium_blob);
- }
- | LONG_SYM varchar opt_binary_and_compression
- { $$.set(&type_handler_medium_blob); }
- | TINYTEXT opt_binary_and_compression
- { $$.set(&type_handler_tiny_blob); }
- | TEXT_SYM opt_field_length opt_binary_and_compression
- { $$.set(&type_handler_blob, $2); }
- | MEDIUMTEXT opt_binary_and_compression
- { $$.set(&type_handler_medium_blob); }
- | LONGTEXT opt_binary_and_compression
- { $$.set(&type_handler_long_blob); }
- | CLOB_ORACLE_SYM opt_binary_and_compression
- { $$.set(&type_handler_long_blob); }
- | LONG_SYM opt_binary_and_compression
- { $$.set(&type_handler_medium_blob); }
- | JSON_SYM opt_compressed
- {
- Lex->charset= &my_charset_utf8mb4_bin;
- $$.set(&type_handler_json_longtext);
- }
- ;
-
-field_type_misc:
- ENUM '(' string_list ')' opt_binary
- { $$.set(&type_handler_enum); }
- | SET '(' string_list ')' opt_binary
- { $$.set(&type_handler_set); }
- ;
-
-spatial_type:
- GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; }
- | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; }
- | POINT_SYM { $$= Field::GEOM_POINT; }
- | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; }
- | LINESTRING { $$= Field::GEOM_LINESTRING; }
- | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; }
- | POLYGON { $$= Field::GEOM_POLYGON; }
- | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; }
- ;
-
-char:
- CHAR_SYM {}
- ;
-
-nchar:
- NCHAR_SYM {}
- | NATIONAL_SYM CHAR_SYM {}
- ;
-
-varchar:
- char VARYING {}
- | VARCHAR {}
- ;
-
-nvarchar:
- NATIONAL_SYM VARCHAR {}
- | NVARCHAR_SYM {}
- | NCHAR_SYM VARCHAR {}
- | NATIONAL_SYM CHAR_SYM VARYING {}
- | NCHAR_SYM VARYING {}
- ;
-
-int_type:
- INT_SYM { $$= &type_handler_long; }
- | TINYINT { $$= &type_handler_tiny; }
- | SMALLINT { $$= &type_handler_short; }
- | MEDIUMINT { $$= &type_handler_int24; }
- | BIGINT { $$= &type_handler_longlong; }
- ;
-
-real_type:
- REAL
- {
- $$= thd->variables.sql_mode & MODE_REAL_AS_FLOAT ?
- static_cast<const Type_handler *>(&type_handler_float) :
- static_cast<const Type_handler *>(&type_handler_double);
- }
- | DOUBLE_SYM { $$= &type_handler_double; }
- | DOUBLE_SYM PRECISION { $$= &type_handler_double; }
- ;
-
-srid_option:
- /* empty */
- { Lex->last_field->srid= 0; }
- |
- REF_SYSTEM_ID_SYM '=' NUM
- {
- Lex->last_field->srid=atoi($3.str);
- }
- ;
-
-float_options:
- /* empty */ { $$.set(0, 0); }
- | field_length { $$.set($1, 0); }
- | precision { $$= $1; }
- ;
-
-precision:
- '(' NUM ',' NUM ')' { $$.set($2.str, $4.str); }
- ;
-
-field_options:
- /* empty */ {}
- | SIGNED_SYM {}
- | UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG;}
- | ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
- | UNSIGNED ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
- | ZEROFILL UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
- ;
-
-field_length:
- '(' LONG_NUM ')' { $$= $2.str; }
- | '(' ULONGLONG_NUM ')' { $$= $2.str; }
- | '(' DECIMAL_NUM ')' { $$= $2.str; }
- | '(' NUM ')' { $$= $2.str; }
- ;
-
-opt_field_length:
- /* empty */ { $$= (char*) 0; /* use default length */ }
- | field_length { $$= $1; }
- ;
-
-opt_field_length_default_1:
- /* empty */ { $$= (char*) "1"; }
- | field_length { $$= $1; }
- ;
-
-/*
- In sql_mode=ORACLE, real size of VARCHAR and CHAR with no length
- in SP parameters is fixed at runtime with the length of real args.
- Let's translate VARCHAR to VARCHAR(4000) for return value.
-
- Since Oracle 9, maximum size for VARCHAR in PL/SQL is 32767.
-
- In MariaDB the limit for VARCHAR is 65535 bytes.
- We could translate VARCHAR with no length to VARCHAR(65535), but
- it would mean that for multi-byte character sets we'd have to translate
- VARCHAR to MEDIUMTEXT, to guarantee 65535 characters.
-
- Also we could translate VARCHAR to VARCHAR(16383), where 16383 is
- the maximum possible length in characters in case of mbmaxlen=4
- (e.g. utf32, utf16, utf8mb4). However, we'll have character sets with
- mbmaxlen=5 soon (e.g. gb18030).
-*/
-opt_field_length_default_sp_param_varchar:
- /* empty */ { $$.set("4000", "4000"); }
- | field_length { $$.set($1, NULL); }
- ;
-
-opt_field_length_default_sp_param_char:
- /* empty */ { $$.set("2000", "2000"); }
- | field_length { $$.set($1, NULL); }
- ;
-
-opt_precision:
- /* empty */ { $$.set(0, 0); }
- | precision { $$= $1; }
- ;
-
-
-attribute_list:
- attribute_list attribute {}
- | attribute
- ;
-
-attribute:
- NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; }
- | DEFAULT column_default_expr { Lex->last_field->default_value= $2; }
- | ON UPDATE_SYM NOW_SYM opt_default_time_precision
- {
- Item *item= new (thd->mem_root) Item_func_now_local(thd, $4);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- Lex->last_field->on_update= item;
- }
- | AUTO_INC { Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
- | SERIAL_SYM DEFAULT VALUE_SYM
- {
- LEX *lex=Lex;
- lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | COLLATE_SYM collation_name
- {
- if (unlikely(Lex->charset && !my_charset_same(Lex->charset,$2)))
- my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- $2->name,Lex->charset->csname));
- Lex->last_field->charset= $2;
- }
- | serial_attribute
- ;
-
-opt_compression_method:
- /* empty */ { $$= NULL; }
- | equal ident { $$= $2.str; }
- ;
-
-opt_compressed:
- /* empty */ {}
- | compressed { }
- ;
-
-compressed:
- COMPRESSED_SYM opt_compression_method
- {
- if (unlikely(Lex->last_field->set_compressed($2)))
- MYSQL_YYABORT;
- }
- ;
-
-compressed_deprecated_data_type_attribute:
- COMPRESSED_SYM opt_compression_method
- {
- if (unlikely(Lex->last_field->set_compressed_deprecated(thd, $2)))
- MYSQL_YYABORT;
- }
- ;
-
-compressed_deprecated_column_attribute:
- COMPRESSED_SYM opt_compression_method
- {
- if (unlikely(Lex->last_field->
- set_compressed_deprecated_column_attribute(thd, $1.pos(), $2)))
- MYSQL_YYABORT;
- }
- ;
-
-asrow_attribute:
- not NULL_SYM
- {
- Lex->last_field->flags|= NOT_NULL_FLAG;
- }
- | opt_primary KEY_SYM
- {
- LEX *lex=Lex;
- lex->last_field->flags|= PRI_KEY_FLAG | NOT_NULL_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | vcol_attribute
- ;
-
-serial_attribute:
- asrow_attribute
- | IDENT_sys equal TEXT_STRING_sys
- {
- if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
- my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- (void) new (thd->mem_root)
- engine_option_value($1, $3, true,
- &Lex->last_field->option_list,
- &Lex->option_list_last);
- }
- | IDENT_sys equal ident
- {
- if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
- my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- (void) new (thd->mem_root)
- engine_option_value($1, $3, false,
- &Lex->last_field->option_list,
- &Lex->option_list_last);
- }
- | IDENT_sys equal real_ulonglong_num
- {
- (void) new (thd->mem_root)
- engine_option_value($1, $3, &Lex->last_field->option_list,
- &Lex->option_list_last, thd->mem_root);
- }
- | IDENT_sys equal DEFAULT
- {
- (void) new (thd->mem_root)
- engine_option_value($1, &Lex->last_field->option_list,
- &Lex->option_list_last);
- }
- | with_or_without_system VERSIONING_SYM
- {
- Lex->last_field->versioning= $1;
- Lex->create_info.options|= HA_VERSIONED_TABLE;
- if (Lex->alter_info.flags & ALTER_DROP_SYSTEM_VERSIONING)
- {
- my_yyabort_error((ER_VERS_NOT_VERSIONED, MYF(0),
- Lex->create_last_non_select_table->table_name.str));
- }
- }
- ;
-
-with_or_without_system:
- WITH_SYSTEM_SYM
- {
- Lex->alter_info.flags|= ALTER_COLUMN_UNVERSIONED;
- Lex->create_info.vers_info.versioned_fields= true;
- $$= Column_definition::WITH_VERSIONING;
- }
- | WITHOUT SYSTEM
- {
- Lex->alter_info.flags|= ALTER_COLUMN_UNVERSIONED;
- Lex->create_info.vers_info.unversioned_fields= true;
- $$= Column_definition::WITHOUT_VERSIONING;
- }
- ;
-
-
-type_with_opt_collate:
- field_type opt_collate
- {
- Lex->map_data_type(Lex_ident_sys(), &($$= $1));
-
- if ($2)
- {
- if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))))
- MYSQL_YYABORT;
- }
- Lex->last_field->set_attributes($$, Lex->charset);
- }
- ;
-
-sp_param_type_with_opt_collate:
- sp_param_field_type opt_collate
- {
- Lex->map_data_type(Lex_ident_sys(), &($$= $1));
-
- if ($2)
- {
- if (unlikely(!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))))
- MYSQL_YYABORT;
- }
- Lex->last_field->set_attributes($$, Lex->charset);
- }
- ;
-
-charset:
- CHAR_SYM SET {}
- | CHARSET {}
- ;
-
-charset_name:
- ident_or_text
- {
- if (unlikely(!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))))
- my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str));
- }
- | BINARY { $$= &my_charset_bin; }
- ;
-
-charset_name_or_default:
- charset_name { $$=$1; }
- | DEFAULT { $$=NULL; }
- ;
-
-opt_load_data_charset:
- /* Empty */ { $$= NULL; }
- | charset charset_name_or_default { $$= $2; }
- ;
-
-old_or_new_charset_name:
- ident_or_text
- {
- if (unlikely(!($$=get_charset_by_csname($1.str,
- MY_CS_PRIMARY,MYF(0))) &&
- !($$=get_old_charset_by_name($1.str))))
- my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str));
- }
- | BINARY { $$= &my_charset_bin; }
- ;
-
-old_or_new_charset_name_or_default:
- old_or_new_charset_name { $$=$1; }
- | DEFAULT { $$=NULL; }
- ;
-
-collation_name:
- ident_or_text
- {
- if (unlikely(!($$= mysqld_collation_get_by_name($1.str))))
- MYSQL_YYABORT;
- }
- ;
-
-opt_collate:
- /* empty */ { $$=NULL; }
- | COLLATE_SYM collation_name_or_default { $$=$2; }
- ;
-
-collation_name_or_default:
- collation_name { $$=$1; }
- | DEFAULT { $$=NULL; }
- ;
-
-opt_default:
- /* empty */ {}
- | DEFAULT {}
- ;
-
-charset_or_alias:
- charset charset_name { $$= $2; }
- | ASCII_SYM { $$= &my_charset_latin1; }
- | UNICODE_SYM
- {
- if (unlikely(!($$= get_charset_by_csname("ucs2", MY_CS_PRIMARY,MYF(0)))))
- my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"));
- }
- ;
-
-opt_binary:
- /* empty */ { bincmp_collation(NULL, false); }
- | binary {}
- ;
-
-binary:
- BYTE_SYM { bincmp_collation(&my_charset_bin, false); }
- | charset_or_alias opt_bin_mod { bincmp_collation($1, $2); }
- | BINARY { bincmp_collation(NULL, true); }
- | BINARY charset_or_alias { bincmp_collation($2, true); }
- ;
-
-opt_bin_mod:
- /* empty */ { $$= false; }
- | BINARY { $$= true; }
- ;
-
-ws_nweights:
- '(' real_ulong_num
- {
- if (unlikely($2 == 0))
- {
- thd->parse_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 */
- | PRIMARY_SYM
- ;
-
-references:
- REFERENCES
- table_ident
- opt_ref_list
- opt_match_clause
- opt_on_update_delete
- {
- $$=$2;
- }
- ;
-
-opt_ref_list:
- /* empty */
- { Lex->ref_list.empty(); }
- | '(' ref_list ')'
- ;
-
-ref_list:
- ref_list ',' ident
- {
- Key_part_spec *key= new (thd->mem_root) Key_part_spec(&$3, 0);
- if (unlikely(key == NULL))
- MYSQL_YYABORT;
- Lex->ref_list.push_back(key, thd->mem_root);
- }
- | ident
- {
- Key_part_spec *key= new (thd->mem_root) Key_part_spec(&$1, 0);
- if (unlikely(key == NULL))
- MYSQL_YYABORT;
- LEX *lex= Lex;
- lex->ref_list.empty();
- lex->ref_list.push_back(key, thd->mem_root);
- }
- ;
-
-opt_match_clause:
- /* empty */
- { Lex->fk_match_option= Foreign_key::FK_MATCH_UNDEF; }
- | MATCH FULL
- { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; }
- | MATCH PARTIAL
- { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; }
- | MATCH SIMPLE_SYM
- { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; }
- ;
-
-opt_on_update_delete:
- /* empty */
- {
- LEX *lex= Lex;
- lex->fk_update_opt= FK_OPTION_UNDEF;
- lex->fk_delete_opt= FK_OPTION_UNDEF;
- }
- | ON UPDATE_SYM delete_option
- {
- LEX *lex= Lex;
- lex->fk_update_opt= $3;
- lex->fk_delete_opt= FK_OPTION_UNDEF;
- }
- | ON DELETE_SYM delete_option
- {
- LEX *lex= Lex;
- lex->fk_update_opt= FK_OPTION_UNDEF;
- lex->fk_delete_opt= $3;
- }
- | ON UPDATE_SYM delete_option
- ON DELETE_SYM delete_option
- {
- LEX *lex= Lex;
- lex->fk_update_opt= $3;
- lex->fk_delete_opt= $6;
- }
- | ON DELETE_SYM delete_option
- ON UPDATE_SYM delete_option
- {
- LEX *lex= Lex;
- lex->fk_update_opt= $6;
- lex->fk_delete_opt= $3;
- }
- ;
-
-delete_option:
- RESTRICT { $$= FK_OPTION_RESTRICT; }
- | CASCADE { $$= FK_OPTION_CASCADE; }
- | SET NULL_SYM { $$= FK_OPTION_SET_NULL; }
- | NO_SYM ACTION { $$= FK_OPTION_NO_ACTION; }
- | SET DEFAULT { $$= FK_OPTION_SET_DEFAULT; }
- ;
-
-constraint_key_type:
- PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; }
- | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; }
- ;
-
-key_or_index:
- KEY_SYM {}
- | INDEX_SYM {}
- ;
-
-opt_key_or_index:
- /* empty */ {}
- | key_or_index
- ;
-
-keys_or_index:
- KEYS {}
- | INDEX_SYM {}
- | INDEXES {}
- ;
-
-opt_unique:
- /* empty */ { $$= Key::MULTIPLE; }
- | UNIQUE_SYM { $$= Key::UNIQUE; }
- ;
-
-fulltext:
- FULLTEXT_SYM { $$= Key::FULLTEXT;}
- ;
-
-spatial:
- SPATIAL_SYM
- {
-#ifdef HAVE_SPATIAL
- $$= Key::SPATIAL;
-#else
- my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name,
- sym_group_geom.needed_define));
-#endif
- }
- ;
-
-normal_key_options:
- /* empty */ {}
- | normal_key_opts { Lex->last_key->option_list= Lex->option_list; }
- ;
-
-fulltext_key_options:
- /* empty */ {}
- | fulltext_key_opts { Lex->last_key->option_list= Lex->option_list; }
- ;
-
-spatial_key_options:
- /* empty */ {}
- | spatial_key_opts { Lex->last_key->option_list= Lex->option_list; }
- ;
-
-normal_key_opts:
- normal_key_opt
- | normal_key_opts normal_key_opt
- ;
-
-spatial_key_opts:
- spatial_key_opt
- | spatial_key_opts spatial_key_opt
- ;
-
-fulltext_key_opts:
- fulltext_key_opt
- | fulltext_key_opts fulltext_key_opt
- ;
-
-opt_USING_key_algorithm:
- /* Empty*/ { $$= HA_KEY_ALG_UNDEF; }
- | USING btree_or_rtree { $$= $2; }
- ;
-
-/* TYPE is a valid identifier, so it's handled differently than USING */
-opt_key_algorithm_clause:
- /* Empty*/ { $$= HA_KEY_ALG_UNDEF; }
- | USING btree_or_rtree { $$= $2; }
- | TYPE_SYM btree_or_rtree { $$= $2; }
- ;
-
-key_using_alg:
- USING btree_or_rtree
- { Lex->last_key->key_create_info.algorithm= $2; }
- | TYPE_SYM btree_or_rtree
- { Lex->last_key->key_create_info.algorithm= $2; }
- ;
-
-all_key_opt:
- KEY_BLOCK_SIZE opt_equal ulong_num
- {
- Lex->last_key->key_create_info.block_size= $3;
- Lex->last_key->key_create_info.flags|= HA_USES_BLOCK_SIZE;
- }
- | COMMENT_SYM TEXT_STRING_sys
- { Lex->last_key->key_create_info.comment= $2; }
- | IDENT_sys equal TEXT_STRING_sys
- {
- if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
- my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- (void) new (thd->mem_root)
- engine_option_value($1, $3, true, &Lex->option_list,
- &Lex->option_list_last);
- }
- | IDENT_sys equal ident
- {
- if (unlikely($3.length > ENGINE_OPTION_MAX_LENGTH))
- my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str));
- (void) new (thd->mem_root)
- engine_option_value($1, $3, false, &Lex->option_list,
- &Lex->option_list_last);
- }
- | IDENT_sys equal real_ulonglong_num
- {
- (void) new (thd->mem_root)
- engine_option_value($1, $3, &Lex->option_list,
- &Lex->option_list_last, thd->mem_root);
- }
- | IDENT_sys equal DEFAULT
- {
- (void) new (thd->mem_root)
- engine_option_value($1, &Lex->option_list,
- &Lex->option_list_last);
- }
- ;
-
-normal_key_opt:
- all_key_opt
- | key_using_alg
- ;
-
-spatial_key_opt:
- all_key_opt
- ;
-
-fulltext_key_opt:
- all_key_opt
- | WITH PARSER_SYM IDENT_sys
- {
- if (likely(plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN)))
- Lex->last_key->key_create_info.parser_name= $3;
- else
- my_yyabort_error((ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str));
- }
- ;
-
-btree_or_rtree:
- BTREE_SYM { $$= HA_KEY_ALG_BTREE; }
- | RTREE_SYM { $$= HA_KEY_ALG_RTREE; }
- | HASH_SYM { $$= HA_KEY_ALG_HASH; }
- ;
-
-key_list:
- key_list ',' key_part order_dir
- {
- Lex->last_key->columns.push_back($3, thd->mem_root);
- }
- | key_part order_dir
- {
- Lex->last_key->columns.push_back($1, thd->mem_root);
- }
- ;
-
-key_part:
- ident
- {
- $$= new (thd->mem_root) Key_part_spec(&$1, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ident '(' NUM ')'
- {
- int key_part_len= atoi($3.str);
- if (unlikely(!key_part_len))
- my_yyabort_error((ER_KEY_PART_0, MYF(0), $1.str));
- $$= new (thd->mem_root) Key_part_spec(&$1, (uint) key_part_len);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_ident:
- /* empty */ { $$= null_clex_str; }
- | field_ident { $$= $1; }
- ;
-
-string_list:
- text_string
- { Lex->last_field->interval_list.push_back($1, thd->mem_root); }
- | string_list ',' text_string
- { Lex->last_field->interval_list.push_back($3, thd->mem_root); }
- ;
-
-/*
-** Alter table
-*/
-
-alter:
- ALTER
- {
- Lex->name= null_clex_str;
- Lex->table_type= TABLE_TYPE_UNKNOWN;
- Lex->sql_command= SQLCOM_ALTER_TABLE;
- Lex->duplicates= DUP_ERROR;
- Lex->first_select_lex()->order_list.empty();
- Lex->create_info.init();
- 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;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- DBUG_ASSERT(!Lex->m_sql_cmd);
- }
- alter_options TABLE_SYM table_ident opt_lock_wait_timeout
- {
- if (!Lex->first_select_lex()->
- add_table_to_list(thd, $5, NULL, TL_OPTION_UPDATING,
- TL_READ_NO_INSERT, MDL_SHARED_UPGRADABLE))
- MYSQL_YYABORT;
- Lex->first_select_lex()->db=
- (Lex->first_select_lex()->table_list.first)->db;
- Lex->create_last_non_select_table= Lex->last_table();
- Lex->mark_first_table_as_inserting();
- }
- alter_commands
- {
- if (likely(!Lex->m_sql_cmd))
- {
- /* Create a generic ALTER TABLE statment. */
- Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table();
- if (unlikely(Lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- Lex->pop_select(); //main select
- }
- | ALTER DATABASE ident_or_empty
- {
- Lex->create_info.default_table_charset= NULL;
- Lex->create_info.used_fields= 0;
- if (Lex->main_select_push(true))
- MYSQL_YYABORT;
- }
- create_database_options
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_ALTER_DB;
- lex->name= $3;
- if (lex->name.str == NULL &&
- unlikely(lex->copy_db_to(&lex->name)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- }
- | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM
- {
- LEX *lex= Lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "DATABASE"));
- lex->sql_command= SQLCOM_ALTER_DB_UPGRADE;
- lex->name= $3;
- }
- | ALTER PROCEDURE_SYM sp_name
- {
- LEX *lex= Lex;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- lex->sp_chistics.init();
- }
- sp_a_chistics
- {
- LEX *lex=Lex;
-
- lex->sql_command= SQLCOM_ALTER_PROCEDURE;
- lex->spname= $3;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- | ALTER FUNCTION_SYM sp_name
- {
- LEX *lex= Lex;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- lex->sp_chistics.init();
- }
- sp_a_chistics
- {
- LEX *lex=Lex;
-
- lex->sql_command= SQLCOM_ALTER_FUNCTION;
- lex->spname= $3;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- if (Lex->add_alter_view(thd, $2, $4, $6))
- MYSQL_YYABORT;
- }
- view_list_opt AS view_select
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- | ALTER definer_opt opt_view_suid VIEW_SYM table_ident
- /*
- We have two separate rules for ALTER VIEW rather that
- optional view_algorithm above, to resolve the ambiguity
- with the ALTER EVENT below.
- */
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5))
- MYSQL_YYABORT;
- }
- view_list_opt AS view_select
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- | ALTER definer_opt remember_name EVENT_SYM sp_name
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- /*
- It is safe to use Lex->spname because
- ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO
- is not allowed. Lex->spname is used in the case of RENAME TO
- If it had to be supported spname had to be added to
- Event_parse_data.
- */
-
- if (unlikely(!(Lex->event_parse_data= Event_parse_data::new_instance(thd))))
- MYSQL_YYABORT;
- Lex->event_parse_data->identifier= $5;
-
- Lex->sql_command= SQLCOM_ALTER_EVENT;
- Lex->stmt_definition_begin= $3;
- }
- ev_alter_on_schedule_completion
- opt_ev_rename_to
- opt_ev_status
- opt_ev_comment
- opt_ev_sql_stmt
- {
- if (unlikely(!($7 || $8 || $9 || $10 || $11)))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- /*
- sql_command is set here because some rules in ev_sql_stmt
- can overwrite it
- */
- Lex->sql_command= SQLCOM_ALTER_EVENT;
- Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr();
-
- Lex->pop_select(); //main select
- }
- | ALTER TABLESPACE alter_tablespace_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE;
- }
- | ALTER LOGFILE_SYM GROUP_SYM alter_logfile_group_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP;
- }
- | ALTER TABLESPACE change_tablespace_info
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= CHANGE_FILE_TABLESPACE;
- }
- | ALTER TABLESPACE change_tablespace_access
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE;
- }
- | ALTER SERVER_SYM ident_or_text
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_ALTER_SERVER;
- lex->server_options.reset($3);
- } OPTIONS_SYM '(' server_options_list ')' { }
- /* ALTER USER foo is allowed for MySQL compatibility. */
- | ALTER USER_SYM opt_if_exists clear_privileges grant_list
- opt_require_clause opt_resource_options opt_account_locking opt_password_expiration
- {
- Lex->create_info.set($3);
- Lex->sql_command= SQLCOM_ALTER_USER;
- }
- | ALTER SEQUENCE_SYM opt_if_exists
- {
- LEX *lex= Lex;
- lex->name= null_clex_str;
- lex->table_type= TABLE_TYPE_UNKNOWN;
- lex->sql_command= SQLCOM_ALTER_SEQUENCE;
- lex->create_info.init();
- lex->no_write_to_binlog= 0;
- DBUG_ASSERT(!lex->m_sql_cmd);
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- table_ident
- {
- LEX *lex= Lex;
- if (!(lex->create_info.seq_create_info= new (thd->mem_root)
- sequence_definition()) ||
- !lex->first_select_lex()->
- add_table_to_list(thd, $5, NULL, TL_OPTION_SEQUENCE,
- TL_WRITE, MDL_EXCLUSIVE))
- MYSQL_YYABORT;
- }
- sequence_defs
- {
- /* Create a generic ALTER SEQUENCE statment. */
- Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence($3);
- if (unlikely(Lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- ;
-
-opt_account_locking:
- /* Nothing */ {}
- | ACCOUNT_SYM LOCK_SYM
- {
- Lex->account_options.account_locked= ACCOUNTLOCK_LOCKED;
- }
- | ACCOUNT_SYM UNLOCK_SYM
- {
- Lex->account_options.account_locked= ACCOUNTLOCK_UNLOCKED;
- }
- ;
-opt_password_expiration:
- /* Nothing */ {}
- | PASSWORD_SYM EXPIRE_SYM
- {
- Lex->account_options.password_expire= PASSWORD_EXPIRE_NOW;
- }
- | PASSWORD_SYM EXPIRE_SYM NEVER_SYM
- {
- Lex->account_options.password_expire= PASSWORD_EXPIRE_NEVER;
- }
- | PASSWORD_SYM EXPIRE_SYM DEFAULT
- {
- Lex->account_options.password_expire= PASSWORD_EXPIRE_DEFAULT;
- }
- | PASSWORD_SYM EXPIRE_SYM INTERVAL_SYM NUM DAY_SYM
- {
- Lex->account_options.password_expire= PASSWORD_EXPIRE_INTERVAL;
- if (!(Lex->account_options.num_expiration_days= atoi($4.str)))
- my_yyabort_error((ER_WRONG_VALUE, MYF(0), "DAY", $4.str));
- }
- ;
-
-ev_alter_on_schedule_completion:
- /* empty */ { $$= 0;}
- | ON SCHEDULE_SYM ev_schedule_time { $$= 1; }
- | ev_on_completion { $$= 1; }
- | ON SCHEDULE_SYM ev_schedule_time ev_on_completion { $$= 1; }
- ;
-
-opt_ev_rename_to:
- /* empty */ { $$= 0;}
- | RENAME TO_SYM sp_name
- {
- /*
- Use lex's spname to hold the new name.
- The original name is in the Event_parse_data object
- */
- Lex->spname= $3;
- $$= 1;
- }
- ;
-
-opt_ev_sql_stmt:
- /* empty*/ { $$= 0;}
- | DO_SYM ev_sql_stmt { $$= 1; }
- ;
-
-ident_or_empty:
- /* empty */ { $$= Lex_ident_sys(); }
- | ident
- ;
-
-alter_commands:
- /* empty */
- | DISCARD TABLESPACE
- {
- Lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_discard_import_tablespace(
- Sql_cmd_discard_import_tablespace::DISCARD_TABLESPACE);
- if (unlikely(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 (unlikely(Lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- | alter_list
- opt_partitioning
- | alter_list
- remove_partitioning
- | remove_partitioning
- | partitioning
-/*
- This part was added for release 5.1 by Mikael Ronstrm.
- 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.
-*/
- | add_partition_rule
- | DROP PARTITION_SYM opt_if_exists alt_part_name_list
- {
- Lex->alter_info.partition_flags|= ALTER_PARTITION_DROP;
- DBUG_ASSERT(!Lex->if_exists());
- Lex->create_info.add($3);
- }
- | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog
- all_or_alt_part_name_list
- {
- LEX *lex= Lex;
- lex->alter_info.partition_flags|= ALTER_PARTITION_REBUILD;
- lex->no_write_to_binlog= $3;
- }
- | OPTIMIZE PARTITION_SYM opt_no_write_to_binlog
- all_or_alt_part_name_list
- {
- LEX *lex= thd->lex;
- lex->no_write_to_binlog= $3;
- lex->check_opt.init();
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_optimize_partition();
- if (unlikely(lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- opt_no_write_to_binlog
- | ANALYZE_SYM PARTITION_SYM opt_no_write_to_binlog
- all_or_alt_part_name_list
- {
- LEX *lex= thd->lex;
- lex->no_write_to_binlog= $3;
- lex->check_opt.init();
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_analyze_partition();
- if (unlikely(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_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_check_partition();
- if (unlikely(lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- opt_mi_check_type
- | REPAIR PARTITION_SYM opt_no_write_to_binlog
- all_or_alt_part_name_list
- {
- LEX *lex= thd->lex;
- lex->no_write_to_binlog= $3;
- lex->check_opt.init();
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_repair_partition();
- if (unlikely(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.partition_flags|= ALTER_PARTITION_COALESCE;
- lex->no_write_to_binlog= $3;
- lex->alter_info.num_parts= $4;
- }
- | TRUNCATE_SYM PARTITION_SYM all_or_alt_part_name_list
- {
- LEX *lex= thd->lex;
- lex->check_opt.init();
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root)
- Sql_cmd_alter_table_truncate_partition();
- if (unlikely(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
- {
- if (Lex->stmt_alter_table_exchange_partition($6))
- MYSQL_YYABORT;
- }
- ;
-
-remove_partitioning:
- REMOVE_SYM PARTITIONING_SYM
- {
- Lex->alter_info.partition_flags|= ALTER_PARTITION_REMOVE;
- }
- ;
-
-all_or_alt_part_name_list:
- ALL
- {
- Lex->alter_info.partition_flags|= ALTER_PARTITION_ALL;
- }
- | alt_part_name_list
- ;
-
-add_partition_rule:
- ADD PARTITION_SYM opt_if_not_exists
- opt_no_write_to_binlog
- {
- LEX *lex= Lex;
- lex->part_info= new (thd->mem_root) partition_info();
- if (unlikely(!lex->part_info))
- MYSQL_YYABORT;
-
- lex->alter_info.partition_flags|= ALTER_PARTITION_ADD;
- DBUG_ASSERT(!Lex->create_info.if_not_exists());
- lex->create_info.set($3);
- lex->no_write_to_binlog= $4;
- }
- add_part_extra
- {}
- ;
-
-add_part_extra:
- /* empty */
- | '(' part_def_list ')'
- {
- LEX *lex= Lex;
- lex->part_info->num_parts= lex->part_info->partitions.elements;
- }
- | PARTITIONS_SYM real_ulong_num
- {
- Lex->part_info->num_parts= $2;
- }
- ;
-
-reorg_partition_rule:
- REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog
- {
- LEX *lex= Lex;
- lex->part_info= new (thd->mem_root) partition_info();
- if (unlikely(!lex->part_info))
- MYSQL_YYABORT;
-
- lex->no_write_to_binlog= $3;
- }
- reorg_parts_rule
- ;
-
-reorg_parts_rule:
- /* empty */
- {
- Lex->alter_info.partition_flags|= ALTER_PARTITION_TABLE_REORG;
- }
- | alt_part_name_list
- {
- Lex->alter_info.partition_flags|= ALTER_PARTITION_REORGANIZE;
- }
- INTO '(' part_def_list ')'
- {
- partition_info *part_info= Lex->part_info;
- part_info->num_parts= part_info->partitions.elements;
- }
- ;
-
-alt_part_name_list:
- alt_part_name_item {}
- | alt_part_name_list ',' alt_part_name_item {}
- ;
-
-alt_part_name_item:
- ident
- {
- if (unlikely(Lex->alter_info.partition_names.push_back($1.str,
- thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-/*
- End of management of partition commands
-*/
-
-alter_list:
- alter_list_item
- | alter_list ',' alter_list_item
- ;
-
-add_column:
- ADD opt_column opt_if_not_exists_table_element
- ;
-
-alter_list_item:
- add_column column_def opt_place
- {
- LEX *lex=Lex;
- lex->create_last_non_select_table= lex->last_table();
- lex->alter_info.flags|= ALTER_PARSER_ADD_COLUMN;
- $2->after= $3;
- }
- | ADD key_def
- {
- Lex->create_last_non_select_table= Lex->last_table();
- Lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | ADD period_for_system_time
- {
- Lex->alter_info.flags|= ALTER_ADD_PERIOD;
- }
- | ADD
- PERIOD_SYM opt_if_not_exists_table_element period_for_application_time
- {
- Table_period_info &period= Lex->create_info.period_info;
- period.create_if_not_exists= Lex->check_exists;
- Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
- }
- | add_column '(' create_field_list ')'
- {
- LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_PARSER_ADD_COLUMN;
- if (!lex->alter_info.key_list.is_empty())
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | ADD constraint_def
- {
- Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
- }
- | ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint
- {
- Lex->alter_info.flags|= ALTER_ADD_CHECK_CONSTRAINT;
- Lex->add_constraint($6, $7, TRUE);
- }
- | CHANGE opt_column opt_if_exists_table_element field_ident
- field_spec opt_place
- {
- Lex->alter_info.flags|= ALTER_CHANGE_COLUMN | ALTER_RENAME_COLUMN;
- Lex->create_last_non_select_table= Lex->last_table();
- $5->change= $4;
- $5->after= $6;
- }
- | MODIFY_SYM opt_column opt_if_exists_table_element
- field_spec opt_place
- {
- Lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
- Lex->create_last_non_select_table= Lex->last_table();
- $4->change= $4->field_name;
- $4->after= $5;
- }
- | DROP opt_column opt_if_exists_table_element field_ident opt_restrict
- {
- LEX *lex=Lex;
- Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::COLUMN, $4.str, $3));
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= ALTER_PARSER_DROP_COLUMN;
- }
- | DROP CONSTRAINT opt_if_exists_table_element field_ident
- {
- LEX *lex=Lex;
- Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::CHECK_CONSTRAINT,
- $4.str, $3));
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= ALTER_DROP_CHECK_CONSTRAINT;
- }
- | DROP FOREIGN KEY_SYM opt_if_exists_table_element field_ident
- {
- LEX *lex=Lex;
- Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::FOREIGN_KEY, $5.str, $4));
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= ALTER_DROP_FOREIGN_KEY;
- }
- | DROP opt_constraint_no_id PRIMARY_SYM KEY_SYM
- {
- LEX *lex=Lex;
- Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::KEY, primary_key_name,
- FALSE));
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= ALTER_DROP_INDEX;
- }
- | DROP key_or_index opt_if_exists_table_element field_ident
- {
- LEX *lex=Lex;
- Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::KEY, $4.str, $3));
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- lex->alter_info.flags|= ALTER_DROP_INDEX;
- }
- | DISABLE_SYM KEYS
- {
- LEX *lex=Lex;
- lex->alter_info.keys_onoff= Alter_info::DISABLE;
- lex->alter_info.flags|= ALTER_KEYS_ONOFF;
- }
- | ENABLE_SYM KEYS
- {
- LEX *lex=Lex;
- lex->alter_info.keys_onoff= Alter_info::ENABLE;
- lex->alter_info.flags|= ALTER_KEYS_ONOFF;
- }
- | ALTER opt_column opt_if_exists_table_element field_ident SET DEFAULT column_default_expr
- {
- if (check_expression($7, &$4, VCOL_DEFAULT))
- MYSQL_YYABORT;
- if (unlikely(Lex->add_alter_list($4.str, $7, $3)))
- MYSQL_YYABORT;
- }
- | ALTER opt_column opt_if_exists_table_element field_ident DROP DEFAULT
- {
- if (unlikely(Lex->add_alter_list($4.str, (Virtual_column_info*) 0,
- $3)))
- MYSQL_YYABORT;
- }
- | RENAME opt_to table_ident
- {
- LEX *lex=Lex;
- lex->first_select_lex()->db= $3->db;
- if (lex->first_select_lex()->db.str == NULL &&
- lex->copy_db_to(&lex->first_select_lex()->db))
- MYSQL_YYABORT;
- if (unlikely(check_table_name($3->table.str,$3->table.length,
- FALSE)) ||
- ($3->db.str && unlikely(check_db_name((LEX_STRING*) &$3->db))))
- my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3->table.str));
- lex->name= $3->table;
- lex->alter_info.flags|= ALTER_RENAME;
- }
- | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
- {
- if (!$4)
- {
- $4= thd->variables.collation_database;
- }
- $5= $5 ? $5 : $4;
- if (unlikely(!my_charset_same($4,$5)))
- my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- $5->name, $4->csname));
- if (unlikely(Lex->create_info.add_alter_list_item_convert_to_charset($5)))
- MYSQL_YYABORT;
- Lex->alter_info.flags|= ALTER_CONVERT_TO;
- }
- | create_table_options_space_separated
- {
- LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_OPTIONS;
- }
- | FORCE_SYM
- {
- Lex->alter_info.flags|= ALTER_RECREATE;
- }
- | alter_order_clause
- {
- LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_ORDER;
- }
- | alter_algorithm_option
- | alter_lock_option
- | ADD SYSTEM VERSIONING_SYM
- {
- Lex->alter_info.flags|= ALTER_ADD_SYSTEM_VERSIONING;
- Lex->create_info.options|= HA_VERSIONED_TABLE;
- }
- | DROP SYSTEM VERSIONING_SYM
- {
- Lex->alter_info.flags|= ALTER_DROP_SYSTEM_VERSIONING;
- Lex->create_info.options&= ~HA_VERSIONED_TABLE;
- }
- | DROP PERIOD_SYM FOR_SYSTEM_TIME_SYM
- {
- Lex->alter_info.flags|= ALTER_DROP_PERIOD;
- }
- | DROP PERIOD_SYM opt_if_exists_table_element FOR_SYM ident
- {
- Alter_drop *ad= new Alter_drop(Alter_drop::PERIOD, $5.str, $3);
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- Lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- Lex->alter_info.flags|= ALTER_DROP_CHECK_CONSTRAINT;
- }
- ;
-
-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.set_requested_algorithm(
- Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT);
- }
- | ALGORITHM_SYM opt_equal ident
- {
- if (unlikely(Lex->alter_info.set_requested_algorithm(&$3)))
- my_yyabort_error((ER_UNKNOWN_ALTER_ALGORITHM, MYF(0), $3.str));
- }
- ;
-
-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 (unlikely(Lex->alter_info.set_requested_lock(&$3)))
- my_yyabort_error((ER_UNKNOWN_ALTER_LOCK, MYF(0), $3.str));
- }
- ;
-
-opt_column:
- /* empty */ {} %prec PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE
- | COLUMN_SYM {}
- ;
-
-opt_ignore:
- /* empty */ { Lex->ignore= 0;}
- | IGNORE_SYM { Lex->ignore= 1;}
- ;
-
-alter_options:
- { Lex->ignore= 0;} alter_options_part2
- ;
-
-alter_options_part2:
- /* empty */
- | alter_option_list
- ;
-
-alter_option_list:
- alter_option_list alter_option
- | alter_option
- ;
-
-alter_option:
- IGNORE_SYM { Lex->ignore= 1;}
- | ONLINE_SYM
- {
- Lex->alter_info.requested_lock=
- Alter_info::ALTER_TABLE_LOCK_NONE;
- }
- ;
-
-opt_restrict:
- /* empty */ { Lex->drop_mode= DROP_DEFAULT; }
- | RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
- | CASCADE { Lex->drop_mode= DROP_CASCADE; }
- ;
-
-opt_place:
- /* empty */ { $$= null_clex_str; }
- | AFTER_SYM ident
- {
- $$= $2;
- Lex->alter_info.flags |= ALTER_COLUMN_ORDER;
- }
- | FIRST_SYM
- {
- $$.str= first_keyword;
- $$.length= 5; /* Length of "first" */
- Lex->alter_info.flags |= ALTER_COLUMN_ORDER;
- }
- ;
-
-opt_to:
- /* empty */ {}
- | TO_SYM {}
- | '=' {}
- | AS {}
- ;
-
-slave:
- START_SYM SLAVE optional_connection_name slave_thread_opts
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_START;
- lex->type = 0;
- /* If you change this code don't forget to update SLAVE START too */
- }
- slave_until
- {}
- | START_SYM ALL SLAVES slave_thread_opts
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_ALL_START;
- lex->type = 0;
- /* If you change this code don't forget to update STOP SLAVE too */
- }
- {}
- | STOP_SYM SLAVE optional_connection_name slave_thread_opts
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_STOP;
- lex->type = 0;
- /* If you change this code don't forget to update SLAVE STOP too */
- }
- | STOP_SYM ALL SLAVES slave_thread_opts
- {
- LEX *lex=Lex;
- 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 opt_start_transaction_option_list
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_BEGIN;
- /* READ ONLY and READ WRITE are mutually exclusive. */
- if (unlikely(($3 & MYSQL_START_TRANS_OPT_READ_WRITE) &&
- ($3 & MYSQL_START_TRANS_OPT_READ_ONLY)))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- lex->start_transaction_opt= $3;
- }
- ;
-
-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:
- { Lex->slave_thd_opt= 0; }
- slave_thread_opt_list
- {}
- ;
-
-slave_thread_opt_list:
- slave_thread_opt
- | slave_thread_opt_list ',' slave_thread_opt
- ;
-
-slave_thread_opt:
- /*empty*/ {}
- | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; }
- | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; }
- ;
-
-slave_until:
- /*empty*/ {}
- | UNTIL_SYM slave_until_opts
- {
- LEX *lex=Lex;
- if (unlikely(((lex->mi.log_file_name || lex->mi.pos) &&
- (lex->mi.relay_log_name || lex->mi.relay_log_pos)) ||
- !((lex->mi.log_file_name && lex->mi.pos) ||
- (lex->mi.relay_log_name && lex->mi.relay_log_pos))))
- my_yyabort_error((ER_BAD_SLAVE_UNTIL_COND, MYF(0)));
- }
- | UNTIL_SYM MASTER_GTID_POS_SYM '=' TEXT_STRING_sys
- {
- Lex->mi.gtid_pos_str = $4;
- }
- ;
-
-slave_until_opts:
- master_file_def
- | slave_until_opts ',' master_file_def
- ;
-
-checksum:
- CHECKSUM_SYM table_or_tables
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_CHECKSUM;
- /* Will be overridden during execution. */
- YYPS->m_lock_type= TL_UNLOCK;
- }
- table_list opt_checksum_type
- {}
- ;
-
-opt_checksum_type:
- /* nothing */ { Lex->check_opt.flags= 0; }
- | QUICK { Lex->check_opt.flags= T_QUICK; }
- | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; }
- ;
-
-repair_table_or_view:
- table_or_tables table_list opt_mi_repair_type
- | VIEW_SYM
- { Lex->table_type= TABLE_TYPE_VIEW; }
- table_list opt_view_repair_type
- ;
-
-repair:
- REPAIR opt_no_write_to_binlog
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_REPAIR;
- lex->no_write_to_binlog= $2;
- lex->check_opt.init();
- lex->alter_info.reset();
- /* Will be overridden during execution. */
- YYPS->m_lock_type= TL_UNLOCK;
- }
- repair_table_or_view
- {
- LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_repair_table();
- if (unlikely(lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_mi_repair_type:
- /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
- | mi_repair_types {}
- ;
-
-mi_repair_types:
- mi_repair_type {}
- | mi_repair_type mi_repair_types {}
- ;
-
-mi_repair_type:
- QUICK { Lex->check_opt.flags|= T_QUICK; }
- | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; }
- | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }
- ;
-
-opt_view_repair_type:
- /* empty */ { }
- | FROM MYSQL_SYM { Lex->check_opt.sql_flags|= TT_FROM_MYSQL; }
- ;
-
-analyze:
- ANALYZE_SYM opt_no_write_to_binlog table_or_tables
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_ANALYZE;
- lex->no_write_to_binlog= $2;
- lex->check_opt.init();
- lex->alter_info.reset();
- /* Will be overridden during execution. */
- YYPS->m_lock_type= TL_UNLOCK;
- }
- analyze_table_list
- {
- LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_analyze_table();
- if (unlikely(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 (thd->mem_root) List<LEX_STRING>;
- if (unlikely(lex->column_list == NULL))
- MYSQL_YYABORT;
- }
- table_column_list
- ')'
- { }
- ;
-
-persistent_index_stat_spec:
- ALL {}
- | '('
- {
- LEX* lex= thd->lex;
- lex->index_list= new (thd->mem_root) List<LEX_STRING>;
- if (unlikely(lex->index_list == NULL))
- MYSQL_YYABORT;
- }
- table_index_list
- ')'
- { }
- ;
-
-table_column_list:
- /* empty */
- {}
- | ident
- {
- Lex->column_list->push_back((LEX_STRING*)
- thd->memdup(&$1, sizeof(LEX_STRING)), thd->mem_root);
- }
- | table_column_list ',' ident
- {
- Lex->column_list->push_back((LEX_STRING*)
- thd->memdup(&$3, sizeof(LEX_STRING)), thd->mem_root);
- }
- ;
-
-table_index_list:
- /* empty */
- {}
- | table_index_name
- | table_index_list ',' table_index_name
- ;
-
-table_index_name:
- ident
- {
- Lex->index_list->push_back((LEX_STRING*)
- thd->memdup(&$1, sizeof(LEX_STRING)),
- thd->mem_root);
- }
- |
- PRIMARY_SYM
- {
- LEX_STRING str= {(char*) "PRIMARY", 7};
- Lex->index_list->push_back((LEX_STRING*)
- thd->memdup(&str, sizeof(LEX_STRING)),
- thd->mem_root);
- }
- ;
-
-binlog_base64_event:
- BINLOG_SYM TEXT_STRING_sys
- {
- Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
- Lex->comment= $2;
- Lex->ident.str= NULL;
- Lex->ident.length= 0;
- }
- |
- BINLOG_SYM '@' ident_or_text ',' '@' ident_or_text
- {
- Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT;
- Lex->comment= $3;
- Lex->ident= $6;
- }
- ;
-
-check_view_or_table:
- table_or_tables table_list opt_mi_check_type
- | VIEW_SYM
- { Lex->table_type= TABLE_TYPE_VIEW; }
- table_list opt_view_check_type
- ;
-
-check: CHECK_SYM
- {
- LEX *lex=Lex;
-
- lex->sql_command = SQLCOM_CHECK;
- lex->check_opt.init();
- lex->alter_info.reset();
- /* Will be overridden during execution. */
- YYPS->m_lock_type= TL_UNLOCK;
- }
- check_view_or_table
- {
- LEX* lex= thd->lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "CHECK"));
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_check_table();
- if (unlikely(lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_mi_check_type:
- /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
- | mi_check_types {}
- ;
-
-mi_check_types:
- mi_check_type {}
- | mi_check_type mi_check_types {}
- ;
-
-mi_check_type:
- QUICK { Lex->check_opt.flags|= T_QUICK; }
- | FAST_SYM { Lex->check_opt.flags|= T_FAST; }
- | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; }
- | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; }
- | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; }
- | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }
- ;
-
-opt_view_check_type:
- /* empty */ { }
- | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; }
- ;
-
-optimize:
- OPTIMIZE opt_no_write_to_binlog table_or_tables
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_OPTIMIZE;
- lex->no_write_to_binlog= $2;
- lex->check_opt.init();
- lex->alter_info.reset();
- /* Will be overridden during execution. */
- YYPS->m_lock_type= TL_UNLOCK;
- }
- table_list opt_lock_wait_timeout
- {
- LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_optimize_table();
- if (unlikely(lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_no_write_to_binlog:
- /* empty */ { $$= 0; }
- | NO_WRITE_TO_BINLOG { $$= 1; }
- | LOCAL_SYM { $$= 1; }
- ;
-
-rename:
- RENAME table_or_tables
- {
- Lex->sql_command= SQLCOM_RENAME_TABLE;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- table_to_table_list
- {
- Lex->pop_select(); //main select
- }
- | RENAME USER_SYM clear_privileges rename_list
- {
- Lex->sql_command = SQLCOM_RENAME_USER;
- }
- ;
-
-rename_list:
- user TO_SYM user
- {
- if (unlikely(Lex->users_list.push_back($1, thd->mem_root) ||
- Lex->users_list.push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | rename_list ',' user TO_SYM user
- {
- if (unlikely(Lex->users_list.push_back($3, thd->mem_root) ||
- Lex->users_list.push_back($5, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-table_to_table_list:
- table_to_table
- | table_to_table_list ',' table_to_table
- ;
-
-table_to_table:
- table_ident opt_lock_wait_timeout TO_SYM table_ident
- {
- LEX *lex=Lex;
- SELECT_LEX *sl= lex->current_select;
- if (unlikely(!sl->add_table_to_list(thd, $1,NULL,
- TL_OPTION_UPDATING,
- TL_IGNORE, MDL_EXCLUSIVE)) ||
- unlikely(!sl->add_table_to_list(thd, $4, NULL,
- TL_OPTION_UPDATING,
- TL_IGNORE, MDL_EXCLUSIVE)))
- MYSQL_YYABORT;
- }
- ;
-
-keycache:
- CACHE_SYM INDEX_SYM
- {
- Lex->alter_info.reset();
- }
- keycache_list_or_parts IN_SYM key_cache_name
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE;
- lex->ident= $6;
- }
- ;
-
-keycache_list_or_parts:
- keycache_list
- | assign_to_keycache_parts
- ;
-
-keycache_list:
- assign_to_keycache
- | keycache_list ',' assign_to_keycache
- ;
-
-assign_to_keycache:
- table_ident cache_keys_spec
- {
- if (unlikely(!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
- MDL_SHARED_READ,
- Select->
- pop_index_hints())))
- MYSQL_YYABORT;
- }
- ;
-
-assign_to_keycache_parts:
- table_ident adm_partition cache_keys_spec
- {
- if (unlikely(!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ,
- MDL_SHARED_READ,
- Select->
- pop_index_hints())))
- MYSQL_YYABORT;
- }
- ;
-
-key_cache_name:
- ident { $$= $1; }
- | DEFAULT { $$ = default_key_cache_base; }
- ;
-
-preload:
- LOAD INDEX_SYM INTO CACHE_SYM
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_PRELOAD_KEYS;
- lex->alter_info.reset();
- if (lex->main_select_push())
- MYSQL_YYABORT;
- }
- preload_list_or_parts
- {
- Lex->pop_select(); //main select
- }
- ;
-
-preload_list_or_parts:
- preload_keys_parts
- | preload_list
- ;
-
-preload_list:
- preload_keys
- | preload_list ',' preload_keys
- ;
-
-preload_keys:
- table_ident cache_keys_spec opt_ignore_leaves
- {
- if (unlikely(!Select->add_table_to_list(thd, $1, NULL, $3, TL_READ,
- MDL_SHARED_READ,
- Select->
- pop_index_hints())))
- MYSQL_YYABORT;
- }
- ;
-
-preload_keys_parts:
- table_ident adm_partition cache_keys_spec opt_ignore_leaves
- {
- if (unlikely(!Select->add_table_to_list(thd, $1, NULL, $4, TL_READ,
- MDL_SHARED_READ,
- Select->
- pop_index_hints())))
- MYSQL_YYABORT;
- }
- ;
-
-adm_partition:
- PARTITION_SYM have_partitioning
- {
- Lex->alter_info.partition_flags|= ALTER_PARTITION_ADMIN;
- }
- '(' all_or_alt_part_name_list ')'
- ;
-
-cache_keys_spec:
- {
- Lex->first_select_lex()->alloc_index_hints(thd);
- Lex->first_select_lex()->set_index_hint_type(INDEX_HINT_USE,
- INDEX_HINT_MASK_ALL);
- }
- cache_key_list_or_empty
- ;
-
-cache_key_list_or_empty:
- /* empty */ { }
- | key_or_index '(' opt_key_usage_list ')'
- ;
-
-opt_ignore_leaves:
- /* empty */
- { $$= 0; }
- | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; }
- ;
-
-/*
- Select : retrieve data from table
-*/
-
-select:
- query_expression_no_with_clause
- {
- if (Lex->push_select($1->fake_select_lex ?
- $1->fake_select_lex :
- $1->first_select()))
- MYSQL_YYABORT;
- }
- opt_procedure_or_into
- {
- Lex->pop_select();
- $1->set_with_clause(NULL);
- if (Lex->select_finalize($1, $3))
- MYSQL_YYABORT;
- }
- | with_clause query_expression_no_with_clause
- {
- if (Lex->push_select($2->fake_select_lex ?
- $2->fake_select_lex :
- $2->first_select()))
- MYSQL_YYABORT;
- }
- opt_procedure_or_into
- {
- Lex->pop_select();
- $2->set_with_clause($1);
- $1->attach_to($2->first_select());
- if (Lex->select_finalize($2, $4))
- MYSQL_YYABORT;
- }
- ;
-
-
-select_into:
- select_into_query_specification
- {
- if (Lex->push_select($1))
- MYSQL_YYABORT;
- }
- opt_order_limit_lock
- {
- SELECT_LEX_UNIT *unit;
- if (!(unit = Lex->create_unit($1)))
- MYSQL_YYABORT;
- if ($3)
- unit= Lex->add_tail_to_query_expression_body(unit, $3);
- if (Lex->select_finalize(unit))
- MYSQL_YYABORT;
- }
- | with_clause
- select_into_query_specification
- {
- if (Lex->push_select($2))
- MYSQL_YYABORT;
- }
- opt_order_limit_lock
- {
- SELECT_LEX_UNIT *unit;
- if (!(unit = Lex->create_unit($2)))
- MYSQL_YYABORT;
- if ($4)
- unit= Lex->add_tail_to_query_expression_body(unit, $4);
- unit->set_with_clause($1);
- $1->attach_to($2);
- if (Lex->select_finalize(unit))
- MYSQL_YYABORT;
- }
- ;
-
-
-simple_table:
- query_specification { $$= $1; }
- | table_value_constructor { $$= $1; }
- ;
-
-table_value_constructor:
- VALUES
- {
- if (Lex->parsed_TVC_start())
- MYSQL_YYABORT;
- }
- values_list
- {
- if (!($$= Lex->parsed_TVC_end()))
- MYSQL_YYABORT;
- }
- ;
-
-query_specification_start:
- SELECT_SYM
- {
- SELECT_LEX *sel;
- LEX *lex= Lex;
- if (!(sel= lex->alloc_select(TRUE)) ||
- lex->push_select(sel))
- MYSQL_YYABORT;
- sel->init_select();
- sel->braces= FALSE;
- }
- select_options
- {
- Select->parsing_place= SELECT_LIST;
- }
- select_item_list
- {
- Select->parsing_place= NO_MATTER;
- }
- ;
-
-query_specification:
- query_specification_start
- opt_from_clause
- opt_where_clause
- opt_group_clause
- opt_having_clause
- opt_window_clause
- {
- $$= Lex->pop_select();
- }
- ;
-
-select_into_query_specification:
- query_specification_start
- into
- opt_from_clause
- opt_where_clause
- opt_group_clause
- opt_having_clause
- opt_window_clause
- {
- $$= Lex->pop_select();
- }
- ;
-
-/**
-
- The following grammar for query expressions conformant to
- the latest SQL Standard is supported:
-
- <query expression> ::=
- [ <with clause> ] <query expression body>
- [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
-
- <with clause> ::=
- WITH [ RECURSIVE ] <with_list
-
- <with list> ::=
- <with list element> [ { <comma> <with list element> }... ]
-
- <with list element> ::=
- <query name> [ '(' <with column list> ')' ]
- AS <table subquery>
-
- <with column list> ::=
- <column name list>
-
- <query expression body> ::
- <query term>
- | <query expression body> UNION [ ALL | DISTINCT ] <query term>
- | <query expression body> EXCEPT [ DISTINCT ] <query term>
-
- <query term> ::=
- <query primary>
- | <query term> INTERSECT [ DISTINCT ] <query primary>
-
- <query primary> ::=
- <simple table>
- | '(' <query expression body>
- [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
- ')'
-
- <simple table>
- <query specification>
- | <table value constructor>
-
- <subquery>
- '(' <query_expression> ')'
-
-*/
-
-/*
- query_expression produces the same expressions as
- <query expression>
-*/
-
-query_expression:
- query_expression_no_with_clause
- {
- $1->set_with_clause(NULL);
- $$= $1;
- }
- | with_clause
- query_expression_no_with_clause
- {
- $2->set_with_clause($1);
- $1->attach_to($2->first_select());
- $$= $2;
- }
- ;
-
-/*
- query_expression_no_with_clause produces the same expressions as
- <query expression> without [ <with clause> ]
-*/
-
-query_expression_no_with_clause:
- query_expression_body_ext { $$= $1; }
- | query_expression_body_ext_parens { $$= $1; }
- ;
-
-/*
- query_expression_body_ext produces the same expressions as
- <query expression body>
- [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
- | '('... <query expression body>
- [ <order by clause> ] [ <result offset clause> ] [ <fetch first clause> ]
- ')'...
- Note: number of ')' must be equal to the number of '(' in the rule above
-*/
-
-query_expression_body_ext:
- query_expression_body
- {
- if ($1->first_select()->next_select())
- {
- if (Lex->parsed_multi_operand_query_expression_body($1))
- MYSQL_YYABORT;
- }
- }
- opt_query_expression_tail
- {
- if (!$3)
- $$= $1;
- else
- $$= Lex->add_tail_to_query_expression_body($1, $3);
- }
- | query_expression_body_ext_parens
- {
- Lex->push_select(!$1->first_select()->next_select() ?
- $1->first_select() : $1->fake_select_lex);
- }
- query_expression_tail
- {
- if (!($$= Lex->add_tail_to_query_expression_body_ext_parens($1, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-query_expression_body_ext_parens:
- '(' query_expression_body_ext_parens ')'
- { $$= $2; }
- | '(' query_expression_body_ext ')'
- {
- SELECT_LEX *sel= $2->first_select()->next_select() ?
- $2->fake_select_lex : $2->first_select();
- sel->braces= true;
- $$= $2;
- }
- ;
-
-/*
- query_expression_body produces the same expressions as
- <query expression body>
-*/
-
-query_expression_body:
- query_simple
- {
- Lex->push_select($1);
- if (!($$= Lex->create_unit($1)))
- MYSQL_YYABORT;
- }
- | query_expression_body
- unit_type_decl
- {
- if (!$1->first_select()->next_select())
- {
- Lex->pop_select();
- }
- }
- query_primary
- {
- if (!($$= Lex->add_primary_to_query_expression_body($1, $4,
- $2.unit_type,
- $2.distinct,
- TRUE)))
- MYSQL_YYABORT;
- }
- | query_expression_body_ext_parens
- unit_type_decl
- query_primary
- {
- if (!($$= Lex->add_primary_to_query_expression_body_ext_parens(
- $1, $3,
- $2.unit_type,
- $2.distinct)))
- MYSQL_YYABORT;
- }
- ;
-
-/*
- query_primary produces the same expressions as
- <query primary>
-*/
-
-query_primary:
- query_simple
- { $$= $1; }
- | query_expression_body_ext_parens
- { $$= $1->first_select(); }
- ;
-
-/*
- query_simple produces the same expressions as
- <simple table>
-*/
-
-query_simple:
- simple_table { $$= $1;}
- ;
-
-subselect:
- query_expression
- {
- if (!($$= Lex->parsed_subselect($1)))
- YYABORT;
- }
- ;
-
-/*
- subquery produces the same expressions as
- <subquery>
-
- Consider the production rule of the SQL Standard
- subquery:
- '(' query_expression ')'
-
- This rule is equivalent to the rule
- subquery:
- '(' query_expression_no_with_clause ')'
- | '(' with_clause query_expression_no_with_clause ')'
- that in its turn is equivalent to
- subquery:
- '(' query_expression_body_ext ')'
- | query_expression_body_ext_parens
- | '(' with_clause query_expression_no_with_clause ')'
-
- The latter can be re-written into
- subquery:
- query_expression_body_ext_parens
- | '(' with_clause query_expression_no_with_clause ')'
-
- The last rule allows us to resolve properly the shift/reduce conflict
- when subquery is used in expressions such as in the following queries
- select (select * from t1 limit 1) + t2.a from t2
- select * from t1 where t1.a [not] in (select t2.a from t2)
-
- In the rule below %prec SUBQUERY_AS_EXPR forces the parser to perform a shift
- operation rather then a reduce operation when ')' is encountered and can be
- considered as the last symbol a query expression.
-*/
-
-subquery:
- query_expression_body_ext_parens %prec SUBQUERY_AS_EXPR
- {
- if (!$1->fake_select_lex)
- $1->first_select()->braces= false;
- else
- $1->fake_select_lex->braces= false;
- if (!($$= Lex->parsed_subselect($1)))
- YYABORT;
- }
- | '(' with_clause query_expression_no_with_clause ')'
- {
- $3->set_with_clause($2);
- $2->attach_to($3->first_select());
- if (!($$= Lex->parsed_subselect($3)))
- YYABORT;
- }
- ;
-
-opt_from_clause:
- /* empty */ %prec EMPTY_FROM_CLAUSE
- | from_clause
- ;
-
-from_clause:
- FROM table_reference_list
- ;
-
-table_reference_list:
- join_table_list
- {
- Select->context.table_list=
- Select->context.first_name_resolution_table=
- Select->table_list.first;
- }
- | DUAL_SYM
- /* oracle compatibility: oracle always requires FROM clause,
- and DUAL is system table without fields.
- Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ?
- Hmmm :) */
- ;
-
-select_options:
- /* empty*/
- | select_option_list
- {
- if (unlikely((Select->options & SELECT_DISTINCT) &&
- (Select->options & SELECT_ALL)))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT"));
- }
- ;
-
-opt_history_unit:
- /* empty*/ %prec PREC_BELOW_IDENTIFIER_OPT_SPECIAL_CASE
- {
- $$= VERS_UNDEFINED;
- }
- | TRANSACTION_SYM
- {
- $$= VERS_TRX_ID;
- }
- | TIMESTAMP
- {
- $$= VERS_TIMESTAMP;
- }
- ;
-
-history_point:
- TIMESTAMP TEXT_STRING
- {
- Item *item;
- if (!(item= type_handler_datetime2.create_literal_item(thd,
- $2.str, $2.length,
- YYCSCL, true)))
- MYSQL_YYABORT;
- $$= Vers_history_point(VERS_TIMESTAMP, item);
- }
- | function_call_keyword_timestamp
- {
- $$= Vers_history_point(VERS_TIMESTAMP, $1);
- }
- | opt_history_unit bit_expr
- {
- $$= Vers_history_point($1, $2);
- }
- ;
-
-for_portion_of_time_clause:
- FOR_SYM PORTION_SYM OF_SYM remember_tok_start ident FROM
- bit_expr TO_SYM bit_expr
- {
- if (unlikely(0 == strcasecmp($5.str, "SYSTEM_TIME")))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $4);
- MYSQL_YYABORT;
- }
- Lex->period_conditions.init(SYSTEM_TIME_FROM_TO,
- Vers_history_point(VERS_TIMESTAMP, $7),
- Vers_history_point(VERS_TIMESTAMP, $9),
- $5);
- }
- ;
-
-opt_for_portion_of_time_clause:
- /* empty */
- {
- $$= false;
- }
- | for_portion_of_time_clause
- {
- $$= true;
- }
- ;
-
-opt_for_system_time_clause:
- /* empty */
- {
- $$= false;
- }
- | FOR_SYSTEM_TIME_SYM system_time_expr
- {
- $$= true;
- }
- ;
-
-system_time_expr:
- AS OF_SYM history_point
- {
- Lex->vers_conditions.init(SYSTEM_TIME_AS_OF, $3);
- }
- | ALL
- {
- Lex->vers_conditions.init(SYSTEM_TIME_ALL);
- }
- | FROM history_point TO_SYM history_point
- {
- Lex->vers_conditions.init(SYSTEM_TIME_FROM_TO, $2, $4);
- }
- | BETWEEN_SYM history_point AND_SYM history_point
- {
- Lex->vers_conditions.init(SYSTEM_TIME_BETWEEN, $2, $4);
- }
- ;
-
-select_option_list:
- select_option_list select_option
- | select_option
- ;
-
-select_option:
- query_expression_option
- | SQL_NO_CACHE_SYM
- {
- /*
- Allow this flag once per query.
- */
- if (Select->options & OPTION_NO_QUERY_CACHE)
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE"));
- Select->options|= OPTION_NO_QUERY_CACHE;
- }
- | SQL_CACHE_SYM
- {
- /*
- Allow this flag once per query.
- */
- if (Select->options & OPTION_TO_QUERY_CACHE)
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE"));
- Select->options|= OPTION_TO_QUERY_CACHE;
- }
- ;
-
-
-select_lock_type:
- FOR_SYM UPDATE_SYM opt_lock_wait_timeout_new
- {
- $$= $3;
- $$.defined_lock= TRUE;
- $$.update_lock= TRUE;
- }
- | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM opt_lock_wait_timeout_new
- {
- $$= $5;
- $$.defined_lock= TRUE;
- $$.update_lock= FALSE;
- }
- ;
-
-opt_select_lock_type:
- /* empty */
- {
- $$.empty();
- }
- | select_lock_type
- {
- $$= $1;
- }
- ;
-
-opt_lock_wait_timeout_new:
- /* empty */
- {
- $$.empty();
- }
- | WAIT_SYM ulong_num
- {
- $$.defined_timeout= TRUE;
- $$.timeout= $2;
- }
- | NOWAIT_SYM
- {
- $$.defined_timeout= TRUE;
- $$.timeout= 0;
- }
- ;
-
-select_item_list:
- select_item_list ',' select_item
- | select_item
- | '*'
- {
- Item *item= new (thd->mem_root)
- Item_field(thd, &thd->lex->current_select->context,
- NULL, NULL, &star_clex_str);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- if (unlikely(add_item_to_list(thd, item)))
- MYSQL_YYABORT;
- (thd->lex->current_select->with_wild)++;
- }
- ;
-
-select_item:
- remember_name select_sublist_qualified_asterisk remember_end
- {
- if (unlikely(add_item_to_list(thd, $2)))
- MYSQL_YYABORT;
- }
- | remember_name expr remember_end select_alias
- {
- DBUG_ASSERT($1 < $3);
-
- if (unlikely(add_item_to_list(thd, $2)))
- MYSQL_YYABORT;
- if ($4.str)
- {
- if (unlikely(Lex->sql_command == SQLCOM_CREATE_VIEW &&
- check_column_name($4.str)))
- my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), $4.str));
- $2->is_autogenerated_name= FALSE;
- $2->set_name(thd, $4.str, $4.length, system_charset_info);
- }
- else if (!$2->name.str || $2->name.str == item_empty_name)
- {
- $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset());
- }
- }
- ;
-
-remember_tok_start:
- {
- $$= (char*) YYLIP->get_tok_start();
- }
- ;
-
-remember_name:
- {
- $$= (char*) YYLIP->get_cpp_tok_start();
- }
- ;
-
-remember_end:
- {
- $$= (char*) YYLIP->get_cpp_tok_end_rtrim();
- }
- ;
-
-remember_end_opt:
- {
- if (yychar == YYEMPTY)
- $$= (char*) YYLIP->get_cpp_ptr_rtrim();
- else
- $$= (char*) YYLIP->get_cpp_tok_end_rtrim();
- }
- ;
-
-select_alias:
- /* empty */ { $$=null_clex_str;}
- | AS ident { $$=$2; }
- | AS TEXT_STRING_sys { $$=$2; }
- | ident { $$=$1; }
- | 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; }
- | '(' real_ulong_num ')' { $$= $2; }
- ;
-
-optional_braces:
- /* empty */ {}
- | '(' ')' {}
- ;
-
-/* all possible expressions */
-expr:
- expr or expr %prec OR_SYM
- {
- /*
- Design notes:
- Do not use a manually maintained stack like thd->lex->xxx_list,
- but use the internal bison stack ($$, $1 and $3) instead.
- Using the bison stack is:
- - more robust to changes in the grammar,
- - guaranteed to be in sync with the parser state,
- - better for performances (no memory allocation).
- */
- Item_cond_or *item1;
- Item_cond_or *item3;
- if (is_cond_or($1))
- {
- item1= (Item_cond_or*) $1;
- if (is_cond_or($3))
- {
- item3= (Item_cond_or*) $3;
- /*
- (X1 OR X2) OR (Y1 OR Y2) ==> OR (X1, X2, Y1, Y2)
- */
- item3->add_at_head(item1->argument_list());
- $$ = $3;
- }
- else
- {
- /*
- (X1 OR X2) OR Y ==> OR (X1, X2, Y)
- */
- item1->add($3, thd->mem_root);
- $$ = $1;
- }
- }
- else if (is_cond_or($3))
- {
- item3= (Item_cond_or*) $3;
- /*
- X OR (Y1 OR Y2) ==> OR (X, Y1, Y2)
- */
- item3->add_at_head($1, thd->mem_root);
- $$ = $3;
- }
- else
- {
- /* X OR Y */
- $$= new (thd->mem_root) Item_cond_or(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- }
- | expr XOR expr %prec XOR
- {
- /* XOR is a proprietary extension */
- $$= new (thd->mem_root) Item_func_xor(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr and expr %prec AND_SYM
- {
- /* See comments in rule expr: expr or expr */
- Item_cond_and *item1;
- Item_cond_and *item3;
- if (is_cond_and($1))
- {
- item1= (Item_cond_and*) $1;
- if (is_cond_and($3))
- {
- item3= (Item_cond_and*) $3;
- /*
- (X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2)
- */
- item3->add_at_head(item1->argument_list());
- $$ = $3;
- }
- else
- {
- /*
- (X1 AND X2) AND Y ==> AND (X1, X2, Y)
- */
- item1->add($3, thd->mem_root);
- $$ = $1;
- }
- }
- else if (is_cond_and($3))
- {
- item3= (Item_cond_and*) $3;
- /*
- X AND (Y1 AND Y2) ==> AND (X, Y1, Y2)
- */
- item3->add_at_head($1, thd->mem_root);
- $$ = $3;
- }
- else
- {
- /* X AND Y */
- $$= new (thd->mem_root) Item_cond_and(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- }
- | NOT_SYM expr %prec NOT_SYM
- {
- $$= negate_expression(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS TRUE_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_istrue(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS not TRUE_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_isnottrue(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS FALSE_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_isfalse(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS not FALSE_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_isnotfalse(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS UNKNOWN_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_isnull(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS not UNKNOWN_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS NULL_SYM %prec PREC_BELOW_NOT
- {
- $$= new (thd->mem_root) Item_func_isnull(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | expr IS not NULL_SYM %prec IS
- {
- $$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bool_pri
- ;
-
-bool_pri:
- bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
- {
- $$= new (thd->mem_root) Item_func_equal(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bool_pri comp_op predicate %prec '='
- {
- $$= (*$2)(0)->create(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bool_pri comp_op all_or_any '(' subselect ')' %prec '='
- {
- $$= all_any_subquery_creator(thd, $1, $2, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate
- ;
-
-predicate:
- predicate IN_SYM subquery
- {
- $$= new (thd->mem_root) Item_in_subselect(thd, $1, $3);
- if (unlikely(!$$))
- MYSQL_YYABORT;
- }
- | predicate not IN_SYM subquery
- {
- Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
- if (unlikely(!item))
- MYSQL_YYABORT;
- $$= negate_expression(thd, item);
- if (unlikely(!$$))
- MYSQL_YYABORT;
- }
- | predicate IN_SYM '(' expr ')'
- {
- $$= handle_sql2003_note184_exception(thd, $1, true, $4);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate IN_SYM '(' expr ',' expr_list ')'
- {
- $6->push_front($4, thd->mem_root);
- $6->push_front($1, thd->mem_root);
- $$= new (thd->mem_root) Item_func_in(thd, *$6);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate not IN_SYM '(' expr ')'
- {
- $$= handle_sql2003_note184_exception(thd, $1, false, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate not IN_SYM '(' expr ',' expr_list ')'
- {
- $7->push_front($5, thd->mem_root);
- $7->push_front($1, thd->mem_root);
- Item_func_in *item= new (thd->mem_root) Item_func_in(thd, *$7);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= item->neg_transformer(thd);
- }
- | predicate BETWEEN_SYM predicate AND_SYM predicate %prec BETWEEN_SYM
- {
- $$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate not BETWEEN_SYM predicate AND_SYM predicate %prec BETWEEN_SYM
- {
- Item_func_between *item;
- item= new (thd->mem_root) Item_func_between(thd, $1, $4, $6);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= item->neg_transformer(thd);
- }
- | predicate SOUNDS_SYM LIKE predicate
- {
- Item *item1= new (thd->mem_root) Item_func_soundex(thd, $1);
- Item *item4= new (thd->mem_root) Item_func_soundex(thd, $4);
- if (unlikely(item1 == NULL) || unlikely(item4 == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_func_eq(thd, item1, item4);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate LIKE predicate
- {
- $$= new (thd->mem_root) Item_func_like(thd, $1, $3, escape(thd), false);
- if (unlikely(!$$))
- MYSQL_YYABORT;
- }
- | predicate LIKE predicate ESCAPE_SYM predicate %prec LIKE
- {
- Lex->escape_used= true;
- $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $5, true);
- if (unlikely(!$$))
- MYSQL_YYABORT;
- }
- | predicate not LIKE predicate
- {
- Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, escape(thd), false);
- if (unlikely(!item))
- MYSQL_YYABORT;
- $$= item->neg_transformer(thd);
- }
- | predicate not LIKE predicate ESCAPE_SYM predicate %prec LIKE
- {
- Lex->escape_used= true;
- Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $6, true);
- if (unlikely(!item))
- MYSQL_YYABORT;
- $$= item->neg_transformer(thd);
- }
- | predicate REGEXP predicate
- {
- $$= new (thd->mem_root) Item_func_regex(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | predicate not REGEXP predicate
- {
- Item *item= new (thd->mem_root) Item_func_regex(thd, $1, $4);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= negate_expression(thd, item);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr %prec PREC_BELOW_NOT
- ;
-
-bit_expr:
- bit_expr '|' bit_expr %prec '|'
- {
- $$= new (thd->mem_root) Item_func_bit_or(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '&' bit_expr %prec '&'
- {
- $$= new (thd->mem_root) Item_func_bit_and(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT
- {
- $$= new (thd->mem_root) Item_func_shift_left(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT
- {
- $$= new (thd->mem_root) Item_func_shift_right(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr ORACLE_CONCAT_SYM bit_expr
- {
- $$= new (thd->mem_root) Item_func_concat_operator_oracle(thd,
- $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '+' bit_expr %prec '+'
- {
- $$= new (thd->mem_root) Item_func_plus(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '-' bit_expr %prec '-'
- {
- $$= new (thd->mem_root) Item_func_minus(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '+' INTERVAL_SYM expr interval %prec '+'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '-' INTERVAL_SYM expr interval %prec '-'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | INTERVAL_SYM expr interval '+' expr
- /* we cannot put interval before - */
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $5, $2, $3, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | '+' INTERVAL_SYM expr interval '+' expr %prec NEG
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $6, $3, $4, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | '-' INTERVAL_SYM expr interval '+' expr %prec NEG
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $6, $3, $4, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '*' bit_expr %prec '*'
- {
- $$= new (thd->mem_root) Item_func_mul(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '/' bit_expr %prec '/'
- {
- $$= new (thd->mem_root) Item_func_div(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '%' bit_expr %prec '%'
- {
- $$= new (thd->mem_root) Item_func_mod(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr DIV_SYM bit_expr %prec DIV_SYM
- {
- $$= new (thd->mem_root) Item_func_int_div(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr MOD_SYM bit_expr %prec MOD_SYM
- {
- $$= new (thd->mem_root) Item_func_mod(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | bit_expr '^' bit_expr
- {
- $$= new (thd->mem_root) Item_func_bit_xor(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | mysql_concatenation_expr %prec '^'
- ;
-
-or:
- OR_SYM
- | OR2_SYM
- ;
-
-and:
- AND_SYM
- | AND_AND_SYM
- ;
-
-not:
- NOT_SYM
- | NOT2_SYM
- ;
-
-not2:
- '!'
- | NOT2_SYM
- ;
-
-comp_op:
- '=' { $$ = &comp_eq_creator; }
- | GE { $$ = &comp_ge_creator; }
- | '>' { $$ = &comp_gt_creator; }
- | LE { $$ = &comp_le_creator; }
- | '<' { $$ = &comp_lt_creator; }
- | NE { $$ = &comp_ne_creator; }
- ;
-
-all_or_any:
- ALL { $$ = 1; }
- | ANY_SYM { $$ = 0; }
- ;
-
-opt_dyncol_type:
- /* empty */
- {
- $$.set(DYN_COL_NULL); /* automatic type */
- Lex->charset= NULL;
- }
- | AS dyncol_type { $$= $2; }
- ;
-
-dyncol_type:
- numeric_dyncol_type { $$= $1; Lex->charset= NULL; }
- | temporal_dyncol_type { $$= $1; Lex->charset= NULL; }
- | string_dyncol_type { $$= $1; }
- ;
-
-numeric_dyncol_type:
- INT_SYM { $$.set(DYN_COL_INT); }
- | UNSIGNED INT_SYM { $$.set(DYN_COL_UINT); }
- | DOUBLE_SYM { $$.set(DYN_COL_DOUBLE); }
- | REAL { $$.set(DYN_COL_DOUBLE); }
- | FLOAT_SYM { $$.set(DYN_COL_DOUBLE); }
- | DECIMAL_SYM float_options { $$.set(DYN_COL_DECIMAL, $2); }
- ;
-
-temporal_dyncol_type:
- DATE_SYM { $$.set(DYN_COL_DATE); }
- | TIME_SYM opt_field_length { $$.set(DYN_COL_TIME, 0, $2); }
- | DATETIME opt_field_length { $$.set(DYN_COL_DATETIME, 0, $2); }
- ;
-
-string_dyncol_type:
- char
- { Lex->charset= thd->variables.collation_connection; }
- opt_binary
- {
- $$.set(DYN_COL_STRING);
- }
- | nchar
- {
- $$.set(DYN_COL_STRING);
- Lex->charset= national_charset_info;
- }
- ;
-
-dyncall_create_element:
- expr ',' expr opt_dyncol_type
- {
- LEX *lex= Lex;
- $$= (DYNCALL_CREATE_DEF *)
- alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF));
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- $$->key= $1;
- $$->value= $3;
- $$->type= (DYNAMIC_COLUMN_TYPE)$4.dyncol_type();
- $$->cs= lex->charset;
- if ($4.length())
- $$->len= strtoul($4.length(), NULL, 10);
- else
- $$->len= 0;
- if ($4.dec())
- $$->frac= strtoul($4.dec(), NULL, 10);
- else
- $$->len= 0;
- }
- ;
-
-dyncall_create_list:
- dyncall_create_element
- {
- $$= new (thd->mem_root) List<DYNCALL_CREATE_DEF>;
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- $$->push_back($1, thd->mem_root);
- }
- | dyncall_create_list ',' dyncall_create_element
- {
- $1->push_back($3, thd->mem_root);
- $$= $1;
- }
- ;
-
-
-plsql_cursor_attr:
- ISOPEN_SYM { $$= PLSQL_CURSOR_ATTR_ISOPEN; }
- | FOUND_SYM { $$= PLSQL_CURSOR_ATTR_FOUND; }
- | NOTFOUND_SYM { $$= PLSQL_CURSOR_ATTR_NOTFOUND; }
- | ROWCOUNT_SYM { $$= PLSQL_CURSOR_ATTR_ROWCOUNT; }
- ;
-
-explicit_cursor_attr:
- ident PERCENT_ORACLE_SYM plsql_cursor_attr
- {
- if (unlikely(!($$= Lex->make_item_plsql_cursor_attr(thd, &$1, $3))))
- MYSQL_YYABORT;
- }
- ;
-
-
-trim_operands:
- expr { $$.set(TRIM_BOTH, $1); }
- | LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
- | TRAILING expr FROM expr { $$.set(TRIM_TRAILING, $2, $4); }
- | BOTH expr FROM expr { $$.set(TRIM_BOTH, $2, $4); }
- | LEADING FROM expr { $$.set(TRIM_LEADING, $3); }
- | TRAILING FROM expr { $$.set(TRIM_TRAILING, $3); }
- | BOTH FROM expr { $$.set(TRIM_BOTH, $3); }
- | expr FROM expr { $$.set(TRIM_BOTH, $1, $3); }
- ;
-
-/*
- Expressions that the parser allows in a column DEFAULT clause
- without parentheses. These expressions cannot end with a COLLATE clause.
-
- If we allowed any "expr" in DEFAULT clause, there would be a confusion
- in queries like this:
- CREATE TABLE t1 (a TEXT DEFAULT 'a' COLLATE latin1_bin);
- It would be not clear what COLLATE stands for:
- - the collation of the column `a`, or
- - the collation of the string literal 'a'
-
- This restriction allows to parse the above query unambiguiusly:
- COLLATE belongs to the column rather than the literal.
- If one needs COLLATE to belong to the literal, parentheses must be used:
- CREATE TABLE t1 (a TEXT DEFAULT ('a' COLLATE latin1_bin));
- Note: the COLLATE clause is rather meaningless here, but the query
- is syntactically correct.
-
- Note, some of the expressions are not actually allowed in DEFAULT,
- e.g. sum_expr, window_func_expr, ROW(...), VALUES().
- We could move them to simple_expr, but that would make
- these two queries return a different error messages:
- CREATE TABLE t1 (a INT DEFAULT AVG(1));
- CREATE TABLE t1 (a INT DEFAULT (AVG(1)));
- The first query would return "syntax error".
- Currenly both return:
- Function or expression 'avg(' is not allowed for 'DEFAULT' ...
-*/
-column_default_non_parenthesized_expr:
- simple_ident
- | function_call_keyword
- | function_call_nonkeyword
- | function_call_generic
- | function_call_conflict
- | literal
- | param_marker { $$= $1; }
- | variable
- | sum_expr
- {
- if (!Lex->select_stack_top)
- {
- my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0));
- MYSQL_YYABORT;
- }
- }
- | window_func_expr
- {
- if (!Lex->select_stack_top)
- {
- my_error(ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION, MYF(0));
- MYSQL_YYABORT;
- }
- }
- | inverse_distribution_function
- | ROW_SYM '(' expr ',' expr_list ')'
- {
- $5->push_front($3, thd->mem_root);
- $$= new (thd->mem_root) Item_row(thd, *$5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | EXISTS '(' subselect ')'
- {
- $$= new (thd->mem_root) Item_exists_subselect(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | '{' ident expr '}'
- {
- if (unlikely(!($$= $3->make_odbc_literal(thd, &$2))))
- MYSQL_YYABORT;
- }
- | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
- {
- $2->push_front($5, thd->mem_root);
- Item_func_match *i1= new (thd->mem_root) Item_func_match(thd, *$2,
- $6);
- if (unlikely(i1 == NULL))
- MYSQL_YYABORT;
- Select->add_ftfunc_to_list(thd, i1);
- $$= i1;
- }
- | CAST_SYM '(' expr AS cast_type ')'
- {
- if (unlikely(!($$= $5.create_typecast_item(thd, $3, Lex->charset))))
- MYSQL_YYABORT;
- }
- | CASE_SYM when_list_opt_else END
- {
- if (unlikely(!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))))
- MYSQL_YYABORT;
- }
- | CASE_SYM expr when_list_opt_else END
- {
- $3->push_front($2, thd->mem_root);
- if (unlikely(!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))))
- MYSQL_YYABORT;
- }
- | CONVERT_SYM '(' expr ',' cast_type ')'
- {
- if (unlikely(!($$= $5.create_typecast_item(thd, $3, Lex->charset))))
- MYSQL_YYABORT;
- }
- | CONVERT_SYM '(' expr USING charset_name ')'
- {
- $$= new (thd->mem_root) Item_func_conv_charset(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DEFAULT '(' simple_ident ')'
- {
- Item_splocal *il= $3->get_item_splocal();
- if (unlikely(il))
- my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str));
- $$= new (thd->mem_root) Item_default_value(thd, Lex->current_context(),
- $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->default_used= TRUE;
- }
- | VALUE_SYM '(' simple_ident_nospvar ')'
- {
- $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(),
- $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | NEXT_SYM VALUE_SYM FOR_SYM table_ident
- {
- if (unlikely(!($$= Lex->create_item_func_nextval(thd, $4))))
- MYSQL_YYABORT;
- }
- | NEXTVAL_SYM '(' table_ident ')'
- {
- if (unlikely(!($$= Lex->create_item_func_nextval(thd, $3))))
- MYSQL_YYABORT;
- }
- | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident
- {
- if (unlikely(!($$= Lex->create_item_func_lastval(thd, $4))))
- MYSQL_YYABORT;
- }
- | LASTVAL_SYM '(' table_ident ')'
- {
- if (unlikely(!($$= Lex->create_item_func_lastval(thd, $3))))
- MYSQL_YYABORT;
- }
- | SETVAL_SYM '(' table_ident ',' longlong_num ')'
- {
- if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, 0, 1))))
- MYSQL_YYABORT;
- }
- | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ')'
- {
- if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, 0, $7))))
- MYSQL_YYABORT;
- }
- | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ',' ulonglong_num ')'
- {
- if (unlikely(!($$= Lex->create_item_func_setval(thd, $3, $5, $9, $7))))
- MYSQL_YYABORT;
- }
- ;
-
-primary_expr:
- column_default_non_parenthesized_expr
- | explicit_cursor_attr
- | '(' parenthesized_expr ')' { $$= $2; }
- | subquery
- {
- if (!($$= Lex->create_item_query_expression(thd, $1->master_unit())))
- MYSQL_YYABORT;
- }
- ;
-
-string_factor_expr:
- primary_expr
- | string_factor_expr COLLATE_SYM collation_name
- {
- if (unlikely(!($$= new (thd->mem_root) Item_func_set_collation(thd, $1, $3))))
- MYSQL_YYABORT;
- }
- ;
-
-simple_expr:
- string_factor_expr %prec NEG
- | BINARY simple_expr
- {
- Type_cast_attributes at(&my_charset_bin);
- if (unlikely(!($$= type_handler_long_blob.create_typecast_item(thd, $2, at))))
- MYSQL_YYABORT;
- }
- | '+' simple_expr %prec NEG
- {
- $$= $2;
- }
- | '-' simple_expr %prec NEG
- {
- $$= $2->neg(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | '~' simple_expr %prec NEG
- {
- $$= new (thd->mem_root) Item_func_bit_neg(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | not2 simple_expr %prec NEG
- {
- $$= negate_expression(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-mysql_concatenation_expr:
- simple_expr
- | mysql_concatenation_expr MYSQL_CONCAT_SYM simple_expr
- {
- $$= new (thd->mem_root) Item_func_concat(thd, $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-function_call_keyword_timestamp:
- TIMESTAMP '(' expr ')'
- {
- $$= new (thd->mem_root) Item_datetime_typecast(thd, $3,
- AUTO_SEC_PART_DIGITS);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | TIMESTAMP '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_timestamp(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-/*
- Function call syntax using official SQL 2003 keywords.
- Because the function name is an official token,
- a dedicated grammar rule is needed in the parser.
- There is no potential for conflicts
-*/
-function_call_keyword:
- CHAR_SYM '(' expr_list ')'
- {
- $$= new (thd->mem_root) Item_func_char(thd, *$3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | CHAR_SYM '(' expr_list USING charset_name ')'
- {
- $$= new (thd->mem_root) Item_func_char(thd, *$3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | CURRENT_USER optional_braces
- {
- $$= new (thd->mem_root) Item_func_current_user(thd,
- Lex->current_context());
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- 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(thd,
- Lex->current_context());
- if (unlikely($$ == 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(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DAY_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_dayofmonth(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | HOUR_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_hour(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | INSERT '(' expr ',' expr ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_insert(thd, $3, $5, $7, $9);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | INTERVAL_SYM '(' expr ',' expr ')'
- {
- List<Item> *list= new (thd->mem_root) List<Item>;
- if (unlikely(list == NULL))
- MYSQL_YYABORT;
- if (unlikely(list->push_front($5, thd->mem_root)) ||
- unlikely(list->push_front($3, thd->mem_root)))
- MYSQL_YYABORT;
- Item_row *item= new (thd->mem_root) Item_row(thd, *list);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_func_interval(thd, item);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | INTERVAL_SYM '(' expr ',' expr ',' expr_list ')'
- {
- $7->push_front($5, thd->mem_root);
- $7->push_front($3, thd->mem_root);
- Item_row *item= new (thd->mem_root) Item_row(thd, *$7);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_func_interval(thd, item);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | LEFT '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_left(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MINUTE_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_minute(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MONTH_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_month(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | RIGHT '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_right(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SECOND_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_second(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SQL_SYM PERCENT_ORACLE_SYM ROWCOUNT_SYM
- {
- $$= new (thd->mem_root) Item_func_oracle_sql_rowcount(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
- Lex->safe_to_cache_query= 0;
- }
- | TIME_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_time_typecast(thd, $3,
- AUTO_SEC_PART_DIGITS);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | function_call_keyword_timestamp
- {
- $$= $1;
- }
- | TRIM '(' trim_operands ')'
- {
- if (unlikely(!($$= $3.make_item_func_trim(thd))))
- MYSQL_YYABORT;
- }
- | USER_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_func_user(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
- Lex->safe_to_cache_query=0;
- }
- | YEAR_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_year(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-/*
- Function calls using non reserved keywords, with special syntaxic forms.
- Dedicated grammar rules are needed because of the syntax,
- but also have the potential to cause incompatibilities with other
- parts of the language.
- MAINTAINER:
- The only reasons a function should be added here are:
- - for compatibility reasons with another SQL syntax (CURDATE),
- - for typing reasons (GET_FORMAT)
- Any other 'Syntaxic sugar' enhancements should be *STRONGLY*
- discouraged.
-*/
-function_call_nonkeyword:
- ADDDATE_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5,
- INTERVAL_DAY, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | CURDATE optional_braces
- {
- $$= new (thd->mem_root) Item_func_curdate_local(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | CURTIME opt_time_precision
- {
- $$= new (thd->mem_root) Item_func_curtime_local(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DATE_FORMAT_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DATE_FORMAT_SYM '(' expr ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5, $7);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DECODE_MARIADB_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_decode(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DECODE_ORACLE_SYM '(' expr ',' decode_when_list_oracle ')'
- {
- $5->push_front($3, thd->mem_root);
- if (unlikely(!($$= new (thd->mem_root) Item_func_decode_oracle(thd, *$5))))
- MYSQL_YYABORT;
- }
- | EXTRACT_SYM '(' interval FROM expr ')'
- {
- $$=new (thd->mem_root) Item_extract(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | GET_FORMAT '(' date_time_type ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_get_format(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | NOW_SYM opt_time_precision
- {
- $$= new (thd->mem_root) Item_func_now_local(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | POSITION_SYM '(' bit_expr IN_SYM expr ')'
- {
- $$= new (thd->mem_root) Item_func_locate(thd, $5, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SUBDATE_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5,
- INTERVAL_DAY, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr ',' expr ',' expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5, $7))))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr ',' expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5))))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5, $7))))
- MYSQL_YYABORT;
- }
- | SUBSTRING '(' expr FROM expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_substr(thd, $3, $5))))
- MYSQL_YYABORT;
- }
- | SYSDATE opt_time_precision
- {
- /*
- Unlike other time-related functions, SYSDATE() is
- replication-unsafe because it is not affected by the
- TIMESTAMP variable. It is unsafe even if
- sysdate_is_now=1, because the slave may have
- sysdate_is_now=0.
- */
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
- if (global_system_variables.sysdate_is_now == 0)
- $$= new (thd->mem_root) Item_func_sysdate_local(thd, $2);
- else
- $$= new (thd->mem_root) Item_func_now_local(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_date_add_interval(thd, $7, $5, $3, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_timestamp_diff(thd, $5, $7, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | TRIM_ORACLE '(' trim_operands ')'
- {
- if (unlikely(!($$= $3.make_item_func_trim_oracle(thd))))
- MYSQL_YYABORT;
- }
- | UTC_DATE_SYM optional_braces
- {
- $$= new (thd->mem_root) Item_func_curdate_utc(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | UTC_TIME_SYM opt_time_precision
- {
- $$= new (thd->mem_root) Item_func_curtime_utc(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | UTC_TIMESTAMP_SYM opt_time_precision
- {
- $$= new (thd->mem_root) Item_func_now_utc(thd, $2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- |
- COLUMN_ADD_SYM '(' expr ',' dyncall_create_list ')'
- {
- $$= create_func_dyncol_add(thd, $3, *$5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- COLUMN_DELETE_SYM '(' expr ',' expr_list ')'
- {
- $$= create_func_dyncol_delete(thd, $3, *$5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- COLUMN_CHECK_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_dyncol_check(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- COLUMN_CREATE_SYM '(' dyncall_create_list ')'
- {
- $$= create_func_dyncol_create(thd, *$3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')'
- {
- LEX *lex= Lex;
- $$= create_func_dyncol_get(thd, $3, $5, $7.type_handler(),
- $7.length(), $7.dec(),
- lex->charset);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-/*
- Functions calls using a non reserved keyword, and using a regular syntax.
- Because the non reserved keyword is used in another part of the grammar,
- a dedicated rule is needed here.
-*/
-function_call_conflict:
- ASCII_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_ascii(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | CHARSET '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_charset(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | COALESCE '(' expr_list ')'
- {
- $$= new (thd->mem_root) Item_func_coalesce(thd, *$3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | COLLATION_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_collation(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DATABASE '(' ')'
- {
- $$= new (thd->mem_root) Item_func_database(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- Lex->safe_to_cache_query=0;
- }
- | IF_SYM '(' expr ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_if(thd, $3, $5, $7);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | FORMAT_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_format(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | FORMAT_SYM '(' expr ',' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_format(thd, $3, $5, $7);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- /* LAST_VALUE here conflicts with the definition for window functions.
- We have these 2 separate rules to remove the shift/reduce conflict.
- */
- | LAST_VALUE '(' expr ')'
- {
- List<Item> *list= new (thd->mem_root) List<Item>;
- if (unlikely(list == NULL))
- MYSQL_YYABORT;
- list->push_back($3, thd->mem_root);
-
- $$= new (thd->mem_root) Item_func_last_value(thd, *list);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | LAST_VALUE '(' expr_list ',' expr ')'
- {
- $3->push_back($5, thd->mem_root);
- $$= new (thd->mem_root) Item_func_last_value(thd, *$3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MICROSECOND_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_microsecond(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MOD_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_mod(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | OLD_PASSWORD_SYM '(' expr ')'
- {
- $$= new (thd->mem_root)
- Item_func_password(thd, $3, Item_func_password::OLD);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | PASSWORD_SYM '(' expr ')'
- {
- Item* i1;
- i1= new (thd->mem_root) Item_func_password(thd, $3);
- if (unlikely(i1 == NULL))
- MYSQL_YYABORT;
- $$= i1;
- }
- | QUARTER_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_quarter(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | REPEAT_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_repeat(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | REPLACE '(' expr ',' expr ',' expr ')'
- {
- if (unlikely(!($$= Lex->make_item_func_replace(thd, $3, $5, $7))))
- MYSQL_YYABORT;
- }
- | REVERSE_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_reverse(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ROW_COUNT_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_func_row_count(thd);
- if (unlikely($$ == 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(thd, $3, $5, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEEK_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_func_week(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEEK_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_week(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEIGHT_STRING_SYM '(' expr opt_ws_levels ')'
- {
- $$= new (thd->mem_root) Item_func_weight_string(thd, $3, 0, 0, $4);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEIGHT_STRING_SYM '(' expr AS CHAR_SYM ws_nweights opt_ws_levels ')'
- {
- $$= new (thd->mem_root)
- Item_func_weight_string(thd, $3, 0, $6,
- $7 | MY_STRXFRM_PAD_WITH_SPACE);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEIGHT_STRING_SYM '(' expr AS BINARY ws_nweights ')'
- {
- Item *item= new (thd->mem_root) Item_char_typecast(thd, $3, $6,
- &my_charset_bin);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root)
- Item_func_weight_string(thd, item, 0, $6,
- MY_STRXFRM_PAD_WITH_SPACE);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | WEIGHT_STRING_SYM '(' expr ',' ulong_num ',' ulong_num ',' ulong_num ')'
- {
- $$= new (thd->mem_root) Item_func_weight_string(thd, $3, $5, $7,
- $9);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | geometry_function
- {
-#ifdef HAVE_SPATIAL
- $$= $1;
- /* $1 may be NULL, GEOM_NEW not tested for out of memory */
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
-#else
- my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name,
- sym_group_geom.needed_define));
-#endif
- }
- ;
-
-geometry_function:
- CONTAINS_SYM '(' expr ',' expr ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_precise_rel(thd, $3, $5,
- Item_func::SP_CONTAINS_FUNC));
- }
- | GEOMETRYCOLLECTION '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_geometrycollection,
- Geometry::wkb_point));
- }
- | LINESTRING '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_linestring,
- Geometry::wkb_point));
- }
- | MULTILINESTRING '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_multilinestring,
- Geometry::wkb_linestring));
- }
- | MULTIPOINT '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_multipoint,
- Geometry::wkb_point));
- }
- | MULTIPOLYGON '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_multipolygon,
- Geometry::wkb_polygon));
- }
- | POINT_SYM '(' expr ',' expr ')'
- {
- $$= GEOM_NEW(thd, Item_func_point(thd, $3, $5));
- }
- | POLYGON '(' expr_list ')'
- {
- $$= GEOM_NEW(thd,
- Item_func_spatial_collection(thd, *$3,
- Geometry::wkb_polygon,
- Geometry::wkb_linestring));
- }
- | WITHIN '(' expr ',' expr ')'
- {
- $$= GEOM_NEW(thd, Item_func_spatial_precise_rel(thd, $3, $5,
- Item_func::SP_WITHIN_FUNC));
- }
- ;
-
-/*
- Regular function calls.
- The function name is *not* a token, and therefore is guaranteed to not
- introduce side effects to the language in general.
- MAINTAINER:
- All the new functions implemented for new features should fit into
- this category. The place to implement the function itself is
- in sql/item_create.cc
-*/
-function_call_generic:
- IDENT_sys '('
- {
-#ifdef HAVE_DLOPEN
- udf_func *udf= 0;
- LEX *lex= Lex;
- if (using_udf_functions &&
- (udf= find_udf($1.str, $1.length)) &&
- udf->type == UDFTYPE_AGGREGATE)
- {
- if (unlikely(lex->current_select->inc_in_sum_expr()))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- /* Temporary placing the result of find_udf in $3 */
- $<udf>$= udf;
-#endif
- }
- opt_udf_expr_list ')'
- {
- Create_func *builder;
- Item *item= NULL;
-
- if (unlikely(check_routine_name(&$1)))
- MYSQL_YYABORT;
-
- /*
- Implementation note:
- names are resolved with the following order:
- - MySQL native functions,
- - User Defined Functions,
- - Stored Functions (assuming the current <use> database)
-
- This will be revised with WL#2128 (SQL PATH)
- */
- builder= find_native_function_builder(thd, &$1);
- if (builder)
- {
- item= builder->create_func(thd, &$1, $4);
- }
- else
- {
-#ifdef HAVE_DLOPEN
- /* Retrieving the result of find_udf */
- udf_func *udf= $<udf>3;
-
- if (udf)
- {
- if (udf->type == UDFTYPE_AGGREGATE)
- {
- Select->in_sum_expr--;
- }
-
- item= Create_udf_func::s_singleton.create(thd, udf, $4);
- }
- else
-#endif
- {
- builder= find_qualified_function_builder(thd);
- DBUG_ASSERT(builder);
- item= builder->create_func(thd, &$1, $4);
- }
- }
-
- if (unlikely(! ($$= item)))
- MYSQL_YYABORT;
- }
- | ident_cli '.' ident_cli '(' opt_expr_list ')'
- {
- if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, $5))))
- MYSQL_YYABORT;
- }
- ;
-
-fulltext_options:
- opt_natural_language_mode opt_query_expansion
- { $$= $1 | $2; }
- | IN_SYM BOOLEAN_SYM MODE_SYM
- { $$= FT_BOOL; }
- ;
-
-opt_natural_language_mode:
- /* nothing */ { $$= FT_NL; }
- | IN_SYM NATURAL LANGUAGE_SYM MODE_SYM { $$= FT_NL; }
- ;
-
-opt_query_expansion:
- /* nothing */ { $$= 0; }
- | WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; }
- ;
-
-opt_udf_expr_list:
- /* empty */ { $$= NULL; }
- | udf_expr_list { $$= $1; }
- ;
-
-udf_expr_list:
- udf_expr
- {
- $$= new (thd->mem_root) List<Item>;
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- $$->push_back($1, thd->mem_root);
- }
- | udf_expr_list ',' udf_expr
- {
- $1->push_back($3, thd->mem_root);
- $$= $1;
- }
- ;
-
-udf_expr:
- remember_name expr remember_end select_alias
- {
- /*
- Use Item::name as a storage for the attribute value of user
- defined function argument. It is safe to use Item::name
- because the syntax will not allow having an explicit name here.
- See WL#1017 re. udf attributes.
- */
- if ($4.str)
- {
- $2->is_autogenerated_name= FALSE;
- $2->set_name(thd, $4.str, $4.length, system_charset_info);
- }
- /*
- A field has to have its proper name in order for name
- resolution to work, something we are only guaranteed if we
- parse it out. If we hijack the input stream with
- remember_name we may get quoted or escaped names.
- */
- else if ($2->type() != Item::FIELD_ITEM &&
- $2->type() != Item::REF_ITEM /* For HAVING */ )
- $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset());
- $$= $2;
- }
- ;
-
-sum_expr:
- AVG_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_avg(thd, $3, FALSE);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | AVG_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_avg(thd, $4, TRUE);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | BIT_AND '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_and(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | BIT_OR '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_or(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | BIT_XOR '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_xor(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | COUNT_SYM '(' opt_all '*' ')'
- {
- Item *item= new (thd->mem_root) Item_int(thd, (int32) 0L, 1);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_sum_count(thd, item);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | COUNT_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_count(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | COUNT_SYM '(' DISTINCT
- { Select->in_sum_expr++; }
- expr_list
- { Select->in_sum_expr--; }
- ')'
- {
- $$= new (thd->mem_root) Item_sum_count(thd, *$5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MIN_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_min(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- /*
- According to ANSI SQL, DISTINCT is allowed and has
- no sense inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...)
- is processed like an ordinary MIN | MAX()
- */
- | MIN_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_min(thd, $4);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MAX_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_max(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | MAX_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_max(thd, $4);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | STD_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_std(thd, $3, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | VARIANCE_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_variance(thd, $3, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | STDDEV_SAMP_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_std(thd, $3, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | VAR_SAMP_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_variance(thd, $3, 1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SUM_SYM '(' in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_sum(thd, $3, FALSE);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SUM_SYM '(' DISTINCT in_sum_expr ')'
- {
- $$= new (thd->mem_root) Item_sum_sum(thd, $4, TRUE);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | GROUP_CONCAT_SYM '(' opt_distinct
- { Select->in_sum_expr++; }
- expr_list opt_gorder_clause
- opt_gconcat_separator opt_glimit_clause
- ')'
- {
- SELECT_LEX *sel= Select;
- sel->in_sum_expr--;
- $$= new (thd->mem_root)
- Item_func_group_concat(thd, Lex->current_context(),
- $3, $5,
- sel->gorder_list, $7, $8,
- sel->select_limit,
- sel->offset_limit);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- sel->select_limit= NULL;
- sel->offset_limit= NULL;
- sel->explicit_limit= 0;
- $5->empty();
- sel->gorder_list.empty();
- }
- ;
-
-window_func_expr:
- window_func OVER_SYM window_name
- {
- $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- if (unlikely(Select->add_window_func((Item_window_func *) $$)))
- MYSQL_YYABORT;
- }
- |
- window_func OVER_SYM window_spec
- {
- LEX *lex= Lex;
- if (unlikely(Select->add_window_spec(thd, lex->win_ref,
- Select->group_list,
- Select->order_list,
- lex->win_frame)))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1,
- thd->lex->win_spec);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- if (unlikely(Select->add_window_func((Item_window_func *) $$)))
- MYSQL_YYABORT;
- }
- ;
-
-window_func:
- simple_window_func
- |
- sum_expr
- {
- ((Item_sum *) $1)->mark_as_window_func_sum_expr();
- }
- |
- function_call_generic
- {
- Item* item = (Item*)$1;
- /* Only UDF aggregate here possible */
- if ((item == NULL) ||
- (item->type() != Item::SUM_FUNC_ITEM)
- || (((Item_sum *)item)->sum_func() != Item_sum::UDF_SUM_FUNC))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
-
- ((Item_sum *) $1)->mark_as_window_func_sum_expr();
- }
- ;
-
-simple_window_func:
- ROW_NUMBER_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_sum_row_number(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- RANK_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_sum_rank(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- DENSE_RANK_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_sum_dense_rank(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- PERCENT_RANK_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_sum_percent_rank(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- CUME_DIST_SYM '(' ')'
- {
- $$= new (thd->mem_root) Item_sum_cume_dist(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- NTILE_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_ntile(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- FIRST_VALUE_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_first_value(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- LAST_VALUE '(' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_last_value(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- NTH_VALUE_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_nth_value(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- LEAD_SYM '(' expr ')'
- {
- /* No second argument defaults to 1. */
- Item* item_offset= new (thd->mem_root) Item_uint(thd, 1);
- if (unlikely(item_offset == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_sum_lead(thd, $3, item_offset);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- LEAD_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_lead(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- LAG_SYM '(' expr ')'
- {
- /* No second argument defaults to 1. */
- Item* item_offset= new (thd->mem_root) Item_uint(thd, 1);
- if (unlikely(item_offset == NULL))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_sum_lag(thd, $3, item_offset);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- |
- LAG_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_lag(thd, $3, $5);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-
-
-inverse_distribution_function:
- percentile_function OVER_SYM
- '(' opt_window_partition_clause ')'
- {
- LEX *lex= Lex;
- if (unlikely(Select->add_window_spec(thd, lex->win_ref,
- Select->group_list,
- Select->order_list,
- NULL)))
- MYSQL_YYABORT;
- $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1,
- thd->lex->win_spec);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- if (unlikely(Select->add_window_func((Item_window_func *) $$)))
- MYSQL_YYABORT;
- }
- ;
-
-percentile_function:
- inverse_distribution_function_def WITHIN GROUP_SYM '('
- { Select->prepare_add_window_spec(thd); }
- order_by_single_element_list ')'
- {
- $$= $1;
- }
- | MEDIAN_SYM '(' expr ')'
- {
- Item *args= new (thd->mem_root) Item_decimal(thd, "0.5", 3,
- thd->charset());
- if (unlikely(args == NULL) || unlikely(thd->is_error()))
- MYSQL_YYABORT;
- Select->prepare_add_window_spec(thd);
- if (unlikely(add_order_to_list(thd, $3,FALSE)))
- MYSQL_YYABORT;
-
- $$= new (thd->mem_root) Item_sum_percentile_cont(thd, args);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-inverse_distribution_function_def:
- PERCENTILE_CONT_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_percentile_cont(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | PERCENTILE_DISC_SYM '(' expr ')'
- {
- $$= new (thd->mem_root) Item_sum_percentile_disc(thd, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-order_by_single_element_list:
- ORDER_SYM BY order_ident order_dir
- {
- if (unlikely(add_order_to_list(thd, $3,(bool) $4)))
- MYSQL_YYABORT;
- }
- ;
-
-
-window_name:
- ident
- {
- $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING));
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-variable:
- '@'
- {
- if (unlikely(! Lex->parsing_options.allows_variable))
- my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0)));
- }
- variable_aux
- {
- $$= $3;
- }
- ;
-
-variable_aux:
- ident_or_text SET_VAR expr
- {
- Item_func_set_user_var *item;
- $$= item= new (thd->mem_root) Item_func_set_user_var(thd, &$1, $3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- LEX *lex= Lex;
- lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- lex->set_var_list.push_back(item, thd->mem_root);
- }
- | ident_or_text
- {
- $$= new (thd->mem_root) Item_func_get_user_var(thd, &$1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- LEX *lex= Lex;
- lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- }
- | '@' opt_var_ident_type ident_sysvar_name
- {
- if (unlikely(!($$= Lex->make_item_sysvar(thd, $2, &$3))))
- MYSQL_YYABORT;
- }
- | '@' opt_var_ident_type ident_sysvar_name '.' ident
- {
- if (unlikely(!($$= Lex->make_item_sysvar(thd, $2, &$3, &$5))))
- MYSQL_YYABORT;
- }
- ;
-
-opt_distinct:
- /* empty */ { $$ = 0; }
- | DISTINCT { $$ = 1; }
- ;
-
-opt_gconcat_separator:
- /* empty */
- {
- $$= new (thd->mem_root) String(",", 1, &my_charset_latin1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | SEPARATOR_SYM text_string { $$ = $2; }
- ;
-
-opt_gorder_clause:
- /* empty */
- | ORDER_SYM BY gorder_list
- ;
-
-gorder_list:
- gorder_list ',' order_ident order_dir
- {
- if (unlikely(add_gorder_to_list(thd, $3,(bool) $4)))
- MYSQL_YYABORT;
- }
- | order_ident order_dir
- {
- if (unlikely(add_gorder_to_list(thd, $1,(bool) $2)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_glimit_clause:
- /* empty */ { $$ = 0; }
- | glimit_clause { $$ = 1; }
- ;
-
-glimit_clause_init:
- LIMIT{}
- ;
-
-glimit_clause:
- glimit_clause_init glimit_options
- {
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
- }
- ;
-
-glimit_options:
- limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= 0;
- sel->explicit_limit= 1;
- }
- | limit_option ',' limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $3;
- sel->offset_limit= $1;
- sel->explicit_limit= 1;
- }
- | limit_option OFFSET_SYM limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $1;
- sel->offset_limit= $3;
- sel->explicit_limit= 1;
- }
- ;
-
-
-
-in_sum_expr:
- opt_all
- {
- LEX *lex= Lex;
- if (unlikely(lex->current_select->inc_in_sum_expr()))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- expr
- {
- Select->in_sum_expr--;
- $$= $3;
- }
- ;
-
-cast_type:
- BINARY opt_field_length
- { $$.set(&type_handler_long_blob, $2); Lex->charset= &my_charset_bin; }
- | CHAR_SYM opt_field_length
- { Lex->charset= thd->variables.collation_connection; }
- opt_binary
- { $$.set(&type_handler_long_blob, $2); }
- | VARCHAR field_length
- { Lex->charset= thd->variables.collation_connection; }
- opt_binary
- { $$.set(&type_handler_long_blob, $2); }
- | VARCHAR2_ORACLE_SYM field_length
- { Lex->charset= thd->variables.collation_connection; }
- opt_binary
- { $$.set(&type_handler_long_blob, $2); }
- | NCHAR_SYM opt_field_length
- {
- Lex->charset= national_charset_info;
- $$.set(&type_handler_long_blob, $2, 0);
- }
- | cast_type_numeric { $$= $1; Lex->charset= NULL; }
- | cast_type_temporal { $$= $1; Lex->charset= NULL; }
- ;
-
-cast_type_numeric:
- INT_SYM { $$.set(&type_handler_longlong); }
- | SIGNED_SYM { $$.set(&type_handler_longlong); }
- | SIGNED_SYM INT_SYM { $$.set(&type_handler_longlong); }
- | UNSIGNED { $$.set(&type_handler_ulonglong); }
- | UNSIGNED INT_SYM { $$.set(&type_handler_ulonglong); }
- | DECIMAL_SYM float_options { $$.set(&type_handler_newdecimal, $2); }
- | FLOAT_SYM { $$.set(&type_handler_float); }
- | DOUBLE_SYM opt_precision { $$.set(&type_handler_double, $2); }
- ;
-
-cast_type_temporal:
- DATE_SYM { $$.set(&type_handler_newdate); }
- | TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); }
- | DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); }
- | INTERVAL_SYM DAY_SECOND_SYM field_length
- {
- $$.set(&type_handler_interval_DDhhmmssff, 0, $3);
- }
- ;
-
-opt_expr_list:
- /* empty */ { $$= NULL; }
- | expr_list { $$= $1;}
- ;
-
-expr_list:
- expr
- {
- if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
- MYSQL_YYABORT;
- }
- | expr_list ',' expr
- {
- $1->push_back($3, thd->mem_root);
- $$= $1;
- }
- ;
-
-ident_list_arg:
- ident_list { $$= $1; }
- | '(' ident_list ')' { $$= $2; }
- ;
-
-ident_list:
- simple_ident
- {
- $$= new (thd->mem_root) List<Item>;
- if (unlikely($$ == NULL) ||
- unlikely($$->push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | ident_list ',' simple_ident
- {
- $1->push_back($3, thd->mem_root);
- $$= $1;
- }
- ;
-
-when_list:
- WHEN_SYM expr THEN_SYM expr
- {
- $$= new (thd->mem_root) List<Item>;
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- if (unlikely($$->push_back($2, thd->mem_root) ||
- $$->push_back($4, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | when_list WHEN_SYM expr THEN_SYM expr
- {
- if (unlikely($1->push_back($3, thd->mem_root) ||
- $1->push_back($5, thd->mem_root)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-when_list_opt_else:
- when_list
- | when_list ELSE expr
- {
- if (unlikely($1->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-decode_when_list_oracle:
- expr ',' expr
- {
- $$= new (thd->mem_root) List<Item>;
- if (unlikely($$ == NULL) ||
- unlikely($$->push_back($1, thd->mem_root)) ||
- unlikely($$->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
-
- }
- | decode_when_list_oracle ',' expr
- {
- $$= $1;
- if (unlikely($$->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-
-/* Equivalent to <table reference> in the SQL:2003 standard. */
-/* Warning - may return NULL in case of incomplete SELECT */
-table_ref:
- table_factor { $$= $1; }
- | join_table
- {
- LEX *lex= Lex;
- if (unlikely(!($$= lex->current_select->nest_last_join(thd))))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- ;
-
-join_table_list:
- derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); }
- ;
-
-/*
- The ODBC escape syntax for Outer Join is: '{' OJ join_table '}'
- The parser does not define OJ as a token, any ident is accepted
- instead in $2 (ident). Also, all productions from table_ref can
- be escaped, not only join_table. Both syntax extensions are safe
- and are ignored.
-*/
-esc_table_ref:
- table_ref { $$=$1; }
- | '{' ident table_ref '}' { $$=$3; }
- ;
-
-/* Equivalent to <table reference list> in the SQL:2003 standard. */
-/* Warning - may return NULL in case of incomplete SELECT */
-derived_table_list:
- esc_table_ref
- {
- $$=$1;
- Select->add_joined_table($1);
- }
- | derived_table_list ',' esc_table_ref
- {
- MYSQL_YYABORT_UNLESS($1 && ($$=$3));
- Select->add_joined_table($3);
- }
- ;
-
-/*
- Notice that JOIN can be a left-associative operator in one context and
- a right-associative operator in another context (see the comment for
- st_select_lex::add_cross_joined_table).
-*/
-join_table:
- /* INNER JOIN variants */
- table_ref normal_join table_ref %prec CONDITIONLESS_JOIN
- {
- MYSQL_YYABORT_UNLESS($1 && ($$=$3));
- if (unlikely(Select->add_cross_joined_table($1, $3, $2)))
- MYSQL_YYABORT;
- }
- | table_ref normal_join table_ref
- ON
- {
- MYSQL_YYABORT_UNLESS($1 && $3);
- Select->add_joined_table($1);
- Select->add_joined_table($3);
- /* Change the current name resolution context to a local context. */
- if (unlikely(push_new_name_resolution_context(thd, $1, $3)))
- MYSQL_YYABORT;
- Select->parsing_place= IN_ON;
- }
- expr
- {
- $3->straight=$2;
- add_join_on(thd, $3, $6);
- $3->on_context= Lex->pop_context();
- Select->parsing_place= NO_MATTER;
- }
- | table_ref normal_join table_ref
- USING
- {
- MYSQL_YYABORT_UNLESS($1 && $3);
- Select->add_joined_table($1);
- Select->add_joined_table($3);
- }
- '(' using_list ')'
- {
- $3->straight=$2;
- add_join_natural($1,$3,$7,Select);
- $$=$3;
- }
- | table_ref NATURAL inner_join table_factor
- {
- MYSQL_YYABORT_UNLESS($1 && ($$=$4));
- Select->add_joined_table($1);
- Select->add_joined_table($4);
- $4->straight=$3;
- add_join_natural($1,$4,NULL,Select);
- }
-
- /* LEFT JOIN variants */
- | table_ref LEFT opt_outer JOIN_SYM table_ref
- ON
- {
- MYSQL_YYABORT_UNLESS($1 && $5);
- Select->add_joined_table($1);
- Select->add_joined_table($5);
- /* Change the current name resolution context to a local context. */
- if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
- MYSQL_YYABORT;
- Select->parsing_place= IN_ON;
- }
- expr
- {
- add_join_on(thd, $5, $8);
- $5->on_context= Lex->pop_context();
- $5->outer_join|=JOIN_TYPE_LEFT;
- $$=$5;
- Select->parsing_place= NO_MATTER;
- }
- | table_ref LEFT opt_outer JOIN_SYM table_factor
- {
- MYSQL_YYABORT_UNLESS($1 && $5);
- Select->add_joined_table($1);
- Select->add_joined_table($5);
- }
- USING '(' using_list ')'
- {
- add_join_natural($1,$5,$9,Select);
- $5->outer_join|=JOIN_TYPE_LEFT;
- $$=$5;
- }
- | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
- {
- MYSQL_YYABORT_UNLESS($1 && $6);
- Select->add_joined_table($1);
- Select->add_joined_table($6);
- add_join_natural($1,$6,NULL,Select);
- $6->outer_join|=JOIN_TYPE_LEFT;
- $$=$6;
- }
-
- /* RIGHT JOIN variants */
- | table_ref RIGHT opt_outer JOIN_SYM table_ref
- ON
- {
- MYSQL_YYABORT_UNLESS($1 && $5);
- Select->add_joined_table($1);
- Select->add_joined_table($5);
- /* Change the current name resolution context to a local context. */
- if (unlikely(push_new_name_resolution_context(thd, $1, $5)))
- MYSQL_YYABORT;
- Select->parsing_place= IN_ON;
- }
- expr
- {
- LEX *lex= Lex;
- if (unlikely(!($$= lex->current_select->convert_right_join())))
- MYSQL_YYABORT;
- add_join_on(thd, $$, $8);
- $1->on_context= Lex->pop_context();
- Select->parsing_place= NO_MATTER;
- }
- | table_ref RIGHT opt_outer JOIN_SYM table_factor
- {
- MYSQL_YYABORT_UNLESS($1 && $5);
- Select->add_joined_table($1);
- Select->add_joined_table($5);
- }
- USING '(' using_list ')'
- {
- LEX *lex= Lex;
- if (unlikely(!($$= lex->current_select->convert_right_join())))
- MYSQL_YYABORT;
- add_join_natural($$,$5,$9,Select);
- }
- | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
- {
- MYSQL_YYABORT_UNLESS($1 && $6);
- Select->add_joined_table($1);
- Select->add_joined_table($6);
- add_join_natural($6,$1,NULL,Select);
- LEX *lex= Lex;
- if (unlikely(!($$= lex->current_select->convert_right_join())))
- MYSQL_YYABORT;
- }
- ;
-
-
-inner_join: /* $$ set if using STRAIGHT_JOIN, false otherwise */
- JOIN_SYM { $$ = 0; }
- | INNER_SYM JOIN_SYM { $$ = 0; }
- | STRAIGHT_JOIN { $$ = 1; }
- ;
-
-normal_join:
- 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;
- Select->parsing_place= Select->save_parsing_place;
- Select->save_parsing_place= NO_MATTER;
- }
- ;
-
-table_factor:
- table_primary_ident_opt_parens { $$= $1; }
- | table_primary_derived_opt_parens { $$= $1; }
- | join_table_parens
- {
- $1->nested_join->nest_type= 0;
- $$= $1;
- }
- | table_reference_list_parens { $$= $1; }
- ;
-
-table_primary_ident_opt_parens:
- table_primary_ident { $$= $1; }
- | '(' table_primary_ident_opt_parens ')' { $$= $2; }
- ;
-
-table_primary_derived_opt_parens:
- table_primary_derived { $$= $1; }
- | '(' table_primary_derived_opt_parens ')' { $$= $2; }
- ;
-
-table_reference_list_parens:
- '(' table_reference_list_parens ')' { $$= $2; }
- | '(' nested_table_reference_list ')'
- {
- if (!($$= Select->end_nested_join(thd)))
- MYSQL_YYABORT;
- }
- ;
-
-nested_table_reference_list:
- table_ref ',' table_ref
- {
- if (Select->init_nested_join(thd))
- MYSQL_YYABORT;
- Select->add_joined_table($1);
- Select->add_joined_table($3);
- $$= $1->embedding;
- }
- | nested_table_reference_list ',' table_ref
- {
- Select->add_joined_table($3);
- $$= $1;
- }
- ;
-
-join_table_parens:
- '(' join_table_parens ')' { $$= $2; }
- | '(' join_table ')'
- {
- LEX *lex= Lex;
- if (!($$= lex->current_select->nest_last_join(thd)))
- {
- thd->parse_error();
- MYSQL_YYABORT;
- }
- }
- ;
-
-
-table_primary_ident:
- table_ident opt_use_partition opt_for_system_time_clause
- opt_table_alias_clause opt_key_definition
- {
- SELECT_LEX *sel= Select;
- sel->table_join_options= 0;
- if (!($$= Select->add_table_to_list(thd, $1, $4,
- Select->get_table_join_options(),
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- Select->pop_index_hints(),
- $2)))
- MYSQL_YYABORT;
- if ($3)
- $$->vers_conditions= Lex->vers_conditions;
- }
- ;
-
-table_primary_derived:
- subquery
- opt_for_system_time_clause table_alias_clause
- {
- if (!($$= Lex->parsed_derived_table($1->master_unit(), $2, $3)))
- MYSQL_YYABORT;
- }
- ;
- ;
-
-opt_outer:
- /* empty */ {}
- | OUTER {}
- ;
-
-index_hint_clause:
- /* empty */
- {
- $$= thd->variables.old_mode ? INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL;
- }
- | FOR_SYM JOIN_SYM { $$= INDEX_HINT_MASK_JOIN; }
- | FOR_SYM ORDER_SYM BY { $$= INDEX_HINT_MASK_ORDER; }
- | FOR_SYM GROUP_SYM BY { $$= INDEX_HINT_MASK_GROUP; }
- ;
-
-index_hint_type:
- FORCE_SYM { $$= INDEX_HINT_FORCE; }
- | IGNORE_SYM { $$= INDEX_HINT_IGNORE; }
- ;
-
-index_hint_definition:
- index_hint_type key_or_index index_hint_clause
- {
- Select->set_index_hint_type($1, $3);
- }
- '(' key_usage_list ')'
- | USE_SYM key_or_index index_hint_clause
- {
- Select->set_index_hint_type(INDEX_HINT_USE, $3);
- }
- '(' opt_key_usage_list ')'
- ;
-
-index_hints_list:
- index_hint_definition
- | index_hints_list index_hint_definition
- ;
-
-opt_index_hints_list:
- /* empty */
- | { Select->alloc_index_hints(thd); } index_hints_list
- ;
-
-opt_key_definition:
- { Select->clear_index_hints(); }
- opt_index_hints_list
- ;
-
-opt_key_usage_list:
- /* empty */ { Select->add_index_hint(thd, NULL, 0); }
- | key_usage_list {}
- ;
-
-key_usage_element:
- ident
- { Select->add_index_hint(thd, $1.str, $1.length); }
- | PRIMARY_SYM
- { Select->add_index_hint(thd, "PRIMARY", 7); }
- ;
-
-key_usage_list:
- key_usage_element
- | key_usage_list ',' key_usage_element
- ;
-
-using_list:
- ident
- {
- if (unlikely(!($$= new (thd->mem_root) List<String>)))
- MYSQL_YYABORT;
- String *s= new (thd->mem_root) String((const char *) $1.str,
- $1.length,
- system_charset_info);
- if (unlikely(unlikely(s == NULL)))
- MYSQL_YYABORT;
- $$->push_back(s, thd->mem_root);
- }
- | using_list ',' ident
- {
- String *s= new (thd->mem_root) String((const char *) $3.str,
- $3.length,
- system_charset_info);
- if (unlikely(unlikely(s == NULL)))
- MYSQL_YYABORT;
- if (unlikely($1->push_back(s, thd->mem_root)))
- MYSQL_YYABORT;
- $$= $1;
- }
- ;
-
-interval:
- interval_time_stamp {}
- | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
- | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
- | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
- | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
- | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
- | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
- | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
- | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
- | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
- | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
- | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
- ;
-
-interval_time_stamp:
- DAY_SYM { $$=INTERVAL_DAY; }
- | WEEK_SYM { $$=INTERVAL_WEEK; }
- | HOUR_SYM { $$=INTERVAL_HOUR; }
- | MINUTE_SYM { $$=INTERVAL_MINUTE; }
- | MONTH_SYM { $$=INTERVAL_MONTH; }
- | QUARTER_SYM { $$=INTERVAL_QUARTER; }
- | SECOND_SYM { $$=INTERVAL_SECOND; }
- | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
- | YEAR_SYM { $$=INTERVAL_YEAR; }
- ;
-
-date_time_type:
- DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;}
- | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;}
- | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;}
- | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;}
- ;
-
-table_alias:
- /* empty */
- | AS
- | '='
- ;
-
-opt_table_alias_clause:
- /* empty */ { $$=0; }
- | table_alias_clause { $$= $1; }
- ;
-
-table_alias_clause:
- table_alias ident_table_alias
- {
- $$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING));
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_all:
- /* empty */
- | ALL
- ;
-
-opt_where_clause:
- /* empty */ { Select->where= 0; }
- | WHERE
- {
- Select->parsing_place= IN_WHERE;
- }
- expr
- {
- SELECT_LEX *select= Select;
- select->where= normalize_cond(thd, $3);
- select->parsing_place= NO_MATTER;
- if ($3)
- $3->top_level_item();
- }
- ;
-
-opt_having_clause:
- /* empty */
- | HAVING
- {
- Select->parsing_place= IN_HAVING;
- }
- expr
- {
- SELECT_LEX *sel= Select;
- sel->having= normalize_cond(thd, $3);
- sel->parsing_place= NO_MATTER;
- if ($3)
- $3->top_level_item();
- }
- ;
-
-/*
- group by statement in select
-*/
-
-opt_group_clause:
- /* empty */
- | GROUP_SYM BY group_list olap_opt
- ;
-
-group_list:
- group_list ',' order_ident order_dir
- {
- if (unlikely(add_group_to_list(thd, $3,(bool) $4)))
- MYSQL_YYABORT;
- }
- | order_ident order_dir
- {
- if (unlikely(add_group_to_list(thd, $1,(bool) $2)))
- MYSQL_YYABORT;
- }
- ;
-
-olap_opt:
- /* empty */ {}
- | WITH_CUBE_SYM
- {
- /*
- 'WITH CUBE' is reserved in the MySQL syntax, but not implemented,
- and cause LALR(2) conflicts.
- This syntax is not standard.
- MySQL syntax: GROUP BY col1, col2, col3 WITH CUBE
- SQL-2003: GROUP BY ... CUBE(col1, col2, col3)
- */
- LEX *lex=Lex;
- if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE",
- "global union parameters"));
- lex->current_select->olap= CUBE_TYPE;
-
- my_yyabort_error((ER_NOT_SUPPORTED_YET, MYF(0), "CUBE"));
- }
- | WITH_ROLLUP_SYM
- {
- /*
- 'WITH ROLLUP' is needed for backward compatibility,
- and cause LALR(2) conflicts.
- This syntax is not standard.
- MySQL syntax: GROUP BY col1, col2, col3 WITH ROLLUP
- SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3)
- */
- LEX *lex= Lex;
- if (unlikely(lex->current_select->get_linkage() == GLOBAL_OPTIONS_TYPE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP",
- "global union parameters"));
- lex->current_select->olap= ROLLUP_TYPE;
- }
- ;
-
-/*
- optional window clause in select
-*/
-
-opt_window_clause:
- /* empty */
- {}
- | WINDOW_SYM
- window_def_list
- {}
- ;
-
-window_def_list:
- window_def_list ',' window_def
- | window_def
- ;
-
-window_def:
- window_name AS window_spec
- {
- LEX *lex= Lex;
- if (unlikely(Select->add_window_def(thd, $1, lex->win_ref,
- Select->group_list,
- Select->order_list,
- lex->win_frame)))
- MYSQL_YYABORT;
- }
- ;
-
-window_spec:
- '('
- { Select->prepare_add_window_spec(thd); }
- opt_window_ref opt_window_partition_clause
- opt_window_order_clause opt_window_frame_clause
- ')'
- { }
- ;
-
-opt_window_ref:
- /* empty */ {}
- | ident
- {
- thd->lex->win_ref= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING));
- if (unlikely(thd->lex->win_ref == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_window_partition_clause:
- /* empty */ { }
- | PARTITION_SYM BY group_list
- ;
-
-opt_window_order_clause:
- /* empty */ { }
- | ORDER_SYM BY order_list { Select->order_list= *($3); }
- ;
-
-opt_window_frame_clause:
- /* empty */ {}
- | window_frame_units window_frame_extent opt_window_frame_exclusion
- {
- LEX *lex= Lex;
- lex->win_frame=
- new (thd->mem_root) Window_frame($1,
- lex->frame_top_bound,
- lex->frame_bottom_bound,
- $3);
- if (unlikely(lex->win_frame == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-window_frame_units:
- ROWS_SYM { $$= Window_frame::UNITS_ROWS; }
- | RANGE_SYM { $$= Window_frame::UNITS_RANGE; }
- ;
-
-window_frame_extent:
- window_frame_start
- {
- LEX *lex= Lex;
- lex->frame_top_bound= $1;
- lex->frame_bottom_bound=
- new (thd->mem_root)
- Window_frame_bound(Window_frame_bound::CURRENT, NULL);
- if (unlikely(lex->frame_bottom_bound == NULL))
- MYSQL_YYABORT;
- }
- | BETWEEN_SYM window_frame_bound AND_SYM window_frame_bound
- {
- LEX *lex= Lex;
- lex->frame_top_bound= $2;
- lex->frame_bottom_bound= $4;
- }
- ;
-
-window_frame_start:
- UNBOUNDED_SYM PRECEDING_SYM
- {
- $$= new (thd->mem_root)
- Window_frame_bound(Window_frame_bound::PRECEDING, NULL);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | CURRENT_SYM ROW_SYM
- {
- $$= new (thd->mem_root)
- Window_frame_bound(Window_frame_bound::CURRENT, NULL);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | literal PRECEDING_SYM
- {
- $$= new (thd->mem_root)
- Window_frame_bound(Window_frame_bound::PRECEDING, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-window_frame_bound:
- window_frame_start { $$= $1; }
- | UNBOUNDED_SYM FOLLOWING_SYM
- {
- $$= new (thd->mem_root)
- Window_frame_bound(Window_frame_bound::FOLLOWING, NULL);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | literal FOLLOWING_SYM
- {
- $$= new (thd->mem_root)
- Window_frame_bound(Window_frame_bound::FOLLOWING, $1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_window_frame_exclusion:
- /* empty */ { $$= Window_frame::EXCL_NONE; }
- | EXCLUDE_SYM CURRENT_SYM ROW_SYM
- { $$= Window_frame::EXCL_CURRENT_ROW; }
- | EXCLUDE_SYM GROUP_SYM
- { $$= Window_frame::EXCL_GROUP; }
- | EXCLUDE_SYM TIES_SYM
- { $$= Window_frame::EXCL_TIES; }
- | EXCLUDE_SYM NO_SYM OTHERS_MARIADB_SYM
- { $$= Window_frame::EXCL_NONE; }
- | EXCLUDE_SYM NO_SYM OTHERS_ORACLE_SYM
- { $$= Window_frame::EXCL_NONE; }
- ;
-
-/*
- Order by statement in ALTER TABLE
-*/
-
-alter_order_clause:
- ORDER_SYM BY alter_order_list
- ;
-
-alter_order_list:
- alter_order_list ',' alter_order_item
- | alter_order_item
- ;
-
-alter_order_item:
- simple_ident_nospvar order_dir
- {
- bool ascending= ($2 == 1) ? true : false;
- if (unlikely(add_order_to_list(thd, $1, ascending)))
- MYSQL_YYABORT;
- }
- ;
-
-/*
- Order by statement in select
-*/
-
-opt_order_clause:
- /* empty */
- { $$= NULL; }
- | order_clause
- { $$= $1; }
- ;
-
-order_clause:
- ORDER_SYM BY
- {
- thd->where= "ORDER clause";
- }
- order_list
- {
- $$= $4;
- }
- ;
-
-order_list:
- order_list ',' order_ident order_dir
- {
- $$= $1;
- if (add_to_list(thd, *$$, $3,(bool) $4))
- MYSQL_YYABORT;
- }
- | order_ident order_dir
- {
- $$= new (thd->mem_root) SQL_I_List<ORDER>();
- if (add_to_list(thd, *$$, $1, (bool) $2))
- MYSQL_YYABORT;
- }
- ;
-
-order_dir:
- /* empty */ { $$ = 1; }
- | ASC { $$ =1; }
- | DESC { $$ =0; }
- ;
-
-opt_limit_clause:
- /* empty */
- { $$.empty(); }
- | limit_clause
- { $$= $1; }
- ;
-
-limit_clause:
- LIMIT limit_options
- {
- $$= $2;
- if (!$$.select_limit->basic_const_item() ||
- $$.select_limit->val_int() > 0)
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
- }
- | LIMIT limit_options
- ROWS_SYM EXAMINED_SYM limit_rows_option
- {
- $$= $2;
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
- }
- | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option
- {
- $$.select_limit= 0;
- $$.offset_limit= 0;
- $$.explicit_limit= 1;
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
- }
- ;
-
-opt_global_limit_clause:
- opt_limit_clause
- {
- Select->explicit_limit= $1.explicit_limit;
- Select->select_limit= $1.select_limit;
- Select->offset_limit= $1.offset_limit;
- }
- ;
-
-limit_options:
- limit_option
- {
- $$.select_limit= $1;
- $$.offset_limit= 0;
- $$.explicit_limit= 1;
- }
- | limit_option ',' limit_option
- {
- $$.select_limit= $3;
- $$.offset_limit= $1;
- $$.explicit_limit= 1;
- }
- | limit_option OFFSET_SYM limit_option
- {
- $$.select_limit= $1;
- $$.offset_limit= $3;
- $$.explicit_limit= 1;
- }
- ;
-
-limit_option:
- ident_cli
- {
- if (unlikely(!($$= Lex->create_item_limit(thd, &$1))))
- MYSQL_YYABORT;
- }
- | ident_cli '.' ident_cli
- {
- if (unlikely(!($$= Lex->create_item_limit(thd, &$1, &$3))))
- MYSQL_YYABORT;
- }
- | param_marker
- {
- $1->limit_clause_param= TRUE;
- }
- | ULONGLONG_NUM
- {
- $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | LONG_NUM
- {
- $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | NUM
- {
- $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-limit_rows_option:
- limit_option
- {
- LEX *lex=Lex;
- lex->limit_rows_examined= $1;
- }
- ;
-
-delete_limit_clause:
- /* empty */
- {
- LEX *lex=Lex;
- lex->current_select->select_limit= 0;
- }
- | LIMIT limit_option
- {
- SELECT_LEX *sel= Select;
- sel->select_limit= $2;
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
- sel->explicit_limit= 1;
- }
- | LIMIT ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
- | LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; }
- ;
-
-order_limit_lock:
- order_or_limit
- {
- $$= $1;
- $$->lock.empty();
- }
- | order_or_limit select_lock_type
- {
- $$= $1;
- $$->lock= $2;
- }
- | select_lock_type
- {
- $$= new(thd->mem_root) Lex_order_limit_lock;
- if (!$$)
- YYABORT;
- $$->order_list= NULL;
- $$->limit.empty();
- $$->lock= $1;
- }
- ;
-opt_order_limit_lock:
- /* empty */
- {
- Lex->pop_select();
- $$= NULL;
- }
- | order_limit_lock { $$= $1; }
- ;
-
-query_expression_tail:
- order_limit_lock
- ;
-
-opt_query_expression_tail:
- opt_order_limit_lock
- ;
-
-opt_procedure_or_into:
- /* empty */
- {
- $$.empty();
- }
- | procedure_clause opt_select_lock_type
- {
- $$= $2;
- }
- | into opt_select_lock_type
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX,
- ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX),
- "<select expression> INTO <destination>;",
- "'SELECT <select list> INTO <destination>"
- " FROM...'");
- $$= $2;
- }
- ;
-
-
-order_or_limit:
- order_clause opt_limit_clause
- {
- $$= new(thd->mem_root) Lex_order_limit_lock;
- if (!$$)
- YYABORT;
- $$->order_list= $1;
- $$->limit= $2;
- }
- | limit_clause
- {
- Lex_order_limit_lock *op= $$= new(thd->mem_root) Lex_order_limit_lock;
- if (!$$)
- YYABORT;
- op->order_list= NULL;
- op->limit= $1;
- $$->order_list= NULL;
- $$->limit= $1;
- }
- ;
-
-
-opt_plus:
- /* empty */
- | '+'
- ;
-
-int_num:
- opt_plus NUM { int error; $$= (int) my_strtoll10($2.str, (char**) 0, &error); }
- | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); }
- ;
-
-ulong_num:
- opt_plus NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
- | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); }
- | opt_plus LONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus FLOAT_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); }
- ;
-
-real_ulong_num:
- NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); }
- | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); }
- | dec_num_error { MYSQL_YYABORT; }
- ;
-
-longlong_num:
- opt_plus NUM { int error; $$= (longlong) my_strtoll10($2.str, (char**) 0, &error); }
- | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); }
- | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); }
- | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); }
- ;
-
-ulonglong_num:
- opt_plus NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus LONG_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); }
- | opt_plus FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); }
- ;
-
-real_ulonglong_num:
- NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | HEX_NUM { $$= strtoull($1.str, (char**) 0, 16); }
- | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); }
- | dec_num_error { MYSQL_YYABORT; }
- ;
-
-dec_num_error:
- dec_num
- { thd->parse_error(ER_ONLY_INTEGERS_ALLOWED); }
- ;
-
-dec_num:
- DECIMAL_NUM
- | FLOAT_NUM
- ;
-
-choice:
- ulong_num { $$= $1 != 0 ? HA_CHOICE_YES : HA_CHOICE_NO; }
- | DEFAULT { $$= HA_CHOICE_UNDEF; }
- ;
-
-bool:
- ulong_num { $$= $1 != 0; }
- | TRUE_SYM { $$= 1; }
- | FALSE_SYM { $$= 0; }
- ;
-
-procedure_clause:
- PROCEDURE_SYM ident /* Procedure name */
- {
- LEX *lex=Lex;
-
- lex->proc_list.elements=0;
- lex->proc_list.first=0;
- lex->proc_list.next= &lex->proc_list.first;
- Item_field *item= new (thd->mem_root)
- Item_field(thd, &lex->current_select->context,
- NULL, NULL, &$2);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- if (unlikely(add_proc_to_list(thd, item)))
- MYSQL_YYABORT;
- Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
-
- /*
- PROCEDURE CLAUSE cannot handle subquery as one of its parameter,
- so disallow any subqueries further.
- Alow subqueries back once the parameters are reduced.
- */
- Lex->clause_that_disallows_subselect= "PROCEDURE";
- Select->options|= OPTION_PROCEDURE_CLAUSE;
- }
- '(' procedure_list ')'
- {
- /* Subqueries are allowed from now.*/
- Lex->clause_that_disallows_subselect= NULL;
- }
- ;
-
-procedure_list:
- /* empty */ {}
- | procedure_list2 {}
- ;
-
-procedure_list2:
- procedure_list2 ',' procedure_item
- | procedure_item
- ;
-
-procedure_item:
- remember_name expr remember_end
- {
- if (unlikely(add_proc_to_list(thd, $2)))
- MYSQL_YYABORT;
- if (!$2->name.str || $2->name.str == item_empty_name)
- $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset());
- }
- ;
-
-select_var_list_init:
- {
- LEX *lex=Lex;
- if (!lex->describe &&
- unlikely((!(lex->result= new (thd->mem_root)
- select_dumpvar(thd)))))
- MYSQL_YYABORT;
- }
- select_var_list
- {}
- ;
-
-select_var_list:
- select_var_list ',' select_var_ident
- | select_var_ident {}
- ;
-
-select_var_ident: select_outvar
- {
- if (Lex->result)
- {
- if (unlikely($1 == NULL))
- MYSQL_YYABORT;
- ((select_dumpvar *)Lex->result)->var_list.push_back($1, thd->mem_root);
- }
- else
- {
- /*
- The parser won't create select_result instance only
- if it's an EXPLAIN.
- */
- DBUG_ASSERT(Lex->describe);
- }
- }
- ;
-
-select_outvar:
- '@' ident_or_text
- {
- $$ = Lex->result ? new (thd->mem_root) my_var_user(&$2) : NULL;
- }
- | ident_or_text
- {
- if (unlikely(!($$= Lex->create_outvar(thd, &$1)) && Lex->result))
- MYSQL_YYABORT;
- }
- | ident '.' ident
- {
- if (unlikely(!($$= Lex->create_outvar(thd, &$1, &$3)) && Lex->result))
- MYSQL_YYABORT;
- }
- ;
-
-into:
- INTO into_destination
- {}
- ;
-
-into_destination:
- OUTFILE TEXT_STRING_filesystem
- {
- LEX *lex= Lex;
- lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- if (unlikely(!(lex->exchange=
- new (thd->mem_root) sql_exchange($2.str, 0))) ||
- unlikely(!(lex->result=
- new (thd->mem_root)
- select_export(thd, lex->exchange))))
- MYSQL_YYABORT;
- }
- opt_load_data_charset
- { Lex->exchange->cs= $4; }
- opt_field_term opt_line_term
- | DUMPFILE TEXT_STRING_filesystem
- {
- LEX *lex=Lex;
- if (!lex->describe)
- {
- lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- if (unlikely(!(lex->exchange=
- new (thd->mem_root) sql_exchange($2.str,1))))
- MYSQL_YYABORT;
- if (unlikely(!(lex->result=
- new (thd->mem_root)
- select_dump(thd, lex->exchange))))
- MYSQL_YYABORT;
- }
- }
- | select_var_list_init
- {
- Lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- }
- ;
-
-/*
- DO statement
-*/
-
-do:
- DO_SYM
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_DO;
- if (lex->main_select_push(true))
- MYSQL_YYABORT;
- mysql_init_select(lex);
- }
- expr_list
- {
- Lex->insert_list= $3;
- Lex->pop_select(); //main select
- }
- ;
-
-/*
- Drop : delete tables or index or user
-*/
-
-drop:
- DROP opt_temporary table_or_tables opt_if_exists
- {
- LEX *lex=Lex;
- lex->set_command(SQLCOM_DROP_TABLE, $2, $4);
- YYPS->m_lock_type= TL_UNLOCK;
- YYPS->m_mdl_type= MDL_EXCLUSIVE;
- }
- table_list opt_lock_wait_timeout opt_restrict
- {}
- | DROP INDEX_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- opt_if_exists_table_element ident ON table_ident opt_lock_wait_timeout
- {
- LEX *lex=Lex;
- Alter_drop *ad= (new (thd->mem_root)
- Alter_drop(Alter_drop::KEY, $5.str, $4));
- if (unlikely(ad == NULL))
- MYSQL_YYABORT;
- lex->sql_command= SQLCOM_DROP_INDEX;
- lex->alter_info.reset();
- lex->alter_info.flags= ALTER_DROP_INDEX;
- lex->alter_info.drop_list.push_back(ad, thd->mem_root);
- if (unlikely(!lex->current_select->
- add_table_to_list(thd, $7, NULL, TL_OPTION_UPDATING,
- TL_READ_NO_INSERT,
- MDL_SHARED_UPGRADABLE)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- }
- | DROP DATABASE opt_if_exists ident
- {
- LEX *lex=Lex;
- lex->set_command(SQLCOM_DROP_DB, $3);
- lex->name= $4;
- }
- | DROP PACKAGE_ORACLE_SYM opt_if_exists sp_name
- {
- LEX *lex= Lex;
- lex->set_command(SQLCOM_DROP_PACKAGE, $3);
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE"));
- lex->spname= $4;
- }
- | DROP PACKAGE_ORACLE_SYM BODY_ORACLE_SYM opt_if_exists sp_name
- {
- LEX *lex= Lex;
- lex->set_command(SQLCOM_DROP_PACKAGE_BODY, $4);
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PACKAGE BODY"));
- lex->spname= $5;
- }
- | DROP FUNCTION_SYM opt_if_exists ident '.' ident
- {
- LEX *lex= thd->lex;
- sp_name *spname;
- if (unlikely($4.str && check_db_name((LEX_STRING*) &$4)))
- my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $4.str));
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- lex->set_command(SQLCOM_DROP_FUNCTION, $3);
- spname= new (thd->mem_root) sp_name(&$4, &$6, true);
- if (unlikely(spname == NULL))
- MYSQL_YYABORT;
- lex->spname= spname;
- }
- | DROP FUNCTION_SYM opt_if_exists ident
- {
- LEX *lex= thd->lex;
- LEX_CSTRING db= {0, 0};
- sp_name *spname;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION"));
- if (thd->db.str && unlikely(lex->copy_db_to(&db)))
- MYSQL_YYABORT;
- lex->set_command(SQLCOM_DROP_FUNCTION, $3);
- spname= new (thd->mem_root) sp_name(&db, &$4, false);
- if (unlikely(spname == NULL))
- MYSQL_YYABORT;
- lex->spname= spname;
- }
- | DROP PROCEDURE_SYM opt_if_exists sp_name
- {
- LEX *lex=Lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE"));
- lex->set_command(SQLCOM_DROP_PROCEDURE, $3);
- lex->spname= $4;
- }
- | DROP USER_SYM opt_if_exists clear_privileges user_list
- {
- Lex->set_command(SQLCOM_DROP_USER, $3);
- }
- | DROP ROLE_SYM opt_if_exists clear_privileges role_list
- {
- Lex->set_command(SQLCOM_DROP_ROLE, $3);
- }
- | DROP VIEW_SYM opt_if_exists
- {
- LEX *lex= Lex;
- lex->set_command(SQLCOM_DROP_VIEW, $3);
- YYPS->m_lock_type= TL_UNLOCK;
- YYPS->m_mdl_type= MDL_EXCLUSIVE;
- }
- table_list opt_restrict
- {}
- | DROP EVENT_SYM opt_if_exists sp_name
- {
- Lex->spname= $4;
- Lex->set_command(SQLCOM_DROP_EVENT, $3);
- }
- | DROP TRIGGER_SYM opt_if_exists sp_name
- {
- LEX *lex= Lex;
- lex->set_command(SQLCOM_DROP_TRIGGER, $3);
- lex->spname= $4;
- }
- | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE;
- }
- | DROP LOGFILE_SYM GROUP_SYM logfile_group_name opt_ts_engine opt_ts_wait
- {
- LEX *lex= Lex;
- lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP;
- }
- | DROP SERVER_SYM opt_if_exists ident_or_text
- {
- Lex->set_command(SQLCOM_DROP_SERVER, $3);
- Lex->server_options.reset($4);
- }
- | DROP opt_temporary SEQUENCE_SYM opt_if_exists
-
- {
- LEX *lex= Lex;
- lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4);
- lex->table_type= TABLE_TYPE_SEQUENCE;
- YYPS->m_lock_type= TL_UNLOCK;
- YYPS->m_mdl_type= MDL_EXCLUSIVE;
- }
- table_list
- {}
- ;
-
-table_list:
- table_name
- | table_list ',' table_name
- ;
-
-table_name:
- table_ident
- {
- if (!thd->lex->current_select_or_default()->
- add_table_to_list(thd, $1, NULL,
- TL_OPTION_UPDATING,
- YYPS->m_lock_type,
- YYPS->m_mdl_type))
- MYSQL_YYABORT;
- }
- ;
-
-table_name_with_opt_use_partition:
- table_ident opt_use_partition
- {
- if (unlikely(!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
- ;
-
-table_alias_ref:
- table_ident_opt_wild
- {
- if (unlikely(!Select->
- add_table_to_list(thd, $1, NULL,
- (TL_OPTION_UPDATING |
- TL_OPTION_ALIAS),
- YYPS->m_lock_type,
- YYPS->m_mdl_type)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_if_exists_table_element:
- /* empty */
- {
- Lex->check_exists= FALSE;
- $$= 0;
- }
- | IF_SYM EXISTS
- {
- Lex->check_exists= TRUE;
- $$= 1;
- }
- ;
-
-opt_if_exists:
- /* empty */
- {
- $$.set(DDL_options_st::OPT_NONE);
- }
- | IF_SYM EXISTS
- {
- $$.set(DDL_options_st::OPT_IF_EXISTS);
- }
- ;
-
-opt_temporary:
- /* empty */ { $$= 0; }
- | TEMPORARY { $$= HA_LEX_CREATE_TMP_TABLE; }
- ;
-/*
-** Insert : add new data to table
-*/
-
-insert:
- INSERT
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_INSERT;
- lex->duplicates= DUP_ERROR;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->parsing_place= BEFORE_OPT_LIST;
- }
- insert_lock_option
- opt_ignore insert2
- {
- Select->set_lock_for_tables($3, true);
- Lex->current_select= Lex->first_select_lex();
- }
- insert_field_spec opt_insert_update
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- Lex->mark_first_table_as_inserting();
- }
- ;
-
-replace:
- REPLACE
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_REPLACE;
- lex->duplicates= DUP_REPLACE;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->parsing_place= BEFORE_OPT_LIST;
- }
- replace_lock_option insert2
- {
- Select->set_lock_for_tables($3, true);
- Lex->current_select= Lex->first_select_lex();
- }
- insert_field_spec
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- Lex->mark_first_table_as_inserting();
- }
- ;
-
-insert_lock_option:
- /* empty */
- {
- /*
- If it is SP we do not allow insert optimisation when result of
- insert visible only after the table unlocking but everyone can
- read table.
- */
- $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT);
- }
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
- | DELAYED_SYM
- {
- // QQ: why was +1?
- Lex->keyword_delayed_begin_offset= (uint)($1.pos() - thd->query());
- Lex->keyword_delayed_end_offset= (uint)($1.end() - thd->query());
- $$= TL_WRITE_DELAYED;
- }
- | HIGH_PRIORITY { $$= TL_WRITE; }
- ;
-
-replace_lock_option:
- opt_low_priority { $$= $1; }
- | DELAYED_SYM
- {
- Lex->keyword_delayed_begin_offset= (uint)($1.pos() - thd->query());
- Lex->keyword_delayed_end_offset= (uint)($1.end() - thd->query());
- $$= TL_WRITE_DELAYED;
- }
- ;
-
-insert2:
- INTO insert_table {}
- | insert_table {}
- ;
-
-insert_table:
- {
- Select->save_parsing_place= Select->parsing_place;
- }
- table_name_with_opt_use_partition
- {
- LEX *lex=Lex;
- //lex->field_list.empty();
- lex->many_values.empty();
- lex->insert_list=0;
- }
- ;
-
-insert_field_spec:
- insert_values {}
- | insert_field_list insert_values {}
- | SET
- {
- LEX *lex=Lex;
- if (unlikely(!(lex->insert_list= new (thd->mem_root) List_item)) ||
- unlikely(lex->many_values.push_back(lex->insert_list,
- thd->mem_root)))
- MYSQL_YYABORT;
- lex->current_select->parsing_place= NO_MATTER;
- }
- ident_eq_list
- ;
-
-insert_field_list:
- LEFT_PAREN_ALT opt_fields ')'
- {
- Lex->current_select->parsing_place= AFTER_LIST;
- }
- ;
-
-opt_fields:
- /* empty */
- | fields
- ;
-
-fields:
- fields ',' insert_ident
- { Lex->field_list.push_back($3, thd->mem_root); }
- | insert_ident { Lex->field_list.push_back($1, thd->mem_root); }
- ;
-
-
-
-insert_values:
- create_select_query_expression {}
- ;
-
-values_list:
- values_list ',' no_braces
- | no_braces_with_names
- ;
-
-ident_eq_list:
- ident_eq_list ',' ident_eq_value
- | ident_eq_value
- ;
-
-ident_eq_value:
- simple_ident_nospvar equal expr_or_default
- {
- LEX *lex=Lex;
- if (unlikely(lex->field_list.push_back($1, thd->mem_root)) ||
- unlikely(lex->insert_list->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-equal:
- '=' {}
- | SET_VAR {}
- ;
-
-opt_equal:
- /* empty */ {}
- | equal {}
- ;
-
-opt_with:
- opt_equal {}
- | WITH {}
- ;
-
-opt_by:
- opt_equal {}
- | BY {}
- ;
-
-no_braces:
- '('
- {
- if (unlikely(!(Lex->insert_list= new (thd->mem_root) List_item)))
- MYSQL_YYABORT;
- }
- opt_values ')'
- {
- LEX *lex=Lex;
- if (unlikely(lex->many_values.push_back(lex->insert_list,
- thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-no_braces_with_names:
- '('
- {
- if (unlikely(!(Lex->insert_list= new (thd->mem_root) List_item)))
- MYSQL_YYABORT;
- }
- opt_values_with_names ')'
- {
- LEX *lex=Lex;
- if (unlikely(lex->many_values.push_back(lex->insert_list,
- thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_values:
- /* empty */ {}
- | values
- ;
-
-opt_values_with_names:
- /* empty */ {}
- | values_with_names
- ;
-
-values:
- values ',' expr_or_default
- {
- if (unlikely(Lex->insert_list->push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | expr_or_default
- {
- if (unlikely(Lex->insert_list->push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-values_with_names:
- values_with_names ',' remember_name expr_or_default remember_end
- {
- if (unlikely(Lex->insert_list->push_back($4, thd->mem_root)))
- MYSQL_YYABORT;
- // give some name in case of using in table value constuctor (TVC)
- if (!$4->name.str || $4->name.str == item_empty_name)
- $4->set_name(thd, $3, (uint) ($5 - $3), thd->charset());
- }
- | remember_name expr_or_default remember_end
- {
- if (unlikely(Lex->insert_list->push_back($2, thd->mem_root)))
- MYSQL_YYABORT;
- // give some name in case of using in table value constuctor (TVC)
- if (!$2->name.str || $2->name.str == item_empty_name)
- $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset());
- }
- ;
-
-expr_or_default:
- expr { $$= $1;}
- | DEFAULT
- {
- $$= new (thd->mem_root) Item_default_specification(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | IGNORE_SYM
- {
- $$= new (thd->mem_root) Item_ignore_specification(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_insert_update:
- /* empty */
- | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; }
- KEY_SYM UPDATE_SYM
- {
- Select->parsing_place= IN_UPDATE_ON_DUP_KEY;
- }
- insert_update_list
- {
- Select->parsing_place= NO_MATTER;
- }
- ;
-
-update_table_list:
- table_ident opt_use_partition for_portion_of_time_clause
- opt_table_alias_clause opt_key_definition
- {
- SELECT_LEX *sel= Select;
- sel->table_join_options= 0;
- if (!($$= Select->add_table_to_list(thd, $1, $4,
- Select->get_table_join_options(),
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- Select->pop_index_hints(),
- $2)))
- MYSQL_YYABORT;
- $$->period_conditions= Lex->period_conditions;
- }
- | join_table_list { $$= $1; }
- ;
-
-/* Update rows in a table */
-
-update:
- UPDATE_SYM
- {
- LEX *lex= Lex;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->sql_command= SQLCOM_UPDATE;
- lex->duplicates= DUP_ERROR;
- }
- opt_low_priority opt_ignore update_table_list
- SET update_list
- {
- SELECT_LEX *slex= Lex->first_select_lex();
- if (slex->table_list.elements > 1)
- Lex->sql_command= SQLCOM_UPDATE_MULTI;
- else if (slex->get_table_list()->derived)
- {
- /* it is single table update and it is update of derived table */
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
- slex->get_table_list()->alias.str, "UPDATE");
- MYSQL_YYABORT;
- }
- /*
- In case of multi-update setting write lock for all tables may
- be too pessimistic. We will decrease lock level if possible in
- mysql_multi_update().
- */
- slex->set_lock_for_tables($3, slex->table_list.elements == 1);
- }
- opt_where_clause opt_order_clause delete_limit_clause
- {
- if ($10)
- Select->order_list= *($10);
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- ;
-
-update_list:
- update_list ',' update_elem
- | update_elem
- ;
-
-update_elem:
- simple_ident_nospvar equal expr_or_default
- {
- if (unlikely(add_item_to_list(thd, $1)) ||
- unlikely(add_value_to_list(thd, $3)))
- MYSQL_YYABORT;
- }
- ;
-
-insert_update_list:
- insert_update_list ',' insert_update_elem
- | insert_update_elem
- ;
-
-insert_update_elem:
- simple_ident_nospvar equal expr_or_default
- {
- LEX *lex= Lex;
- if (unlikely(lex->update_list.push_back($1, thd->mem_root)) ||
- unlikely(lex->value_list.push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_low_priority:
- /* empty */ { $$= TL_WRITE_DEFAULT; }
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
- ;
-
-/* Delete rows from a table */
-
-delete:
- DELETE_SYM
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_DELETE;
- YYPS->m_lock_type= TL_WRITE_DEFAULT;
- YYPS->m_mdl_type= MDL_SHARED_WRITE;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->ignore= 0;
- lex->first_select_lex()->order_list.empty();
- }
- delete_part2
- ;
-
-opt_delete_system_time:
- /* empty */
- {
- Lex->vers_conditions.init(SYSTEM_TIME_ALL);
- }
- | BEFORE_SYM SYSTEM_TIME_SYM history_point
- {
- Lex->vers_conditions.init(SYSTEM_TIME_BEFORE, $3);
- }
- ;
-
-delete_part2:
- opt_delete_options single_multi {}
- | HISTORY_SYM delete_single_table opt_delete_system_time
- {
- Lex->last_table()->vers_conditions= Lex->vers_conditions;
- Lex->pop_select(); //main select
- }
- ;
-
-delete_single_table:
- FROM table_ident opt_use_partition
- {
- if (unlikely(!Select->
- add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
- YYPS->m_lock_type,
- YYPS->m_mdl_type,
- NULL,
- $3)))
- MYSQL_YYABORT;
- YYPS->m_lock_type= TL_READ_DEFAULT;
- YYPS->m_mdl_type= MDL_SHARED_READ;
- }
- ;
-
-delete_single_table_for_period:
- delete_single_table opt_for_portion_of_time_clause
- {
- if ($2)
- Lex->last_table()->period_conditions= Lex->period_conditions;
- }
- ;
-
-single_multi:
- delete_single_table_for_period
- opt_where_clause
- opt_order_clause
- delete_limit_clause
- opt_select_expressions
- {
- if ($3)
- Select->order_list= *($3);
- Lex->pop_select(); //main select
- }
- | table_wild_list
- {
- mysql_init_multi_delete(Lex);
- YYPS->m_lock_type= TL_READ_DEFAULT;
- YYPS->m_mdl_type= MDL_SHARED_READ;
- }
- FROM join_table_list opt_where_clause
- {
- if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- | FROM table_alias_ref_list
- {
- mysql_init_multi_delete(Lex);
- YYPS->m_lock_type= TL_READ_DEFAULT;
- YYPS->m_mdl_type= MDL_SHARED_READ;
- }
- USING join_table_list opt_where_clause
- {
- if (unlikely(multi_delete_set_locks_and_link_aux_tables(Lex)))
- MYSQL_YYABORT;
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- ;
-
-opt_select_expressions:
- /* empty */
- | RETURNING_SYM select_item_list
- ;
-
-table_wild_list:
- table_wild_one
- | table_wild_list ',' table_wild_one
- ;
-
-table_wild_one:
- ident opt_wild
- {
- Table_ident *ti= new (thd->mem_root) Table_ident(&$1);
- if (unlikely(ti == NULL))
- MYSQL_YYABORT;
- if (unlikely(!Select->
- add_table_to_list(thd,
- ti,
- NULL,
- (TL_OPTION_UPDATING |
- TL_OPTION_ALIAS),
- YYPS->m_lock_type,
- YYPS->m_mdl_type)))
- MYSQL_YYABORT;
- }
- | ident '.' ident opt_wild
- {
- Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0);
- if (unlikely(ti == NULL))
- MYSQL_YYABORT;
- if (unlikely(!Select->
- add_table_to_list(thd,
- ti,
- NULL,
- (TL_OPTION_UPDATING |
- TL_OPTION_ALIAS),
- YYPS->m_lock_type,
- YYPS->m_mdl_type)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_wild:
- /* empty */ {}
- | '.' '*' {}
- ;
-
-opt_delete_options:
- /* empty */ {}
- | opt_delete_option opt_delete_options {}
- ;
-
-opt_delete_option:
- QUICK { Select->options|= OPTION_QUICK; }
- | LOW_PRIORITY { YYPS->m_lock_type= TL_WRITE_LOW_PRIORITY; }
- | IGNORE_SYM { Lex->ignore= 1; }
- ;
-
-truncate:
- TRUNCATE_SYM
- {
- LEX* lex= Lex;
- lex->sql_command= SQLCOM_TRUNCATE;
- lex->alter_info.reset();
- lex->first_select_lex()->options= 0;
- lex->sql_cache= LEX::SQL_CACHE_UNSPECIFIED;
- lex->first_select_lex()->order_list.empty();
- YYPS->m_lock_type= TL_WRITE;
- YYPS->m_mdl_type= MDL_EXCLUSIVE;
- }
- opt_table_sym table_name opt_lock_wait_timeout
- {
- LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_sql_cmd);
- lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table();
- if (unlikely(lex->m_sql_cmd == NULL))
- MYSQL_YYABORT;
- }
- opt_truncate_table_storage_clause { }
- ;
-
-opt_truncate_table_storage_clause:
- /* Empty */
- | DROP STORAGE_SYM
- | REUSE_SYM STORAGE_SYM
- ;
-
-opt_table_sym:
- /* empty */
- | TABLE_SYM
- ;
-
-opt_profile_defs:
- /* empty */
- | profile_defs;
-
-profile_defs:
- profile_def
- | profile_defs ',' profile_def;
-
-profile_def:
- CPU_SYM
- {
- Lex->profile_options|= PROFILE_CPU;
- }
- | MEMORY_SYM
- {
- Lex->profile_options|= PROFILE_MEMORY;
- }
- | BLOCK_SYM IO_SYM
- {
- Lex->profile_options|= PROFILE_BLOCK_IO;
- }
- | CONTEXT_SYM SWITCHES_SYM
- {
- Lex->profile_options|= PROFILE_CONTEXT;
- }
- | PAGE_SYM FAULTS_SYM
- {
- Lex->profile_options|= PROFILE_PAGE_FAULTS;
- }
- | IPC_SYM
- {
- Lex->profile_options|= PROFILE_IPC;
- }
- | SWAPS_SYM
- {
- Lex->profile_options|= PROFILE_SWAPS;
- }
- | SOURCE_SYM
- {
- Lex->profile_options|= PROFILE_SOURCE;
- }
- | ALL
- {
- Lex->profile_options|= PROFILE_ALL;
- }
- ;
-
-opt_profile_args:
- /* empty */
- {
- Lex->profile_query_id= 0;
- }
- | FOR_SYM QUERY_SYM NUM
- {
- Lex->profile_query_id= atoi($3.str);
- }
- ;
-
-/* Show things */
-
-show:
- SHOW
- {
- LEX *lex=Lex;
- lex->wild=0;
- lex->ident= null_clex_str;
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
- lex->create_info.init();
- }
- show_param
- {
- Select->parsing_place= NO_MATTER;
- Lex->pop_select(); //main select
- }
- ;
-
-show_param:
- DATABASES wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_DATABASES;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_SCHEMATA)))
- MYSQL_YYABORT;
- }
- | opt_full TABLES opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_TABLES;
- lex->first_select_lex()->db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES))
- MYSQL_YYABORT;
- }
- | opt_full TRIGGERS_SYM opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_TRIGGERS;
- lex->first_select_lex()->db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS))
- MYSQL_YYABORT;
- }
- | EVENTS_SYM opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_EVENTS;
- lex->first_select_lex()->db= $2;
- if (prepare_schema_table(thd, lex, 0, SCH_EVENTS))
- MYSQL_YYABORT;
- }
- | TABLE_SYM STATUS_SYM opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_TABLE_STATUS;
- lex->first_select_lex()->db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_TABLES))
- MYSQL_YYABORT;
- }
- | OPEN_SYM TABLES opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_OPEN_TABLES;
- lex->first_select_lex()->db= $3;
- if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES))
- MYSQL_YYABORT;
- }
- | PLUGINS_SYM
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_PLUGINS;
- if (unlikely(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 (unlikely(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 (unlikely(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
- { Lex->create_info.db_type= NULL; }
- | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_FIELDS;
- if ($5.str)
- $4->change_db(&$5);
- if (unlikely(prepare_schema_table(thd, lex, $4, SCH_COLUMNS)))
- MYSQL_YYABORT;
- }
- | master_or_binary LOGS_SYM
- {
- Lex->sql_command = SQLCOM_SHOW_BINLOGS;
- }
- | SLAVE HOSTS_SYM
- {
- Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS;
- }
- | BINLOG_SYM EVENTS_SYM binlog_in binlog_from
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
- }
- opt_global_limit_clause
- | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
- }
- opt_global_limit_clause
- | keys_or_index from_or_in table_ident opt_db opt_where_clause
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_KEYS;
- if ($4.str)
- $3->change_db(&$4);
- if (unlikely(prepare_schema_table(thd, lex, $3, SCH_STATISTICS)))
- MYSQL_YYABORT;
- }
- | opt_storage ENGINES_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_ENGINES)))
- MYSQL_YYABORT;
- }
- | AUTHORS_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_AUTHORS;
- }
- | CONTRIBUTORS_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS;
- }
- | PRIVILEGES
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_PRIVILEGES;
- }
- | COUNT_SYM '(' '*' ')' WARNINGS
- {
- LEX_CSTRING var= {STRING_WITH_LEN("warning_count")};
- (void) create_select_for_variable(thd, &var);
- }
- | COUNT_SYM '(' '*' ')' ERRORS
- {
- LEX_CSTRING var= {STRING_WITH_LEN("error_count")};
- (void) create_select_for_variable(thd, &var);
- }
- | WARNINGS opt_global_limit_clause
- { Lex->sql_command = SQLCOM_SHOW_WARNS;}
- | ERRORS opt_global_limit_clause
- { Lex->sql_command = SQLCOM_SHOW_ERRORS;}
- | PROFILES_SYM
- { Lex->sql_command = SQLCOM_SHOW_PROFILES; }
- | PROFILE_SYM opt_profile_defs opt_profile_args opt_global_limit_clause
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_PROFILE;
- if (unlikely(prepare_schema_table(thd, lex, NULL, SCH_PROFILES)))
- MYSQL_YYABORT;
- }
- | opt_var_type STATUS_SYM wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_STATUS;
- lex->option_type= $1;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_SESSION_STATUS)))
- MYSQL_YYABORT;
- }
- | opt_full PROCESSLIST_SYM
- { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;}
- | opt_var_type VARIABLES wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_VARIABLES;
- lex->option_type= $1;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_SESSION_VARIABLES)))
- MYSQL_YYABORT;
- }
- | charset wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_CHARSETS;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_CHARSETS)))
- MYSQL_YYABORT;
- }
- | COLLATION_SYM wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_COLLATIONS;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_COLLATIONS)))
- MYSQL_YYABORT;
- }
- | GRANTS
- {
- Lex->sql_command= SQLCOM_SHOW_GRANTS;
- if (unlikely(!(Lex->grant_user=
- (LEX_USER*)thd->alloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- Lex->grant_user->user= current_user_and_current_role;
- }
- | GRANTS FOR_SYM user_or_role clear_privileges
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_GRANTS;
- lex->grant_user=$3;
- }
- | CREATE DATABASE opt_if_not_exists ident
- {
- Lex->set_command(SQLCOM_SHOW_CREATE_DB, $3);
- Lex->name= $4;
- }
- | CREATE TABLE_SYM table_ident
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL,0))
- MYSQL_YYABORT;
- lex->create_info.storage_media= HA_SM_DEFAULT;
- }
- | CREATE VIEW_SYM table_ident
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0))
- MYSQL_YYABORT;
- lex->table_type= TABLE_TYPE_VIEW;
- }
- | CREATE SEQUENCE_SYM table_ident
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!lex->first_select_lex()->add_table_to_list(thd, $3, NULL, 0))
- MYSQL_YYABORT;
- lex->table_type= TABLE_TYPE_SEQUENCE;
- }
- | MASTER_SYM STATUS_SYM
- {
- 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= null_clex_str;
- 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;
- }
- | CREATE PROCEDURE_SYM sp_name
- {
- LEX *lex= Lex;
-
- lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
- lex->spname= $3;
- }
- | CREATE FUNCTION_SYM sp_name
- {
- LEX *lex= Lex;
-
- lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
- lex->spname= $3;
- }
- | CREATE PACKAGE_ORACLE_SYM sp_name
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE;
- lex->spname= $3;
- }
- | CREATE PACKAGE_ORACLE_SYM BODY_ORACLE_SYM sp_name
- {
- LEX *lex= Lex;
- lex->sql_command = SQLCOM_SHOW_CREATE_PACKAGE_BODY;
- lex->spname= $4;
- }
- | CREATE TRIGGER_SYM sp_name
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER;
- lex->spname= $3;
- }
- | CREATE USER_SYM
- {
- Lex->sql_command= SQLCOM_SHOW_CREATE_USER;
- if (unlikely(!(Lex->grant_user=
- (LEX_USER*)thd->alloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- Lex->grant_user->user= current_user;
- }
- | CREATE USER_SYM user
- {
- Lex->sql_command= SQLCOM_SHOW_CREATE_USER;
- Lex->grant_user= $3;
- }
- | PROCEDURE_SYM STATUS_SYM wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_STATUS_PROC;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
- MYSQL_YYABORT;
- }
- | FUNCTION_SYM STATUS_SYM wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_STATUS_FUNC;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
- MYSQL_YYABORT;
- }
- | PACKAGE_ORACLE_SYM STATUS_SYM wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
- MYSQL_YYABORT;
- }
- | PACKAGE_ORACLE_SYM BODY_ORACLE_SYM STATUS_SYM wild_and_where
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_SHOW_STATUS_PACKAGE_BODY;
- if (unlikely(prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)))
- MYSQL_YYABORT;
- }
- | PROCEDURE_SYM CODE_SYM sp_name
- {
- Lex->sql_command= SQLCOM_SHOW_PROC_CODE;
- Lex->spname= $3;
- }
- | FUNCTION_SYM CODE_SYM sp_name
- {
- Lex->sql_command= SQLCOM_SHOW_FUNC_CODE;
- Lex->spname= $3;
- }
- | PACKAGE_ORACLE_SYM BODY_ORACLE_SYM CODE_SYM sp_name
- {
- Lex->sql_command= SQLCOM_SHOW_PACKAGE_BODY_CODE;
- Lex->spname= $4;
- }
- | CREATE EVENT_SYM sp_name
- {
- Lex->spname= $3;
- Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
- }
- | describe_command FOR_SYM expr
- {
- Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
- if (unlikely(prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN)))
- MYSQL_YYABORT;
- add_value_to_list(thd, $3);
- }
- | IDENT_sys remember_tok_start wild_and_where
- {
- LEX *lex= Lex;
- bool in_plugin;
- lex->sql_command= SQLCOM_SHOW_GENERIC;
- ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1, &in_plugin);
- if (unlikely(!table || !table->old_format || !in_plugin))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $2);
- MYSQL_YYABORT;
- }
- if (unlikely(lex->wild && table->idx_field1 < 0))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $3);
- MYSQL_YYABORT;
- }
- if (unlikely(make_schema_select(thd, Lex->current_select, table)))
- MYSQL_YYABORT;
- }
- ;
-
-show_engine_param:
- STATUS_SYM
- { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; }
- | MUTEX_SYM
- { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; }
- | LOGS_SYM
- { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; }
- ;
-
-master_or_binary:
- MASTER_SYM
- | BINARY
- ;
-
-opt_storage:
- /* empty */
- | STORAGE_SYM
- ;
-
-opt_db:
- /* empty */ { $$= null_clex_str; }
- | from_or_in ident { $$= $2; }
- ;
-
-opt_full:
- /* empty */ { Lex->verbose=0; }
- | FULL { Lex->verbose=1; }
- ;
-
-from_or_in:
- FROM
- | IN_SYM
- ;
-
-binlog_in:
- /* empty */ { Lex->mi.log_file_name = 0; }
- | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; }
- ;
-
-binlog_from:
- /* empty */ { Lex->mi.pos = 4; /* skip magic number */ }
- | FROM ulonglong_num { Lex->mi.pos = $2; }
- ;
-
-wild_and_where:
- /* empty */ { $$= 0; }
- | LIKE remember_tok_start TEXT_STRING_sys
- {
- Lex->wild= new (thd->mem_root) String($3.str, $3.length,
- system_charset_info);
- if (unlikely(Lex->wild == NULL))
- MYSQL_YYABORT;
- $$= $2;
- }
- | WHERE remember_tok_start expr
- {
- Select->where= normalize_cond(thd, $3);
- if ($3)
- $3->top_level_item();
- $$= $2;
- }
- ;
-
-/* A Oracle compatible synonym for show */
-describe:
- describe_command table_ident
- {
- LEX *lex= Lex;
- if (lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- lex->current_select->parsing_place= SELECT_LIST;
- lex->sql_command= SQLCOM_SHOW_FIELDS;
- lex->first_select_lex()->db= null_clex_str;
- lex->verbose= 0;
- if (unlikely(prepare_schema_table(thd, lex, $2, SCH_COLUMNS)))
- MYSQL_YYABORT;
- }
- opt_describe_column
- {
- Select->parsing_place= NO_MATTER;
- Lex->pop_select(); //main select
- }
- | describe_command opt_extended_describe
- { Lex->describe|= DESCRIBE_NORMAL; }
- explainable_command
- {
- LEX *lex=Lex;
- lex->first_select_lex()->options|= SELECT_DESCRIBE;
- }
- ;
-
-explainable_command:
- select
- | select_into
- | insert
- | replace
- | update
- | delete
- ;
-
-describe_command:
- DESC
- | DESCRIBE
- ;
-
-analyze_stmt_command:
- ANALYZE_SYM opt_format_json explainable_command
- {
- Lex->analyze_stmt= true;
- }
- ;
-
-opt_extended_describe:
- EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; }
- | EXTENDED_SYM ALL
- { Lex->describe|= DESCRIBE_EXTENDED | DESCRIBE_EXTENDED2; }
- | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; }
- | opt_format_json {}
- ;
-
-opt_format_json:
- /* empty */ {}
- | FORMAT_SYM '=' ident_or_text
- {
- if (lex_string_eq(&$3, STRING_WITH_LEN("JSON")))
- Lex->explain_json= true;
- else if (lex_string_eq(&$3, STRING_WITH_LEN("TRADITIONAL")))
- DBUG_ASSERT(Lex->explain_json==false);
- else
- my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), "EXPLAIN",
- $3.str));
- }
- ;
-
-opt_describe_column:
- /* empty */ {}
- | text_string { Lex->wild= $1; }
- | ident
- {
- Lex->wild= new (thd->mem_root) String((const char*) $1.str,
- $1.length,
- system_charset_info);
- if (unlikely(Lex->wild == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-
-/* flush things */
-
-flush:
- FLUSH_SYM opt_no_write_to_binlog
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_FLUSH;
- lex->type= 0;
- lex->no_write_to_binlog= $2;
- }
- flush_options {}
- ;
-
-flush_options:
- table_or_tables
- {
- Lex->type|= REFRESH_TABLES;
- /*
- Set type of metadata and table locks for
- FLUSH TABLES table_list [WITH READ LOCK].
- */
- YYPS->m_lock_type= TL_READ_NO_INSERT;
- YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO;
- }
- opt_table_list opt_flush_lock
- {}
- | flush_options_list
- {}
- ;
-
-opt_flush_lock:
- /* empty */ {}
- | flush_lock
- {
- TABLE_LIST *tables= Lex->query_tables;
- for (; tables; tables= tables->next_global)
- {
- tables->mdl_request.set_type(MDL_SHARED_NO_WRITE);
- /* Don't try to flush views. */
- tables->required_type= TABLE_TYPE_NORMAL;
- /* Ignore temporary tables. */
- tables->open_type= OT_BASE_ONLY;
- }
- }
- ;
-
-flush_lock:
- WITH READ_SYM LOCK_SYM optional_flush_tables_arguments
- { Lex->type|= REFRESH_READ_LOCK | $4; }
- | FOR_SYM
- {
- if (unlikely(Lex->query_tables == NULL))
- {
- // Table list can't be empty
- thd->parse_error(ER_NO_TABLES_USED);
- MYSQL_YYABORT;
- }
- Lex->type|= REFRESH_FOR_EXPORT;
- } EXPORT_SYM {}
- ;
-
-flush_options_list:
- flush_options_list ',' flush_option
- | flush_option
- {}
- ;
-
-flush_option:
- ERROR_SYM LOGS_SYM
- { Lex->type|= REFRESH_ERROR_LOG; }
- | ENGINE_SYM LOGS_SYM
- { Lex->type|= REFRESH_ENGINE_LOG; }
- | GENERAL LOGS_SYM
- { Lex->type|= REFRESH_GENERAL_LOG; }
- | SLOW LOGS_SYM
- { Lex->type|= REFRESH_SLOW_LOG; }
- | BINARY LOGS_SYM opt_delete_gtid_domain
- { Lex->type|= REFRESH_BINARY_LOG; }
- | RELAY LOGS_SYM optional_connection_name
- {
- LEX *lex= Lex;
- if (unlikely(lex->type & REFRESH_RELAY_LOG))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "FLUSH", "RELAY LOGS"));
- 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
- { Lex->type|= REFRESH_HOSTS; }
- | PRIVILEGES
- { Lex->type|= REFRESH_GRANT; }
- | LOGS_SYM
- {
- Lex->type|= REFRESH_LOG;
- Lex->relay_log_connection_name= empty_clex_str;
- }
- | STATUS_SYM
- { Lex->type|= REFRESH_STATUS; }
- | SLAVE optional_connection_name
- {
- LEX *lex= Lex;
- if (unlikely(lex->type & REFRESH_SLAVE))
- my_yyabort_error((ER_WRONG_USAGE, MYF(0), "FLUSH","SLAVE"));
- lex->type|= REFRESH_SLAVE;
- lex->reset_slave_info.all= false;
- }
- | MASTER_SYM
- { Lex->type|= REFRESH_MASTER; }
- | DES_KEY_FILE
- { Lex->type|= REFRESH_DES_KEY_FILE; }
- | RESOURCES
- { Lex->type|= REFRESH_USER_RESOURCES; }
- | SSL_SYM
- { Lex->type|= REFRESH_SSL;}
- | IDENT_sys remember_tok_start
- {
- Lex->type|= REFRESH_GENERIC;
- ST_SCHEMA_TABLE *table= find_schema_table(thd, &$1);
- if (unlikely(!table || !table->reset_table))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $2);
- MYSQL_YYABORT;
- }
- if (unlikely(Lex->view_list.push_back((LEX_CSTRING*)
- thd->memdup(&$1, sizeof(LEX_CSTRING)),
- thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-opt_table_list:
- /* empty */ {}
- | table_list {}
- ;
-
-backup:
- BACKUP_SYM backup_statements {}
- ;
-
-backup_statements:
- STAGE_SYM ident
- {
- int type;
- if (unlikely(Lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "BACKUP STAGE"));
- if ((type= find_type($2.str, &backup_stage_names,
- FIND_TYPE_NO_PREFIX)) <= 0)
- my_yyabort_error((ER_BACKUP_UNKNOWN_STAGE, MYF(0), $2.str));
- Lex->sql_command= SQLCOM_BACKUP;
- Lex->backup_stage= (backup_stages) (type-1);
- break;
- }
- | LOCK_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- table_ident
- {
- if (unlikely(!Select->add_table_to_list(thd, $3, NULL, 0,
- TL_READ, MDL_SHARED_HIGH_PRIO)))
- MYSQL_YYABORT;
- Lex->sql_command= SQLCOM_BACKUP_LOCK;
- Lex->pop_select(); //main select
- }
- | UNLOCK_SYM
- {
- /* Table list is empty for unlock */
- Lex->sql_command= SQLCOM_BACKUP_LOCK;
- }
- ;
-
-opt_delete_gtid_domain:
- /* empty */ {}
- | DELETE_DOMAIN_ID_SYM '=' '(' delete_domain_id_list ')'
- {}
- ;
-delete_domain_id_list:
- /* Empty */
- | delete_domain_id
- | delete_domain_id_list ',' delete_domain_id
- ;
-
-delete_domain_id:
- ulonglong_num
- {
- uint32 value= (uint32) $1;
- if ($1 > UINT_MAX32)
- {
- my_printf_error(ER_BINLOG_CANT_DELETE_GTID_DOMAIN,
- "The value of gtid domain being deleted ('%llu') "
- "exceeds its maximum size "
- "of 32 bit unsigned integer", MYF(0), $1);
- MYSQL_YYABORT;
- }
- insert_dynamic(&Lex->delete_gtid_domain, (uchar*) &value);
- }
- ;
-
-optional_flush_tables_arguments:
- /* empty */ {$$= 0;}
- | AND_SYM DISABLE_SYM CHECKPOINT_SYM {$$= REFRESH_CHECKPOINT; }
- ;
-
-reset:
- RESET_SYM
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_RESET; lex->type=0;
- }
- reset_options
- {}
- ;
-
-reset_options:
- reset_options ',' reset_option
- | reset_option
- ;
-
-reset_option:
- SLAVE { Lex->type|= REFRESH_SLAVE; }
- optional_connection_name
- slave_reset_options { }
- | MASTER_SYM
- {
- Lex->type|= REFRESH_MASTER;
- Lex->next_binlog_file_number= 0;
- }
- master_reset_options
- | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}
- ;
-
-slave_reset_options:
- /* empty */ { Lex->reset_slave_info.all= false; }
- | ALL { Lex->reset_slave_info.all= true; }
- ;
-
-master_reset_options:
- /* empty */ {}
- | TO_SYM ulong_num
- {
- Lex->next_binlog_file_number = $2;
- }
- ;
-
-purge:
- PURGE master_or_binary LOGS_SYM TO_SYM TEXT_STRING_sys
- {
- Lex->stmt_purge_to($5);
- }
- | PURGE master_or_binary LOGS_SYM BEFORE_SYM
- { Lex->clause_that_disallows_subselect= "PURGE..BEFORE"; }
- expr
- {
- Lex->clause_that_disallows_subselect= NULL;
- if (Lex->stmt_purge_before($6))
- MYSQL_YYABORT;
- }
- ;
-
-
-/* kill threads */
-
-kill:
- KILL_SYM
- {
- LEX *lex=Lex;
- 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
- {
- Lex->kill_signal= (killed_state) ($3 | $4);
- }
- ;
-
-kill_type:
- /* Empty */ { $$= (int) KILL_HARD_BIT; }
- | HARD_SYM { $$= (int) KILL_HARD_BIT; }
- | SOFT_SYM { $$= 0; }
- ;
-
-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($$, thd->mem_root);
- }
- | USER_SYM user
- {
- Lex->users_list.push_back($2, thd->mem_root);
- Lex->kill_type= KILL_TYPE_USER;
- }
- ;
-
-
-shutdown:
- SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; }
- shutdown_option {}
- ;
-
-shutdown_option:
- /* Empty */ { Lex->is_shutdown_wait_for_slaves= false; }
- | WAIT_SYM FOR_SYM ALL SLAVES
- {
- Lex->is_shutdown_wait_for_slaves= true;
- }
- ;
-/* change database */
-
-use:
- USE_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command=SQLCOM_CHANGE_DB;
- lex->first_select_lex()->db= $2;
- }
- ;
-
-/* import, export of files */
-
-load:
- LOAD data_or_xml
- {
- LEX *lex= thd->lex;
-
- if (unlikely(lex->sphead))
- {
- my_error(ER_SP_BADSTATEMENT, MYF(0),
- $2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML");
- MYSQL_YYABORT;
- }
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- mysql_init_select(lex);
- }
- load_data_lock opt_local INFILE TEXT_STRING_filesystem
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_LOAD;
- lex->local_file= $5;
- lex->duplicates= DUP_ERROR;
- lex->ignore= 0;
- if (unlikely(!(lex->exchange= new (thd->mem_root)
- sql_exchange($7.str, 0, $2))))
- MYSQL_YYABORT;
- }
- opt_duplicate INTO TABLE_SYM table_ident opt_use_partition
- {
- LEX *lex=Lex;
- if (unlikely(!Select->add_table_to_list(thd, $12, NULL,
- TL_OPTION_UPDATING,
- $4, MDL_SHARED_WRITE,
- NULL, $13)))
- MYSQL_YYABORT;
- lex->field_list.empty();
- lex->update_list.empty();
- lex->value_list.empty();
- lex->many_values.empty();
- }
- opt_load_data_charset
- { 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
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- Lex->mark_first_table_as_inserting();
- }
- ;
-
-data_or_xml:
- DATA_SYM { $$= FILETYPE_CSV; }
- | XML_SYM { $$= FILETYPE_XML; }
- ;
-
-opt_local:
- /* empty */ { $$=0;}
- | LOCAL_SYM { $$=1;}
- ;
-
-load_data_lock:
- /* empty */ { $$= TL_WRITE_DEFAULT; }
- | CONCURRENT
- {
- /*
- Ignore this option in SP to avoid problem with query cache and
- triggers with non default priority locks
- */
- $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT);
- }
- | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }
- ;
-
-opt_duplicate:
- /* empty */ { Lex->duplicates=DUP_ERROR; }
- | REPLACE { Lex->duplicates=DUP_REPLACE; }
- | IGNORE_SYM { Lex->ignore= 1; }
- ;
-
-opt_field_term:
- /* empty */
- | COLUMNS field_term_list
- ;
-
-field_term_list:
- field_term_list field_term
- | field_term
- ;
-
-field_term:
- TERMINATED BY text_string
- {
- DBUG_ASSERT(Lex->exchange != 0);
- Lex->exchange->field_term= $3;
- }
- | OPTIONALLY ENCLOSED BY text_string
- {
- LEX *lex= Lex;
- DBUG_ASSERT(lex->exchange != 0);
- lex->exchange->enclosed= $4;
- lex->exchange->opt_enclosed= 1;
- }
- | ENCLOSED BY text_string
- {
- DBUG_ASSERT(Lex->exchange != 0);
- Lex->exchange->enclosed= $3;
- }
- | ESCAPED BY text_string
- {
- DBUG_ASSERT(Lex->exchange != 0);
- Lex->exchange->escaped= $3;
- }
- ;
-
-opt_line_term:
- /* empty */
- | LINES line_term_list
- ;
-
-line_term_list:
- line_term_list line_term
- | line_term
- ;
-
-line_term:
- TERMINATED BY text_string
- {
- DBUG_ASSERT(Lex->exchange != 0);
- Lex->exchange->line_term= $3;
- }
- | STARTING BY text_string
- {
- DBUG_ASSERT(Lex->exchange != 0);
- Lex->exchange->line_start= $3;
- }
- ;
-
-opt_xml_rows_identified_by:
- /* empty */ { }
- | ROWS_SYM IDENTIFIED_SYM BY text_string
- { Lex->exchange->line_term = $4; }
- ;
-
-opt_ignore_lines:
- /* empty */
- | IGNORE_SYM NUM lines_or_rows
- {
- DBUG_ASSERT(Lex->exchange != 0);
- Lex->exchange->skip_lines= atol($2.str);
- }
- ;
-
-lines_or_rows:
- LINES { }
- | ROWS_SYM { }
- ;
-
-opt_field_or_var_spec:
- /* empty */ {}
- | '(' fields_or_vars ')' {}
- | '(' ')' {}
- ;
-
-fields_or_vars:
- fields_or_vars ',' field_or_var
- { Lex->field_list.push_back($3, thd->mem_root); }
- | field_or_var
- { Lex->field_list.push_back($1, thd->mem_root); }
- ;
-
-field_or_var:
- simple_ident_nospvar {$$= $1;}
- | '@' ident_or_text
- {
- $$= new (thd->mem_root) Item_user_var_as_out_param(thd, &$2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-opt_load_data_set_spec:
- /* empty */ {}
- | SET load_data_set_list {}
- ;
-
-load_data_set_list:
- load_data_set_list ',' load_data_set_elem
- | load_data_set_elem
- ;
-
-load_data_set_elem:
- simple_ident_nospvar equal remember_name expr_or_default remember_end
- {
- LEX *lex= Lex;
- if (unlikely(lex->update_list.push_back($1, thd->mem_root)) ||
- unlikely(lex->value_list.push_back($4, thd->mem_root)))
- MYSQL_YYABORT;
- $4->set_name_no_truncate(thd, $3, (uint) ($5 - $3), thd->charset());
- }
- ;
-
-/* Common definitions */
-
-text_literal:
- TEXT_STRING
- {
- if (unlikely(!($$= thd->make_string_literal($1))))
- MYSQL_YYABORT;
- }
- | NCHAR_STRING
- {
- if (unlikely(!($$= thd->make_string_literal_nchar($1))))
- MYSQL_YYABORT;
- }
- | UNDERSCORE_CHARSET TEXT_STRING
- {
- if (unlikely(!($$= thd->make_string_literal_charset($2, $1))))
- MYSQL_YYABORT;
- }
- | text_literal TEXT_STRING_literal
- {
- if (unlikely(!($$= $1->make_string_literal_concat(thd, &$2))))
- MYSQL_YYABORT;
- }
- ;
-
-text_string:
- TEXT_STRING_literal
- {
- $$= new (thd->mem_root) String($1.str,
- $1.length,
- thd->variables.collation_connection);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | hex_or_bin_String { $$= $1; }
- ;
-
-
-hex_or_bin_String:
- HEX_NUM
- {
- Item *tmp= new (thd->mem_root) Item_hex_hybrid(thd, $1.str,
- $1.length);
- if (unlikely(tmp == NULL))
- MYSQL_YYABORT;
- $$= tmp->val_str((String*) 0);
- }
- | HEX_STRING
- {
- Item *tmp= new (thd->mem_root) Item_hex_string(thd, $1.str,
- $1.length);
- if (unlikely(tmp == NULL))
- MYSQL_YYABORT;
- $$= tmp->val_str((String*) 0);
- }
- | BIN_NUM
- {
- Item *tmp= new (thd->mem_root) Item_bin_string(thd, $1.str,
- $1.length);
- if (unlikely(tmp == NULL))
- MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fields, because we need only
- value of constant
- */
- $$= tmp->val_str((String*) 0);
- }
- ;
-
-param_marker:
- PARAM_MARKER
- {
- if (unlikely(!($$= Lex->add_placeholder(thd, &param_clex_str,
- YYLIP->get_tok_start(),
- YYLIP->get_tok_start() + 1))))
- MYSQL_YYABORT;
- }
- | COLON_ORACLE_SYM ident_cli
- {
- if (unlikely(!($$= Lex->add_placeholder(thd, &null_clex_str,
- $1.pos(), $2.end()))))
- MYSQL_YYABORT;
- }
- | COLON_ORACLE_SYM NUM
- {
- if (unlikely(!($$= Lex->add_placeholder(thd, &null_clex_str,
- $1.pos(),
- YYLIP->get_ptr()))))
- MYSQL_YYABORT;
- }
- ;
-
-signed_literal:
- '+' NUM_literal { $$ = $2; }
- | '-' NUM_literal
- {
- $2->max_length++;
- $$= $2->neg(thd);
- }
- ;
-
-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(thd);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- YYLIP->next_state= MY_LEX_OPERATOR_OR_IDENT;
- }
- | FALSE_SYM
- {
- $$= new (thd->mem_root) Item_bool(thd, (char*) "FALSE",0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | TRUE_SYM
- {
- $$= new (thd->mem_root) Item_bool(thd, (char*) "TRUE",1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | HEX_NUM
- {
- $$= new (thd->mem_root) Item_hex_hybrid(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | HEX_STRING
- {
- $$= new (thd->mem_root) Item_hex_string(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | BIN_NUM
- {
- $$= new (thd->mem_root) Item_bin_string(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | UNDERSCORE_CHARSET hex_or_bin_String
- {
- Item_string_with_introducer *item_str;
- /*
- Pass NULL as name. Name will be set in the "select_item" rule and
- will include the introducer and the original hex/bin notation.
- */
- item_str= new (thd->mem_root)
- Item_string_with_introducer(thd, NULL, $2->ptr(), $2->length(),
- $1);
- if (unlikely(!item_str ||
- !item_str->check_well_formed_result(true)))
- MYSQL_YYABORT;
-
- $$= item_str;
- }
- ;
-
-NUM_literal:
- NUM
- {
- int error;
- $$= new (thd->mem_root)
- Item_int(thd, $1.str,
- (longlong) my_strtoll10($1.str, NULL, &error),
- $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | LONG_NUM
- {
- int error;
- $$= new (thd->mem_root)
- Item_int(thd, $1.str,
- (longlong) my_strtoll10($1.str, NULL, &error),
- $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ULONGLONG_NUM
- {
- $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | DECIMAL_NUM
- {
- $$= new (thd->mem_root) Item_decimal(thd, $1.str, $1.length,
- thd->charset());
- if (unlikely($$ == NULL) || unlikely(thd->is_error()))
- MYSQL_YYABORT;
- }
- | FLOAT_NUM
- {
- $$= new (thd->mem_root) Item_float(thd, $1.str, $1.length);
- if (unlikely($$ == NULL) || unlikely(thd->is_error()))
- MYSQL_YYABORT;
- }
- ;
-
-
-temporal_literal:
- DATE_SYM TEXT_STRING
- {
- if (unlikely(!($$= type_handler_newdate.create_literal_item(thd,
- $2.str, $2.length,
- YYCSCL, true))))
- MYSQL_YYABORT;
- }
- | TIME_SYM TEXT_STRING
- {
- if (unlikely(!($$= type_handler_time2.create_literal_item(thd,
- $2.str, $2.length,
- YYCSCL, true))))
- MYSQL_YYABORT;
- }
- | TIMESTAMP TEXT_STRING
- {
- if (unlikely(!($$= type_handler_datetime2.create_literal_item(thd,
- $2.str, $2.length,
- YYCSCL, true))))
- MYSQL_YYABORT;
- }
- ;
-
-with_clause:
- WITH opt_recursive
- {
- LEX *lex= Lex;
- With_clause *with_clause=
- new With_clause($2, Lex->curr_with_clause);
- if (unlikely(with_clause == NULL))
- MYSQL_YYABORT;
- lex->derived_tables|= DERIVED_WITH;
- lex->curr_with_clause= with_clause;
- with_clause->add_to_list(Lex->with_clauses_list_last_next);
- if (lex->current_select &&
- lex->current_select->parsing_place == BEFORE_OPT_LIST)
- lex->current_select->parsing_place= NO_MATTER;
- }
- with_list
- {
- $$= Lex->curr_with_clause;
- Lex->curr_with_clause= Lex->curr_with_clause->pop();
- }
- ;
-
-
-opt_recursive:
- /*empty*/ { $$= 0; }
- | RECURSIVE_SYM { $$= 1; }
- ;
-
-
-with_list:
- with_list_element
- | with_list ',' with_list_element
- ;
-
-
-with_list_element:
- query_name
- opt_with_column_list
- {
- $2= new List<LEX_CSTRING> (Lex->with_column_list);
- if (unlikely($2 == NULL))
- MYSQL_YYABORT;
- Lex->with_column_list.empty();
- }
- AS '(' query_expression ')'
- {
- LEX *lex= thd->lex;
- const char *query_start= lex->sphead ? lex->sphead->m_tmp_query
- : thd->query();
- const char *spec_start= $5.pos() + 1;
- With_element *elem= new With_element($1, *$2, $6);
- if (elem == NULL || Lex->curr_with_clause->add_with_element(elem))
- MYSQL_YYABORT;
- if (elem->set_unparsed_spec(thd, spec_start, $7.pos(),
- spec_start - query_start))
- MYSQL_YYABORT;
- }
- ;
-
-
-opt_with_column_list:
- /* empty */
- { $$= NULL; }
- | '(' with_column_list ')'
- { $$= NULL; }
- ;
-
-
-with_column_list:
- ident
- {
- Lex->with_column_list.push_back((LEX_CSTRING*)
- thd->memdup(&$1, sizeof(LEX_CSTRING)));
- }
- | with_column_list ',' ident
- {
- Lex->with_column_list.push_back((LEX_CSTRING*)
- thd->memdup(&$3, sizeof(LEX_CSTRING)));
- }
- ;
-
-
-query_name:
- ident
- {
- $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING));
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-
-
-/**********************************************************************
-** Creating different items.
-**********************************************************************/
-
-insert_ident:
- simple_ident_nospvar { $$=$1; }
- | table_wild { $$=$1; }
- ;
-
-table_wild:
- ident '.' '*'
- {
- if (unlikely(!($$= Lex->create_item_qualified_asterisk(thd, &$1))))
- MYSQL_YYABORT;
- }
- | ident '.' ident '.' '*'
- {
- if (unlikely(!($$= Lex->create_item_qualified_asterisk(thd, &$1, &$3))))
- MYSQL_YYABORT;
- }
- ;
-
-select_sublist_qualified_asterisk:
- ident_cli '.' '*'
- {
- if (unlikely(!($$= Lex->create_item_qualified_asterisk(thd, &$1))))
- MYSQL_YYABORT;
- }
- | ident_cli '.' ident_cli '.' '*'
- {
- if (unlikely(!($$= Lex->create_item_qualified_asterisk(thd, &$1, &$3))))
- MYSQL_YYABORT;
- }
- ;
-
-order_ident:
- expr { $$=$1; }
- ;
-
-
-simple_ident:
- ident_cli
- {
- if (unlikely(!($$= Lex->create_item_ident(thd, &$1))))
- MYSQL_YYABORT;
- }
- | ident_cli '.' ident_cli
- {
- if (unlikely(!($$= Lex->create_item_ident(thd, &$1, &$3))))
- MYSQL_YYABORT;
- }
- | '.' ident_cli '.' ident_cli
- {
- Lex_ident_cli empty($2.pos(), 0);
- if (unlikely(!($$= Lex->create_item_ident(thd, &empty, &$2, &$4))))
- MYSQL_YYABORT;
- }
- | ident_cli '.' ident_cli '.' ident_cli
- {
- if (unlikely(!($$= Lex->create_item_ident(thd, &$1, &$3, &$5))))
- MYSQL_YYABORT;
- }
- | COLON_ORACLE_SYM ident_cli '.' ident_cli
- {
- if (unlikely(!($$= Lex->make_item_colon_ident_ident(thd, &$2, &$4))))
- MYSQL_YYABORT;
- }
- ;
-
-simple_ident_nospvar:
- ident
- {
- if (unlikely(!($$= Lex->create_item_ident_nosp(thd, &$1))))
- MYSQL_YYABORT;
- }
- | ident '.' ident
- {
- if (unlikely(!($$= Lex->create_item_ident_nospvar(thd, &$1, &$3))))
- MYSQL_YYABORT;
- }
- | COLON_ORACLE_SYM ident_cli '.' ident_cli
- {
- if (unlikely(!($$= Lex->make_item_colon_ident_ident(thd, &$2, &$4))))
- MYSQL_YYABORT;
- }
- | '.' ident '.' ident
- {
- Lex_ident_sys none;
- if (unlikely(!($$= Lex->create_item_ident(thd, &none, &$2, &$4))))
- MYSQL_YYABORT;
- }
- | ident '.' ident '.' ident
- {
- if (unlikely(!($$= Lex->create_item_ident(thd, &$1, &$3, &$5))))
- MYSQL_YYABORT;
- }
- ;
-
-field_ident:
- ident { $$=$1;}
- | ident '.' ident '.' ident
- {
- TABLE_LIST *table= Select->table_list.first;
- if (unlikely(my_strcasecmp(table_alias_charset, $1.str,
- table->db.str)))
- my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str));
- if (unlikely(my_strcasecmp(table_alias_charset, $3.str,
- table->table_name.str)))
- my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3.str));
- $$=$5;
- }
- | ident '.' ident
- {
- TABLE_LIST *table= Select->table_list.first;
- if (unlikely(my_strcasecmp(table_alias_charset, $1.str,
- table->alias.str)))
- my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $1.str));
- $$=$3;
- }
- | '.' ident { $$=$2;} /* For Delphi */
- ;
-
-table_ident:
- ident
- {
- $$= new (thd->mem_root) Table_ident(&$1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ident '.' ident
- {
- $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | '.' ident
- {
- /* For Delphi */
- $$= new (thd->mem_root) Table_ident(&$2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-table_ident_opt_wild:
- ident opt_wild
- {
- $$= new (thd->mem_root) Table_ident(&$1);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ident '.' ident opt_wild
- {
- $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-table_ident_nodb:
- ident
- {
- LEX_CSTRING db={(char*) any_db,3};
- $$= new (thd->mem_root) Table_ident(thd, &db, &$1, 0);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-IDENT_cli:
- IDENT
- | IDENT_QUOTED
- ;
-
-ident_cli:
- IDENT
- | IDENT_QUOTED
- | keyword_ident { $$= $1; }
- ;
-
-IDENT_sys:
- IDENT_cli
- {
- if (unlikely(thd->to_ident_sys_alloc(&$$, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-TEXT_STRING_sys:
- TEXT_STRING
- {
- if (thd->make_text_string_sys(&$$, &$1))
- MYSQL_YYABORT;
- }
- ;
-
-TEXT_STRING_literal:
- TEXT_STRING
- {
- if (thd->make_text_string_connection(&$$, &$1))
- MYSQL_YYABORT;
- }
- ;
-
-TEXT_STRING_filesystem:
- TEXT_STRING
- {
- if (thd->make_text_string_filesystem(&$$, &$1))
- MYSQL_YYABORT;
- }
- ;
-
-ident_table_alias:
- IDENT_sys
- | keyword_table_alias
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-ident_set_usual_case:
- IDENT_sys
- | keyword_set_usual_case
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-ident_sysvar_name:
- IDENT_sys
- | keyword_sysvar_name
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
- | TEXT_STRING_sys
- {
- if (unlikely($$.copy_sys(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-
-ident:
- IDENT_sys
- | keyword_ident
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-ident_directly_assignable:
- IDENT_sys
- | keyword_directly_assignable
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-
-label_ident:
- IDENT_sys
- | keyword_label
- {
- if (unlikely($$.copy_keyword(thd, &$1)))
- MYSQL_YYABORT;
- }
- ;
-
-labels_declaration_oracle:
- label_declaration_oracle { $$= $1; }
- | labels_declaration_oracle label_declaration_oracle { $$= $2; }
- ;
-
-label_declaration_oracle:
- SHIFT_LEFT label_ident SHIFT_RIGHT
- {
- if (unlikely(Lex->sp_push_goto_label(thd, &$2)))
- MYSQL_YYABORT;
- $$= $2;
- }
- ;
-
-ident_or_text:
- ident { $$=$1;}
- | TEXT_STRING_sys { $$=$1;}
- | LEX_HOSTNAME { $$=$1;}
- ;
-
-user_maybe_role:
- ident_or_text
- {
- if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- $$->user = $1;
-
- if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
- username_char_length,
- system_charset_info, 0)))
- MYSQL_YYABORT;
- }
- | ident_or_text '@' ident_or_text
- {
- if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- $$->user = $1; $$->host=$3;
-
- if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
- username_char_length,
- system_charset_info, 0)) ||
- unlikely(check_host_name(&$$->host)))
- MYSQL_YYABORT;
- 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, (char*) $$->host.str);
- }
- else
- {
- /*
- fix historical undocumented convention that empty host is the
- same as '%'
- */
- $$->host= host_not_specified;
- }
- }
- | CURRENT_USER optional_braces
- {
- if (unlikely(!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- $$->user= current_user;
- $$->auth= new (thd->mem_root) USER_AUTH();
- }
- ;
-
-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;
- }
- ;
-
-/* Keywords which we allow as table aliases. */
-keyword_table_alias:
- keyword_data_type
- | keyword_set_special_case
- | keyword_sp_block_section
- | keyword_sp_head
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_sysvar_type
- | keyword_verb_clause
- | FUNCTION_SYM
- ;
-
-/* Keyword that we allow for identifiers (except SP labels) */
-keyword_ident:
- keyword_data_type
- | keyword_set_special_case
- | keyword_sp_block_section
- | keyword_sp_head
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_sysvar_type
- | keyword_verb_clause
- | FUNCTION_SYM
- | WINDOW_SYM
- ;
-
-/*
- Keywords that we allow for labels in SPs.
- Should not include keywords that start a statement or SP characteristics.
-*/
-keyword_label:
- keyword_data_type
- | keyword_set_special_case
- | keyword_sp_var_and_label
- | keyword_sysvar_type
- | FUNCTION_SYM
- | COMPRESSED_SYM
- ;
-
-keyword_sysvar_name:
- keyword_data_type
- | keyword_set_special_case
- | keyword_sp_block_section
- | keyword_sp_head
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_verb_clause
- | FUNCTION_SYM
- | WINDOW_SYM
- ;
-
-keyword_sp_decl:
- keyword_sp_head
- | keyword_set_special_case
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_sysvar_type
- | keyword_verb_clause
- | WINDOW_SYM
- ;
-
-keyword_set_usual_case:
- keyword_data_type
- | keyword_sp_block_section
- | keyword_sp_head
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_sysvar_type
- | keyword_verb_clause
- | FUNCTION_SYM
- | WINDOW_SYM
- ;
-
-keyword_directly_assignable:
- keyword_data_type
- | keyword_set_special_case
- | keyword_sp_var_and_label
- | keyword_sp_var_not_label
- | keyword_sysvar_type
- | FUNCTION_SYM
- | WINDOW_SYM
- ;
-
-/*
- Keywords that we allow in Oracle-style direct assignments:
- xxx := 10;
- but do not allow in labels in the default sql_mode:
- label:
- stmt1;
- stmt2;
- TODO: check if some of them can migrate to keyword_sp_var_and_label.
-*/
-keyword_sp_var_not_label:
- ASCII_SYM
- | BACKUP_SYM
- | BINLOG_SYM
- | BYTE_SYM
- | CACHE_SYM
- | CHECKSUM_SYM
- | CHECKPOINT_SYM
- | COLUMN_ADD_SYM
- | COLUMN_CHECK_SYM
- | COLUMN_CREATE_SYM
- | COLUMN_DELETE_SYM
- | COLUMN_GET_SYM
- | COMMENT_SYM
- | COMPRESSED_SYM
- | DEALLOCATE_SYM
- | EXAMINED_SYM
- | EXCLUDE_SYM
- | EXECUTE_SYM
- | FLUSH_SYM
- | FOLLOWING_SYM
- | FORMAT_SYM
- | GET_SYM
- | HELP_SYM
- | HOST_SYM
- | INSTALL_SYM
- | OPTION
- | OPTIONS_SYM
- | OTHERS_MARIADB_SYM
- | OWNER_SYM
- | PARSER_SYM
- | PERIOD_SYM
- | PORT_SYM
- | PRECEDING_SYM
- | PREPARE_SYM
- | REMOVE_SYM
- | RESET_SYM
- | RESTORE_SYM
- | SECURITY_SYM
- | SERVER_SYM
- | SIGNED_SYM
- | SOCKET_SYM
- | SLAVE
- | SLAVES
- | SONAME_SYM
- | START_SYM
- | STOP_SYM
- | STORED_SYM
- | TIES_SYM
- | UNICODE_SYM
- | UNINSTALL_SYM
- | UNBOUNDED_SYM
- | WITHIN
- | WRAPPER_SYM
- | XA_SYM
- | UPGRADE_SYM
- ;
-
-/*
- Keywords that can start optional clauses in SP or trigger declarations
- Allowed as identifiers (e.g. table, column names),
- but:
- - not allowed as SP label names
- - not allowed as variable names in Oracle-style assignments:
- xxx := 10;
-
- If we allowed these variables in assignments, there would be conflicts
- with SP characteristics, or verb clauses, or compound statements, e.g.:
- CREATE PROCEDURE p1 LANGUAGE ...
- would be either:
- CREATE PROCEDURE p1 LANGUAGE SQL BEGIN END;
- or
- CREATE PROCEDURE p1 LANGUAGE:=10;
-
- Note, these variables can still be assigned using quoted identifiers:
- `do`:= 10;
- "do":= 10; (when ANSI_QUOTES)
- or using a SET statement:
- SET do= 10;
-
- Note, some of these keywords are reserved keywords in Oracle.
- In case if heavy grammar conflicts are found in the future,
- we'll possibly need to make them reserved for sql_mode=ORACLE.
-
- TODO: Allow these variables as SP lables when sql_mode=ORACLE.
- TODO: Allow assigning of "SP characteristics" marked variables
- inside compound blocks.
- TODO: Allow "follows" and "precedes" as variables in compound blocks:
- BEGIN
- follows := 10;
- END;
- as they conflict only with non-block FOR EACH ROW statement:
- CREATE TRIGGER .. FOR EACH ROW follows:= 10;
- CREATE TRIGGER .. FOR EACH ROW FOLLOWS tr1 a:= 10;
-*/
-keyword_sp_head:
- CONTAINS_SYM /* SP characteristic */
- | LANGUAGE_SYM /* SP characteristic */
- | NO_SYM /* SP characteristic */
- | CHARSET /* SET CHARSET utf8; */
- | FOLLOWS_SYM /* Conflicts with assignment in FOR EACH */
- | PRECEDES_SYM /* Conflicts with assignment in FOR EACH */
- ;
-
-/*
- Keywords that start a statement.
- Generally allowed as identifiers (e.g. table, column names)
- - not allowed as SP label names
- - not allowed as variable names in Oracle-style assignments:
- xxx:=10
-*/
-keyword_verb_clause:
- CLOSE_SYM /* Verb clause. Reserved in Oracle */
- | COMMIT_SYM /* Verb clause. Reserved in Oracle */
- | DO_SYM /* Verb clause */
- | HANDLER_SYM /* Verb clause */
- | OPEN_SYM /* Verb clause. Reserved in Oracle */
- | REPAIR /* Verb clause */
- | ROLLBACK_SYM /* Verb clause. Reserved in Oracle */
- | SAVEPOINT_SYM /* Verb clause. Reserved in Oracle */
- | SHUTDOWN /* Verb clause */
- | TRUNCATE_SYM /* Verb clause. Reserved in Oracle */
- ;
-
-keyword_set_special_case:
- NAMES_SYM
- | ROLE_SYM
- | PASSWORD_SYM
- ;
-
-/*
- Keywords that start an SP block section.
-*/
-keyword_sp_block_section:
- BEGIN_ORACLE_SYM
- | EXCEPTION_ORACLE_SYM
- | END
- ;
-
-keyword_sysvar_type:
- GLOBAL_SYM
- | LOCAL_SYM
- | SESSION_SYM
- ;
-
-
-/*
- These keywords are generally allowed as identifiers,
- but not allowed as non-delimited SP variable names in sql_mode=ORACLE.
-*/
-keyword_data_type:
- BIT_SYM
- | BOOLEAN_SYM
- | BOOL_SYM
- | CLOB_MARIADB_SYM
- | CLOB_ORACLE_SYM
- | DATE_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | DATETIME
- | ENUM
- | FIXED_SYM
- | GEOMETRYCOLLECTION
- | GEOMETRY_SYM
- | JSON_SYM
- | LINESTRING
- | MEDIUM_SYM
- | MULTILINESTRING
- | MULTIPOINT
- | MULTIPOLYGON
- | NATIONAL_SYM
- | NCHAR_SYM
- | NUMBER_MARIADB_SYM
- | NUMBER_ORACLE_SYM
- | NVARCHAR_SYM
- | POINT_SYM
- | POLYGON
- | RAW_MARIADB_SYM
- | RAW_ORACLE_SYM
- | ROW_SYM
- | SERIAL_SYM
- | TEXT_SYM
- | TIMESTAMP %prec PREC_BELOW_CONTRACTION_TOKEN2
- | TIME_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | VARCHAR2_MARIADB_SYM
- | VARCHAR2_ORACLE_SYM
- | YEAR_SYM
- ;
-
-
-/*
- These keywords are fine for both SP variable names and SP labels.
-*/
-keyword_sp_var_and_label:
- ACTION
- | ACCOUNT_SYM
- | ADDDATE_SYM
- | ADMIN_SYM
- | AFTER_SYM
- | AGAINST
- | AGGREGATE_SYM
- | ALGORITHM_SYM
- | ALWAYS_SYM
- | ANY_SYM
- | AT_SYM
- | ATOMIC_SYM
- | AUTHORS_SYM
- | AUTO_INC
- | AUTOEXTEND_SIZE_SYM
- | AUTO_SYM
- | AVG_ROW_LENGTH
- | AVG_SYM
- | BLOCK_SYM
- | BODY_MARIADB_SYM
- | BTREE_SYM
- | CASCADED
- | CATALOG_NAME_SYM
- | CHAIN_SYM
- | CHANGED
- | CIPHER_SYM
- | CLIENT_SYM
- | CLASS_ORIGIN_SYM
- | COALESCE
- | CODE_SYM
- | COLLATION_SYM
- | COLUMN_NAME_SYM
- | COLUMNS
- | COMMITTED_SYM
- | COMPACT_SYM
- | COMPLETION_SYM
- | CONCURRENT
- | CONNECTION_SYM
- | CONSISTENT_SYM
- | CONSTRAINT_CATALOG_SYM
- | CONSTRAINT_SCHEMA_SYM
- | 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
- | CYCLE_SYM
- | DATA_SYM
- | DATAFILE_SYM
- | DATE_FORMAT_SYM
- | DAY_SYM
- | DECODE_MARIADB_SYM
- | DECODE_ORACLE_SYM
- | DEFINER_SYM
- | DELAY_KEY_WRITE_SYM
- | DES_KEY_FILE
- | DIAGNOSTICS_SYM
- | DIRECTORY_SYM
- | DISABLE_SYM
- | DISCARD
- | DISK_SYM
- | DUMPFILE
- | DUPLICATE_SYM
- | DYNAMIC_SYM
- | ELSEIF_ORACLE_SYM
- | ELSIF_MARIADB_SYM
- | ENDS_SYM
- | ENGINE_SYM
- | ENGINES_SYM
- | ERROR_SYM
- | ERRORS
- | ESCAPE_SYM
- | EVENT_SYM
- | EVENTS_SYM
- | EVERY_SYM
- | EXCEPTION_MARIADB_SYM
- | EXCHANGE_SYM
- | EXPANSION_SYM
- | EXPIRE_SYM
- | EXPORT_SYM
- | EXTENDED_SYM
- | EXTENT_SIZE_SYM
- | FAULTS_SYM
- | FAST_SYM
- | FOUND_SYM
- | ENABLE_SYM
- | FULL
- | FILE_SYM
- | FIRST_SYM
- | GENERAL
- | GENERATED_SYM
- | GET_FORMAT
- | GRANTS
- | GOTO_MARIADB_SYM
- | HASH_SYM
- | HARD_SYM
- | HISTORY_SYM
- | HOSTS_SYM
- | HOUR_SYM
- | ID_SYM
- | IDENTIFIED_SYM
- | IGNORE_SERVER_IDS_SYM
- | INCREMENT_SYM
- | IMMEDIATE_SYM
- | INVOKER_SYM
- | IMPORT
- | INDEXES
- | INITIAL_SIZE_SYM
- | IO_SYM
- | IPC_SYM
- | ISOLATION
- | ISOPEN_SYM
- | ISSUER_SYM
- | INSERT_METHOD
- | INVISIBLE_SYM
- | KEY_BLOCK_SIZE
- | LAST_VALUE
- | LAST_SYM
- | LASTVAL_SYM
- | LEAVES
- | LESS_SYM
- | LEVEL_SYM
- | LIST_SYM
- | LOCKS_SYM
- | LOGFILE_SYM
- | LOGS_SYM
- | 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
- | MASTER_DELAY_SYM
- | MASTER_SSL_SYM
- | MASTER_SSL_CA_SYM
- | 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
- | MAX_SIZE_SYM
- | MAX_STATEMENT_TIME_SYM
- | MAX_UPDATES_PER_HOUR
- | MAX_USER_CONNECTIONS_SYM
- | MEMORY_SYM
- | MERGE_SYM
- | MESSAGE_TEXT_SYM
- | MICROSECOND_SYM
- | MIGRATE_SYM
- | MINUTE_SYM
- | MINVALUE_SYM
- | MIN_ROWS
- | MODIFY_SYM
- | MODE_SYM
- | MONTH_SYM
- | MUTEX_SYM
- | MYSQL_SYM
- | MYSQL_ERRNO_SYM
- | NAME_SYM
- | NEXT_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | NEXTVAL_SYM
- | NEVER_SYM
- | NEW_SYM
- | NOCACHE_SYM
- | NOCYCLE_SYM
- | NOMINVALUE_SYM
- | NOMAXVALUE_SYM
- | NO_WAIT_SYM
- | NOWAIT_SYM
- | NODEGROUP_SYM
- | NONE_SYM
- | NOTFOUND_SYM
- | OF_SYM
- | OFFSET_SYM
- | OLD_PASSWORD_SYM
- | ONE_SYM
- | ONLINE_SYM
- | ONLY_SYM
- | PACKAGE_MARIADB_SYM
- | PACK_KEYS_SYM
- | PAGE_SYM
- | PARTIAL
- | PARTITIONING_SYM
- | PARTITIONS_SYM
- | PERSISTENT_SYM
- | PHASE_SYM
- | PLUGIN_SYM
- | PLUGINS_SYM
- | PRESERVE_SYM
- | PREV_SYM
- | PREVIOUS_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | PRIVILEGES
- | PROCESS
- | PROCESSLIST_SYM
- | PROFILE_SYM
- | PROFILES_SYM
- | PROXY_SYM
- | QUARTER_SYM
- | QUERY_SYM
- | QUICK
- | RAISE_MARIADB_SYM
- | READ_ONLY_SYM
- | REBUILD_SYM
- | RECOVER_SYM
- | REDO_BUFFER_SIZE_SYM
- | REDOFILE_SYM
- | REDUNDANT_SYM
- | RELAY
- | RELAYLOG_SYM
- | RELAY_LOG_FILE_SYM
- | RELAY_LOG_POS_SYM
- | RELAY_THREAD
- | RELOAD
- | REORGANIZE_SYM
- | REPEATABLE_SYM
- | REPLICATION
- | RESOURCES
- | RESTART_SYM
- | RESUME_SYM
- | RETURNED_SQLSTATE_SYM
- | RETURNS_SYM
- | REUSE_SYM
- | REVERSE_SYM
- | ROLLUP_SYM
- | ROUTINE_SYM
- | ROWCOUNT_SYM
- | ROWTYPE_MARIADB_SYM
- | ROW_COUNT_SYM
- | ROW_FORMAT_SYM
- | RTREE_SYM
- | SCHEDULE_SYM
- | SCHEMA_NAME_SYM
- | SECOND_SYM
- | SEQUENCE_SYM
- | SERIALIZABLE_SYM
- | SETVAL_SYM
- | SIMPLE_SYM
- | SHARE_SYM
- | SLAVE_POS_SYM
- | SLOW
- | SNAPSHOT_SYM
- | SOFT_SYM
- | SOUNDS_SYM
- | SOURCE_SYM
- | SQL_CACHE_SYM
- | SQL_BUFFER_RESULT
- | SQL_NO_CACHE_SYM
- | SQL_THREAD
- | STAGE_SYM
- | STARTS_SYM
- | STATEMENT_SYM
- | STATUS_SYM
- | STORAGE_SYM
- | STRING_SYM
- | SUBCLASS_ORIGIN_SYM
- | SUBDATE_SYM
- | SUBJECT_SYM
- | SUBPARTITION_SYM
- | SUBPARTITIONS_SYM
- | SUPER_SYM
- | SUSPEND_SYM
- | SWAPS_SYM
- | SWITCHES_SYM
- | SYSTEM
- | SYSTEM_TIME_SYM
- | TABLE_NAME_SYM
- | TABLES
- | TABLE_CHECKSUM_SYM
- | TABLESPACE
- | TEMPORARY
- | TEMPTABLE_SYM
- | THAN_SYM
- | TRANSACTION_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | TRANSACTIONAL_SYM
- | TRIGGERS_SYM
- | TRIM_ORACLE
- | TIMESTAMP_ADD
- | TIMESTAMP_DIFF
- | TYPES_SYM
- | TYPE_SYM
- | UDF_RETURNS_SYM
- | UNCOMMITTED_SYM
- | UNDEFINED_SYM
- | UNDO_BUFFER_SIZE_SYM
- | UNDOFILE_SYM
- | UNKNOWN_SYM
- | UNTIL_SYM
- | USER_SYM %prec PREC_BELOW_CONTRACTION_TOKEN2
- | USE_FRM
- | VARIABLES
- | VERSIONING_SYM
- | VIEW_SYM
- | VIRTUAL_SYM
- | VALUE_SYM
- | WARNINGS
- | WAIT_SYM
- | WEEK_SYM
- | WEIGHT_STRING_SYM
- | WITHOUT
- | WORK_SYM
- | X509_SYM
- | XML_SYM
- | VIA_SYM
- ;
-
-/*
- 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
- {
- LEX *lex=Lex;
- if (lex->main_select_push(true))
- MYSQL_YYABORT;
- lex->set_stmt_init();
- lex->var_list.empty();
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- start_option_value_list
- {
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- | SET STATEMENT_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- Lex->set_stmt_init();
- }
- set_stmt_option_value_following_option_type_list
- {
- LEX *lex= Lex;
- if (unlikely(lex->table_or_sp_used()))
- my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT"));
- lex->stmt_var_list= lex->var_list;
- lex->var_list.empty();
- Lex->pop_select(); //main select
- if (Lex->check_main_unit_semantics())
- MYSQL_YYABORT;
- }
- FOR_SYM verb_clause
- {}
- ;
-
-set_assign:
- ident_directly_assignable SET_VAR
- {
- LEX *lex=Lex;
- lex->set_stmt_init();
- lex->var_list.empty();
- if(sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- set_expr_or_default
- {
- if (unlikely(Lex->set_variable(&$1, $4)) ||
- unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- | ident_directly_assignable '.' ident SET_VAR
- {
- LEX *lex=Lex;
- lex->set_stmt_init();
- lex->var_list.empty();
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- set_expr_or_default
- {
- LEX *lex= Lex;
- DBUG_ASSERT(lex->var_list.is_empty());
- if (unlikely(lex->set_variable(&$1, &$3, $6)) ||
- unlikely(lex->sphead->restore_lex(thd)))
- MYSQL_YYABORT;
- }
- | COLON_ORACLE_SYM ident '.' ident SET_VAR
- {
- LEX *lex= Lex;
- if (unlikely(!lex->is_trigger_new_or_old_reference(&$2)))
- {
- thd->parse_error(ER_SYNTAX_ERROR, $1.pos());
- MYSQL_YYABORT;
- }
- lex->set_stmt_init();
- lex->var_list.empty();
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- set_expr_or_default
- {
- LEX_CSTRING tmp= { $2.str, $2.length };
- if (unlikely(Lex->set_trigger_field(&tmp, &$4, $7)) ||
- unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- ;
-
-set_stmt_option_value_following_option_type_list:
- /*
- Only system variables can be used here. If this condition is changed
- please check careful code under lex->option_type == OPT_STATEMENT
- condition on wrong type casts.
- */
- option_value_following_option_type
- | set_stmt_option_value_following_option_type_list ',' option_value_following_option_type
- ;
-
-/* Start of option value list */
-start_option_value_list:
- option_value_no_option_type
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- option_value_list_continued
- | TRANSACTION_SYM
- {
- Lex->option_type= OPT_DEFAULT;
- }
- transaction_characteristics
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- | option_type
- {
- Lex->option_type= $1;
- }
- start_option_value_list_following_option_type
- ;
-
-
-/* Start of option value list, option_type was given */
-start_option_value_list_following_option_type:
- option_value_following_option_type
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- option_value_list_continued
- | TRANSACTION_SYM transaction_characteristics
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- ;
-
-/* Remainder of the option value list after first option value. */
-option_value_list_continued:
- /* empty */
- | ',' option_value_list
- ;
-
-/* Repeating list of option values after first option value. */
-option_value_list:
- {
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- option_value
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- MYSQL_YYABORT;
- }
- | option_value_list ','
- {
- if (sp_create_assignment_lex(thd, yychar == YYEMPTY))
- MYSQL_YYABORT;
- }
- option_value
- {
- if (unlikely(sp_create_assignment_instr(thd, yychar == YYEMPTY)))
- 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:
- GLOBAL_SYM { $$=OPT_GLOBAL; }
- | LOCAL_SYM { $$=OPT_SESSION; }
- | SESSION_SYM { $$=OPT_SESSION; }
- ;
-
-opt_var_type:
- /* empty */ { $$=OPT_SESSION; }
- | GLOBAL_SYM { $$=OPT_GLOBAL; }
- | LOCAL_SYM { $$=OPT_SESSION; }
- | SESSION_SYM { $$=OPT_SESSION; }
- ;
-
-opt_var_ident_type:
- /* empty */ { $$=OPT_DEFAULT; }
- | GLOBAL_SYM '.' { $$=OPT_GLOBAL; }
- | LOCAL_SYM '.' { $$=OPT_SESSION; }
- | SESSION_SYM '.' { $$=OPT_SESSION; }
- ;
-
-/* Option values with preceding option_type. */
-option_value_following_option_type:
- ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_system_variable(Lex->option_type, &$1, $3)))
- MYSQL_YYABORT;
- }
- | ident '.' ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_system_variable(thd, Lex->option_type, &$1, &$3, $5)))
- MYSQL_YYABORT;
- }
- | DEFAULT '.' ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_default_system_variable(Lex->option_type, &$3, $5)))
- MYSQL_YYABORT;
- }
- ;
-
-/* Option values without preceding option_type. */
-option_value_no_option_type:
- ident_set_usual_case equal set_expr_or_default
- {
- if (unlikely(Lex->set_variable(&$1, $3)))
- MYSQL_YYABORT;
- }
- | ident '.' ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_variable(&$1, &$3, $5)))
- MYSQL_YYABORT;
- }
- | DEFAULT '.' ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_default_system_variable(Lex->option_type, &$3, $5)))
- MYSQL_YYABORT;
- }
- | '@' ident_or_text equal expr
- {
- if (unlikely(Lex->set_user_variable(thd, &$2, $4)))
- MYSQL_YYABORT;
- }
- | '@' '@' opt_var_ident_type ident_sysvar_name equal set_expr_or_default
- {
- if (unlikely(Lex->set_system_variable($3, &$4, $6)))
- MYSQL_YYABORT;
- }
- | '@' '@' opt_var_ident_type ident_sysvar_name '.' ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_system_variable(thd, $3, &$4, &$6, $8)))
- MYSQL_YYABORT;
- }
- | '@' '@' opt_var_ident_type DEFAULT '.' ident equal set_expr_or_default
- {
- if (unlikely(Lex->set_default_system_variable($3, &$6, $8)))
- MYSQL_YYABORT;
- }
- | charset old_or_new_charset_name_or_default
- {
- LEX *lex= thd->lex;
- CHARSET_INFO *cs2;
- cs2= $2 ? $2: global_system_variables.character_set_client;
- set_var_collation_client *var;
- var= (new (thd->mem_root)
- set_var_collation_client(cs2,
- thd->variables.collation_database,
- cs2));
- if (unlikely(var == NULL))
- MYSQL_YYABORT;
- lex->var_list.push_back(var, thd->mem_root);
- }
- | NAMES_SYM equal expr
- {
- LEX *lex= Lex;
- sp_pcontext *spc= lex->spcont;
- LEX_CSTRING names= { STRING_WITH_LEN("names") };
- if (unlikely(spc && spc->find_variable(&names, false)))
- my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str);
- else
- thd->parse_error();
- MYSQL_YYABORT;
- }
- | NAMES_SYM charset_name_or_default opt_collate
- {
- LEX *lex= Lex;
- CHARSET_INFO *cs2;
- CHARSET_INFO *cs3;
- cs2= $2 ? $2 : global_system_variables.character_set_client;
- cs3= $3 ? $3 : cs2;
- if (unlikely(!my_charset_same(cs2, cs3)))
- {
- my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
- cs3->name, cs2->csname);
- MYSQL_YYABORT;
- }
- set_var_collation_client *var;
- var= new (thd->mem_root) set_var_collation_client(cs3, cs3, cs3);
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | DEFAULT ROLE_SYM grant_role
- {
- LEX *lex = Lex;
- LEX_USER *user;
- if (unlikely(!(user=(LEX_USER *) thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- user->user= current_user;
- set_var_default_role *var= (new (thd->mem_root)
- set_var_default_role(user,
- $3->user));
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
-
- thd->lex->autocommit= TRUE;
- if (lex->sphead)
- lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
- }
- | DEFAULT ROLE_SYM grant_role FOR_SYM user
- {
- LEX *lex = Lex;
- set_var_default_role *var= (new (thd->mem_root)
- set_var_default_role($5, $3->user));
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
- thd->lex->autocommit= TRUE;
- if (lex->sphead)
- lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
- }
- | ROLE_SYM ident_or_text
- {
- LEX *lex = Lex;
- set_var_role *var= new (thd->mem_root) set_var_role($2);
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | ROLE_SYM equal set_expr_or_default
- {
- if (unlikely(Lex->set_variable(&$1, $3)))
- MYSQL_YYABORT;
- }
- | PASSWORD_SYM opt_for_user text_or_password
- {
- LEX *lex = Lex;
- set_var_password *var= (new (thd->mem_root)
- set_var_password(lex->definer));
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
- lex->autocommit= TRUE;
- if (lex->sphead)
- lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
- }
- ;
-
-
-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(thd, (int32) $1);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- set_var *var= (new (thd->mem_root)
- set_var(thd, lex->option_type,
- find_sys_var(thd, "tx_read_only"),
- &null_clex_str,
- item));
- if (unlikely(var == NULL))
- MYSQL_YYABORT;
- if (unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-isolation_level:
- ISOLATION LEVEL_SYM isolation_types
- {
- LEX *lex=Lex;
- Item *item= new (thd->mem_root) Item_int(thd, (int32) $3);
- if (unlikely(item == NULL))
- MYSQL_YYABORT;
- set_var *var= (new (thd->mem_root)
- set_var(thd, lex->option_type,
- find_sys_var(thd, "tx_isolation"),
- &null_clex_str,
- item));
- if (unlikely(var == NULL) ||
- unlikely(lex->var_list.push_back(var, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-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; }
- | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; }
- | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; }
- ;
-
-opt_for_user:
- equal
- {
- LEX *lex= thd->lex;
- sp_pcontext *spc= lex->spcont;
- LEX_CSTRING pw= { STRING_WITH_LEN("password") };
-
- if (unlikely(spc && spc->find_variable(&pw, false)))
- my_yyabort_error((ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str));
- if (unlikely(!(lex->definer= (LEX_USER*)
- thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- lex->definer->user= current_user;
- lex->definer->auth= new (thd->mem_root) USER_AUTH();
- }
- | FOR_SYM user equal { Lex->definer= $2; }
- ;
-
-text_or_password:
- TEXT_STRING
- {
- Lex->definer->auth= new (thd->mem_root) USER_AUTH();
- Lex->definer->auth->auth_str= $1;
- }
- | PASSWORD_SYM '(' TEXT_STRING ')'
- {
- Lex->definer->auth= new (thd->mem_root) USER_AUTH();
- Lex->definer->auth->pwtext= $3;
- }
- | OLD_PASSWORD_SYM '(' TEXT_STRING ')'
- {
- Lex->definer->auth= new (thd->mem_root) USER_AUTH();
- Lex->definer->auth->pwtext= $3;
- Lex->definer->auth->auth_str.str= Item_func_password::alloc(thd,
- $3.str, $3.length, Item_func_password::OLD);
- Lex->definer->auth->auth_str.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- }
- ;
-
-set_expr_or_default:
- expr { $$=$1; }
- | DEFAULT { $$=0; }
- | ON
- {
- $$=new (thd->mem_root) Item_string_sys(thd, "ON", 2);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | ALL
- {
- $$=new (thd->mem_root) Item_string_sys(thd, "ALL", 3);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- | BINARY
- {
- $$=new (thd->mem_root) Item_string_sys(thd, "binary", 6);
- if (unlikely($$ == NULL))
- MYSQL_YYABORT;
- }
- ;
-
-/* Lock function */
-
-lock:
- LOCK_SYM table_or_tables
- {
- LEX *lex= Lex;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK"));
- lex->sql_command= SQLCOM_LOCK_TABLES;
- }
- table_lock_list opt_lock_wait_timeout
- {}
- ;
-
-opt_lock_wait_timeout:
- /* empty */
- {}
- | WAIT_SYM ulong_num
- {
- if (unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), $2)) ||
- unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), $2)))
- MYSQL_YYABORT;
- }
- | NOWAIT_SYM
- {
- if (unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("lock_wait_timeout"), 0)) ||
- unlikely(set_statement_var_if_exists(thd, STRING_WITH_LEN("innodb_lock_wait_timeout"), 0)))
- MYSQL_YYABORT;
- }
- ;
-
-table_or_tables:
- TABLE_SYM { }
- | TABLES { }
- ;
-
-table_lock_list:
- table_lock
- | table_lock_list ',' table_lock
- ;
-
-table_lock:
- table_ident opt_table_alias_clause lock_option
- {
- thr_lock_type lock_type= (thr_lock_type) $3;
- bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE);
- ulong table_options= lock_for_write ? TL_OPTION_UPDATING : 0;
- enum_mdl_type mdl_type= !lock_for_write
- ? MDL_SHARED_READ
- : lock_type == TL_WRITE_CONCURRENT_INSERT
- ? MDL_SHARED_WRITE
- : MDL_SHARED_NO_READ_WRITE;
-
- if (unlikely(!Lex->current_select_or_default()->
- add_table_to_list(thd, $1, $2, table_options,
- lock_type, mdl_type)))
- MYSQL_YYABORT;
- }
- ;
-
-lock_option:
- READ_SYM { $$= TL_READ_NO_INSERT; }
- | WRITE_SYM { $$= TL_WRITE_DEFAULT; }
- | WRITE_SYM CONCURRENT
- {
- $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT);
- }
-
- | LOW_PRIORITY WRITE_SYM { $$= TL_WRITE_LOW_PRIORITY; }
- | READ_SYM LOCAL_SYM { $$= TL_READ; }
- ;
-
-unlock:
- UNLOCK_SYM
- {
- LEX *lex= Lex;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "UNLOCK"));
- lex->sql_command= SQLCOM_UNLOCK_TABLES;
- }
- table_or_tables
- {}
- ;
-
-/*
-** Handler: direct access to ISAM functions
-*/
-
-handler:
- HANDLER_SYM
- {
- if (Lex->main_select_push())
- MYSQL_YYABORT;
- }
- handler_tail
- {
- Lex->pop_select(); //main select
- }
- ;
-
-handler_tail:
- table_ident OPEN_SYM opt_table_alias_clause
- {
- LEX *lex= Lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
- lex->sql_command = SQLCOM_HA_OPEN;
- if (!lex->current_select->add_table_to_list(thd, $1, $3, 0))
- MYSQL_YYABORT;
- }
- | table_ident_nodb CLOSE_SYM
- {
- LEX *lex= Lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
- lex->sql_command = SQLCOM_HA_CLOSE;
- if (!lex->current_select->add_table_to_list(thd, $1, 0, 0))
- MYSQL_YYABORT;
- }
- | table_ident_nodb READ_SYM
- {
- LEX *lex=Lex;
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER"));
- lex->clause_that_disallows_subselect= "HANDLER..READ";
- lex->sql_command = SQLCOM_HA_READ;
- lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
- Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
- if (unlikely(one == NULL))
- MYSQL_YYABORT;
- lex->current_select->select_limit= one;
- lex->current_select->offset_limit= 0;
- lex->limit_rows_examined= 0;
- if (!lex->current_select->add_table_to_list(thd, $1, 0, 0))
- MYSQL_YYABORT;
- }
- handler_read_or_scan opt_where_clause opt_global_limit_clause
- {
- LEX *lex=Lex;
- lex->clause_that_disallows_subselect= NULL;
- if (!lex->current_select->explicit_limit)
- {
- Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
- if (one == NULL)
- MYSQL_YYABORT;
- lex->current_select->select_limit= one;
- lex->current_select->offset_limit= 0;
- lex->limit_rows_examined= 0;
- }
- /* Stored functions are not supported for HANDLER READ. */
- if (lex->uses_stored_routines())
- {
- my_error(ER_NOT_SUPPORTED_YET, MYF(0),
- "stored functions in HANDLER ... READ");
- MYSQL_YYABORT;
- }
- }
- ;
-
-handler_read_or_scan:
- handler_scan_function { Lex->ident= null_clex_str; }
- | ident handler_rkey_function { Lex->ident= $1; }
- ;
-
-handler_scan_function:
- FIRST_SYM { Lex->ha_read_mode = RFIRST; }
- | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
- ;
-
-handler_rkey_function:
- FIRST_SYM { Lex->ha_read_mode = RFIRST; }
- | NEXT_SYM { Lex->ha_read_mode = RNEXT; }
- | PREV_SYM { Lex->ha_read_mode = RPREV; }
- | LAST_SYM { Lex->ha_read_mode = RLAST; }
- | handler_rkey_mode
- {
- LEX *lex=Lex;
- lex->ha_read_mode = RKEY;
- lex->ha_rkey_mode=$1;
- if (unlikely(!(lex->insert_list= new (thd->mem_root) List_item)))
- MYSQL_YYABORT;
- }
- '(' values ')'
- {}
- ;
-
-handler_rkey_mode:
- '=' { $$=HA_READ_KEY_EXACT; }
- | GE { $$=HA_READ_KEY_OR_NEXT; }
- | LE { $$=HA_READ_KEY_OR_PREV; }
- | '>' { $$=HA_READ_AFTER_KEY; }
- | '<' { $$=HA_READ_BEFORE_KEY; }
- ;
-
-/* GRANT / REVOKE */
-
-revoke:
- REVOKE clear_privileges revoke_command
- {}
- ;
-
-revoke_command:
- 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_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_FUNCTION)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_PROCEDURE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM grant_ident
- FROM user_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_PACKAGE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM BODY_ORACLE_SYM grant_ident
- FROM user_and_role_list
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_REVOKE,
- TYPE_ENUM_PACKAGE_BODY)))
- MYSQL_YYABORT;
- }
- | ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
- {
- Lex->sql_command = SQLCOM_REVOKE_ALL;
- }
- | PROXY_SYM ON user FROM user_list
- {
- LEX *lex= Lex;
- 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 (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
- 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
- {}
- ;
-
-grant_command:
- grant_privileges ON opt_table grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- LEX *lex= Lex;
- lex->sql_command= SQLCOM_GRANT;
- lex->type= 0;
- }
- | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_FUNCTION)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_PROCEDURE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_PACKAGE)))
- MYSQL_YYABORT;
- }
- | grant_privileges ON PACKAGE_ORACLE_SYM BODY_ORACLE_SYM grant_ident TO_SYM grant_list
- opt_require_clause opt_grant_options
- {
- if (unlikely(Lex->add_grant_command(thd, SQLCOM_GRANT,
- TYPE_ENUM_PACKAGE_BODY)))
- MYSQL_YYABORT;
- }
- | PROXY_SYM ON user TO_SYM grant_list opt_grant_option
- {
- LEX *lex= Lex;
- 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 (unlikely(Lex->users_list.push_front($1, thd->mem_root)))
- 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 (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | role_list ',' grant_role
- {
- if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-current_role:
- CURRENT_ROLE optional_braces
- {
- if (unlikely(!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- $$->user= current_role;
- $$->auth= NULL;
- }
- ;
-
-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);
- ((char*) $1.str)[$1.length] = '\0';
- if (unlikely($1.length == 0))
- my_yyabort_error((ER_INVALID_ROLE, MYF(0), ""));
- if (unlikely(!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))))
- MYSQL_YYABORT;
- $$->user= $1;
- $$->host= empty_clex_str;
- $$->auth= NULL;
-
- if (unlikely(check_string_char_length(&$$->user, ER_USERNAME,
- username_char_length,
- cs, 0)))
- MYSQL_YYABORT;
- }
- | current_role
- ;
-
-opt_table:
- /* Empty */
- | TABLE_SYM
- ;
-
-grant_privileges:
- object_privilege_list {}
- | ALL opt_privileges
- {
- Lex->all_privileges= 1;
- Lex->grant= GLOBAL_ACLS;
- }
- ;
-
-opt_privileges:
- /* empty */
- | PRIVILEGES
- ;
-
-object_privilege_list:
- object_privilege
- | object_privilege_list ',' object_privilege
- ;
-
-object_privilege:
- SELECT_SYM
- { Lex->which_columns = SELECT_ACL;}
- opt_column_list {}
- | INSERT
- { Lex->which_columns = INSERT_ACL;}
- opt_column_list {}
- | UPDATE_SYM
- { Lex->which_columns = UPDATE_ACL; }
- opt_column_list {}
- | REFERENCES
- { Lex->which_columns = REFERENCES_ACL;}
- opt_column_list {}
- | DELETE_SYM { Lex->grant |= DELETE_ACL;}
- | USAGE {}
- | INDEX_SYM { Lex->grant |= INDEX_ACL;}
- | ALTER { Lex->grant |= ALTER_ACL;}
- | CREATE { Lex->grant |= CREATE_ACL;}
- | DROP { Lex->grant |= DROP_ACL;}
- | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;}
- | RELOAD { Lex->grant |= RELOAD_ACL;}
- | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;}
- | PROCESS { Lex->grant |= PROCESS_ACL;}
- | FILE_SYM { Lex->grant |= FILE_ACL;}
- | GRANT OPTION { Lex->grant |= GRANT_ACL;}
- | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;}
- | SUPER_SYM { Lex->grant |= SUPER_ACL;}
- | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;}
- | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; }
- | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; }
- | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
- | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
- | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
- | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; }
- | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; }
- | CREATE USER_SYM { Lex->grant |= CREATE_USER_ACL; }
- | EVENT_SYM { Lex->grant |= EVENT_ACL;}
- | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; }
- | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; }
- | DELETE_SYM HISTORY_SYM { Lex->grant |= DELETE_HISTORY_ACL; }
- ;
-
-opt_and:
- /* empty */ {}
- | AND_SYM {}
- ;
-
-require_list:
- require_list_element opt_and require_list
- | require_list_element
- ;
-
-require_list_element:
- SUBJECT_SYM TEXT_STRING
- {
- LEX *lex=Lex;
- if (lex->account_options.x509_subject.str)
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT"));
- lex->account_options.x509_subject= $2;
- }
- | ISSUER_SYM TEXT_STRING
- {
- LEX *lex=Lex;
- if (lex->account_options.x509_issuer.str)
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER"));
- lex->account_options.x509_issuer= $2;
- }
- | CIPHER_SYM TEXT_STRING
- {
- LEX *lex=Lex;
- if (lex->account_options.ssl_cipher.str)
- my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER"));
- lex->account_options.ssl_cipher= $2;
- }
- ;
-
-grant_ident:
- '*'
- {
- LEX *lex= Lex;
- if (unlikely(lex->copy_db_to(&lex->first_select_lex()->db)))
- MYSQL_YYABORT;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (unlikely(lex->columns.elements))
- my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
- }
- | ident '.' '*'
- {
- LEX *lex= Lex;
- lex->first_select_lex()->db= $1;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = DB_ACLS & ~GRANT_ACL;
- else if (unlikely(lex->columns.elements))
- my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
- }
- | '*' '.' '*'
- {
- LEX *lex= Lex;
- lex->first_select_lex()->db= null_clex_str;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant= GLOBAL_ACLS & ~GRANT_ACL;
- else if (unlikely(lex->columns.elements))
- my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0)));
- }
- | table_ident
- {
- LEX *lex=Lex;
- if (unlikely(!lex->first_select_lex()->
- add_table_to_list(thd, $1,NULL,
- TL_OPTION_UPDATING)))
- MYSQL_YYABORT;
- if (lex->grant == GLOBAL_ACLS)
- lex->grant = TABLE_ACLS & ~GRANT_ACL;
- }
- ;
-
-user_list:
- user
- {
- if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | user_list ',' user
- {
- if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-grant_list:
- grant_user
- {
- if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | grant_list ',' grant_user
- {
- if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-user_and_role_list:
- user_or_role
- {
- if (unlikely(Lex->users_list.push_back($1, thd->mem_root)))
- MYSQL_YYABORT;
- }
- | user_and_role_list ',' user_or_role
- {
- if (unlikely(Lex->users_list.push_back($3, thd->mem_root)))
- MYSQL_YYABORT;
- }
- ;
-
-via_or_with: VIA_SYM | WITH ;
-using_or_as: USING | AS ;
-
-grant_user:
- user IDENTIFIED_SYM BY TEXT_STRING
- {
- $$= $1;
- $1->auth= new (thd->mem_root) USER_AUTH();
- $1->auth->pwtext= $4;
- }
- | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING
- {
- $$= $1;
- $1->auth= new (thd->mem_root) USER_AUTH();
- $1->auth->auth_str= $5;
- }
- | user IDENTIFIED_SYM via_or_with auth_expression
- {
- $$= $1;
- $1->auth= $4;
- }
- | user_or_role
- {
- $$= $1;
- }
- ;
-
-auth_expression:
- auth_token OR_SYM auth_expression
- {
- $$= $1;
- DBUG_ASSERT($$->next == NULL);
- $$->next= $3;
- }
- | auth_token
- {
- $$= $1;
- }
- ;
-
-auth_token:
- ident_or_text opt_auth_str
- {
- $$= $2;
- $$->plugin= $1;
- }
- ;
-
-opt_auth_str:
- /* empty */
- {
- if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
- MYSQL_YYABORT;
- }
- | using_or_as TEXT_STRING_sys
- {
- if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
- MYSQL_YYABORT;
- $$->auth_str= $2;
- }
- | using_or_as PASSWORD_SYM '(' TEXT_STRING ')'
- {
- if (!($$=(USER_AUTH*) thd->calloc(sizeof(USER_AUTH))))
- MYSQL_YYABORT;
- $$->pwtext= $4;
- }
- ;
-
-opt_column_list:
- /* empty */
- {
- LEX *lex=Lex;
- lex->grant |= lex->which_columns;
- }
- | '(' column_list ')' { }
- ;
-
-column_list:
- column_list ',' column_list_id
- | column_list_id
- ;
-
-column_list_id:
- ident
- {
- String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info);
- if (unlikely(new_str == NULL))
- MYSQL_YYABORT;
- List_iterator <LEX_COLUMN> iter(Lex->columns);
- class LEX_COLUMN *point;
- LEX *lex=Lex;
- while ((point=iter++))
- {
- if (!my_strcasecmp(system_charset_info,
- point->column.c_ptr(), new_str->c_ptr()))
- break;
- }
- lex->grant_tot_col|= lex->which_columns;
- if (point)
- point->rights |= lex->which_columns;
- else
- {
- LEX_COLUMN *col= (new (thd->mem_root)
- LEX_COLUMN(*new_str,lex->which_columns));
- if (unlikely(col == NULL))
- MYSQL_YYABORT;
- lex->columns.push_back(col, thd->mem_root);
- }
- }
- ;
-
-opt_require_clause:
- /* empty */
- | REQUIRE_SYM require_list
- {
- Lex->account_options.ssl_type= SSL_TYPE_SPECIFIED;
- }
- | REQUIRE_SYM SSL_SYM
- {
- Lex->account_options.ssl_type= SSL_TYPE_ANY;
- }
- | REQUIRE_SYM X509_SYM
- {
- Lex->account_options.ssl_type= SSL_TYPE_X509;
- }
- | REQUIRE_SYM NONE_SYM
- {
- Lex->account_options.ssl_type= SSL_TYPE_NONE;
- }
- ;
-
-resource_option:
- MAX_QUERIES_PER_HOUR ulong_num
- {
- Lex->account_options.questions=$2;
- Lex->account_options.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
- }
- | MAX_UPDATES_PER_HOUR ulong_num
- {
- Lex->account_options.updates=$2;
- Lex->account_options.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
- }
- | MAX_CONNECTIONS_PER_HOUR ulong_num
- {
- Lex->account_options.conn_per_hour= $2;
- Lex->account_options.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
- }
- | MAX_USER_CONNECTIONS_SYM int_num
- {
- Lex->account_options.user_conn= $2;
- Lex->account_options.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
- }
- | MAX_STATEMENT_TIME_SYM NUM_literal
- {
- Lex->account_options.max_statement_time= $2->val_real();
- Lex->account_options.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME;
- }
- ;
-
-resource_option_list:
- resource_option_list resource_option {}
- | resource_option {}
- ;
-
-opt_resource_options:
- /* empty */ {}
- | WITH resource_option_list
- ;
-
-
-opt_grant_options:
- /* empty */ {}
- | WITH grant_option_list {}
- ;
-
-opt_grant_option:
- /* empty */ {}
- | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
- ;
-
-grant_option_list:
- grant_option_list grant_option {}
- | grant_option {}
- ;
-
-grant_option:
- GRANT OPTION { Lex->grant |= GRANT_ACL;}
- | resource_option {}
- ;
-
-begin_stmt_mariadb:
- BEGIN_MARIADB_SYM
- {
- LEX *lex=Lex;
- lex->sql_command = SQLCOM_BEGIN;
- lex->start_transaction_opt= 0;
- }
- opt_work {}
- ;
-
-compound_statement:
- sp_proc_stmt_compound_ok
- {
- Lex->sql_command= SQLCOM_COMPOUND;
- if (Lex->sp_body_finalize_procedure(thd))
- MYSQL_YYABORT;
- }
- ;
-
-opt_not:
- /* nothing */ { $$= 0; }
- | not { $$= 1; }
- ;
-
-opt_work:
- /* empty */ {}
- | WORK_SYM {}
- ;
-
-opt_chain:
- /* empty */
- { $$= TVL_UNKNOWN; }
- | AND_SYM NO_SYM CHAIN_SYM { $$= TVL_NO; }
- | AND_SYM CHAIN_SYM { $$= TVL_YES; }
- ;
-
-opt_release:
- /* empty */
- { $$= TVL_UNKNOWN; }
- | RELEASE_SYM { $$= TVL_YES; }
- | NO_SYM RELEASE_SYM { $$= TVL_NO; }
- ;
-
-commit:
- COMMIT_SYM opt_work opt_chain opt_release
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_COMMIT;
- /* Don't allow AND CHAIN RELEASE. */
- MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES);
- lex->tx_chain= $3;
- lex->tx_release= $4;
- }
- ;
-
-rollback:
- ROLLBACK_SYM opt_work opt_chain opt_release
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ROLLBACK;
- /* Don't allow AND CHAIN RELEASE. */
- MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES);
- lex->tx_chain= $3;
- lex->tx_release= $4;
- }
- | ROLLBACK_SYM opt_work TO_SYM SAVEPOINT_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT;
- lex->ident= $5;
- }
- | ROLLBACK_SYM opt_work TO_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT;
- lex->ident= $4;
- }
- ;
-
-savepoint:
- SAVEPOINT_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SAVEPOINT;
- lex->ident= $2;
- }
- ;
-
-release:
- RELEASE_SYM SAVEPOINT_SYM ident
- {
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_RELEASE_SAVEPOINT;
- lex->ident= $3;
- }
- ;
-
-/*
- UNIONS : glue selects together
-*/
-
-unit_type_decl:
- UNION_SYM union_option
- { $$.unit_type= UNION_TYPE; $$.distinct= $2; }
- | INTERSECT_SYM
- { $$.unit_type= INTERSECT_TYPE; $$.distinct= 1; }
- | EXCEPT_SYM
- { $$.unit_type= EXCEPT_TYPE; $$.distinct= 1; }
- ;
-
-/*
- Start a UNION, for non-top level query expressions.
-*/
-union_option:
- /* empty */ { $$=1; }
- | DISTINCT { $$=1; }
- | ALL { $$=0; }
- ;
-
-query_expression_option:
- STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; }
- | HIGH_PRIORITY
- {
- YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
- YYPS->m_mdl_type= MDL_SHARED_READ;
- Select->options|= SELECT_HIGH_PRIORITY;
- }
- | DISTINCT { Select->options|= SELECT_DISTINCT; }
- | UNIQUE_SYM { Select->options|= SELECT_DISTINCT; }
- | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; }
- | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; }
- | SQL_BUFFER_RESULT { Select->options|= OPTION_BUFFER_RESULT; }
- | SQL_CALC_FOUND_ROWS { Select->options|= OPTION_FOUND_ROWS; }
- | ALL { Select->options|= SELECT_ALL; }
- ;
-
-/**************************************************************************
-
- DEFINER clause support.
-
-**************************************************************************/
-
-definer_opt:
- no_definer
- | definer
- ;
-
-no_definer:
- /* empty */
- {
- /*
- We have to distinguish missing DEFINER-clause from case when
- CURRENT_USER specified as definer explicitly in order to properly
- handle CREATE TRIGGER statements which come to replication thread
- from older master servers (i.e. to create non-suid trigger in this
- case).
- */
- thd->lex->definer= 0;
- }
- ;
-
-definer:
- DEFINER_SYM '=' user_or_role
- {
- Lex->definer= $3;
- Lex->account_options.reset();
- }
- ;
-
-/**************************************************************************
-
- CREATE VIEW statement parts.
-
-**************************************************************************/
-
-view_algorithm:
- ALGORITHM_SYM '=' UNDEFINED_SYM { $$= DTYPE_ALGORITHM_UNDEFINED; }
- | ALGORITHM_SYM '=' MERGE_SYM { $$= VIEW_ALGORITHM_MERGE; }
- | ALGORITHM_SYM '=' TEMPTABLE_SYM { $$= VIEW_ALGORITHM_TMPTABLE; }
- ;
-
-opt_view_suid:
- /* empty */ { $$= VIEW_SUID_DEFAULT; }
- | view_suid { $$= $1; }
- ;
-
-view_suid:
- SQL_SYM SECURITY_SYM DEFINER_SYM { $$= VIEW_SUID_DEFINER; }
- | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= VIEW_SUID_INVOKER; }
- ;
-
-view_list_opt:
- /* empty */
- {}
- | '(' view_list ')' { }
- ;
-
-view_list:
- ident
- {
- Lex->view_list.push_back((LEX_CSTRING*)
- thd->memdup(&$1, sizeof(LEX_CSTRING)),
- thd->mem_root);
- }
- | view_list ',' ident
- {
- Lex->view_list.push_back((LEX_CSTRING*)
- thd->memdup(&$3, sizeof(LEX_CSTRING)),
- thd->mem_root);
- }
- ;
-
-view_select:
- {
- LEX *lex= Lex;
- lex->parsing_options.allows_variable= FALSE;
- lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr();
- }
- query_expression
- view_check_option
- {
- if (Lex->parsed_create_view($2, $3))
- MYSQL_YYABORT;
- }
- ;
-
-view_check_option:
- /* empty */ { $$= VIEW_CHECK_NONE; }
- | WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; }
- | WITH CASCADED CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; }
- | WITH LOCAL_SYM CHECK_SYM OPTION { $$= VIEW_CHECK_LOCAL; }
- ;
-
-/**************************************************************************
-
- CREATE TRIGGER statement parts.
-
-**************************************************************************/
-
-trigger_action_order:
- FOLLOWS_SYM
- { $$= TRG_ORDER_FOLLOWS; }
- | PRECEDES_SYM
- { $$= TRG_ORDER_PRECEDES; }
- ;
-
-trigger_follows_precedes_clause:
- /* empty */
- {
- $$.ordering_clause= TRG_ORDER_NONE;
- $$.anchor_trigger_name.str= NULL;
- $$.anchor_trigger_name.length= 0;
- }
- |
- trigger_action_order ident_or_text
- {
- $$.ordering_clause= $1;
- $$.anchor_trigger_name= $2;
- }
- ;
-
-trigger_tail:
- remember_name
- opt_if_not_exists
- {
- if (unlikely(Lex->add_create_options_with_check($2)))
- MYSQL_YYABORT;
- }
- sp_name
- trg_action_time
- trg_event
- ON
- remember_name /* $8 */
- { /* $9 */
- Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start();
- }
- table_ident /* $10 */
- FOR_SYM
- remember_name /* $12 */
- { /* $13 */
- Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start();
- }
- EACH_SYM
- ROW_SYM
- {
- Lex->trg_chistics.ordering_clause_begin= YYLIP->get_cpp_ptr();
- }
- trigger_follows_precedes_clause /* $17 */
- { /* $18 */
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (unlikely(lex->sphead))
- my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"));
-
- lex->stmt_definition_begin= $1;
- lex->ident.str= $8;
- lex->ident.length= $12 - $8;
- lex->spname= $4;
- (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17);
- lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr();
-
- if (unlikely(!lex->make_sp_head(thd, $4, &sp_handler_trigger,
- DEFAULT_AGGREGATE)))
- MYSQL_YYABORT;
-
- lex->sphead->set_body_start(thd, lip->get_cpp_tok_start());
- }
- sp_proc_stmt /* $19 */
- { /* $20 */
- LEX *lex= Lex;
-
- lex->sql_command= SQLCOM_CREATE_TRIGGER;
- if (lex->sp_body_finalize_trigger(thd))
- MYSQL_YYABORT;
-
- /*
- We have to do it after parsing trigger body, because some of
- sp_proc_stmt alternatives are not saving/restoring LEX, so
- lex->query_tables can be wiped out.
- */
- if (!lex->first_select_lex()->
- add_table_to_list(thd, $10, (LEX_CSTRING*) 0,
- TL_OPTION_UPDATING, TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE))
- MYSQL_YYABORT;
- }
- ;
-
-/**************************************************************************
-
- CREATE FUNCTION | PROCEDURE statements parts.
-
-**************************************************************************/
-
-sf_return_type:
- {
- LEX *lex= Lex;
- lex->init_last_field(&lex->sphead->m_return_field_def,
- &empty_clex_str,
- thd->variables.collation_database);
- }
- sp_param_type_with_opt_collate
- {
- if (unlikely(Lex->sphead->fill_field_definition(thd,
- Lex->last_field)))
- MYSQL_YYABORT;
- }
- ;
-
-sf_c_chistics_and_body_standalone:
- sp_c_chistics
- {
- LEX *lex= thd->lex;
- lex->sphead->set_c_chistics(lex->sp_chistics);
- lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
- }
- sp_tail_is
- sp_body
- {
- if (unlikely(Lex->sp_body_finalize_function(thd)))
- MYSQL_YYABORT;
- }
- ;
-
-sp_tail_standalone:
- sp_name
- {
- if (unlikely(!Lex->make_sp_head_no_recursive(thd, $1,
- &sp_handler_procedure,
- DEFAULT_AGGREGATE)))
- MYSQL_YYABORT;
- }
- opt_sp_parenthesized_pdparam_list
- sp_c_chistics
- {
- Lex->sphead->set_c_chistics(Lex->sp_chistics);
- Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start());
- }
- sp_tail_is
- sp_body
- opt_sp_name
- {
- if (unlikely(Lex->sp_body_finalize_procedure_standalone(thd, $8)))
- MYSQL_YYABORT;
- }
- ;
-
-
-opt_package_routine_end_name:
- /* Empty */ { $$= null_clex_str; }
- | ident { $$= $1; }
- ;
-
-sp_tail_is:
- IS
- | AS
- ;
-
-/*************************************************************************/
-
-xa:
- XA_SYM begin_or_start xid opt_join_or_resume
- {
- Lex->sql_command = SQLCOM_XA_START;
- }
- | XA_SYM END xid opt_suspend
- {
- Lex->sql_command = SQLCOM_XA_END;
- }
- | XA_SYM PREPARE_SYM xid
- {
- Lex->sql_command = SQLCOM_XA_PREPARE;
- }
- | XA_SYM COMMIT_SYM xid opt_one_phase
- {
- Lex->sql_command = SQLCOM_XA_COMMIT;
- }
- | XA_SYM ROLLBACK_SYM xid
- {
- Lex->sql_command = SQLCOM_XA_ROLLBACK;
- }
- | XA_SYM RECOVER_SYM opt_format_xid
- {
- Lex->sql_command = SQLCOM_XA_RECOVER;
- Lex->verbose= $3;
- }
- ;
-
-opt_format_xid:
- /* empty */ { $$= false; }
- | FORMAT_SYM '=' ident_or_text
- {
- if (lex_string_eq(&$3, STRING_WITH_LEN("SQL")))
- $$= true;
- else if (lex_string_eq(&$3, STRING_WITH_LEN("RAW")))
- $$= false;
- else
- {
- my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0),
- "XA RECOVER", $3.str));
- $$= false;
- }
- }
- ;
-
-xid:
- text_string
- {
- MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE);
- if (unlikely(!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))))
- MYSQL_YYABORT;
- Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0);
- }
- | text_string ',' text_string
- {
- MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE);
- if (unlikely(!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))))
- MYSQL_YYABORT;
- Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length());
- }
- | text_string ',' text_string ',' ulong_num
- {
- MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE);
- if (unlikely(!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))))
- MYSQL_YYABORT;
- Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length());
- }
- ;
-
-begin_or_start:
- BEGIN_MARIADB_SYM {}
- | BEGIN_ORACLE_SYM {}
- | START_SYM {}
- ;
-
-opt_join_or_resume:
- /* nothing */ { Lex->xa_opt=XA_NONE; }
- | JOIN_SYM { Lex->xa_opt=XA_JOIN; }
- | RESUME_SYM { Lex->xa_opt=XA_RESUME; }
- ;
-
-opt_one_phase:
- /* nothing */ { Lex->xa_opt=XA_NONE; }
- | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; }
- ;
-
-opt_suspend:
- /* nothing */
- { Lex->xa_opt=XA_NONE; }
- | SUSPEND_SYM
- { Lex->xa_opt=XA_SUSPEND; }
- opt_migrate
- ;
-
-opt_migrate:
- /* nothing */ {}
- | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; }
- ;
-
-install:
- INSTALL_SYM PLUGIN_SYM opt_if_not_exists ident SONAME_SYM TEXT_STRING_sys
- {
- if (Lex->stmt_install_plugin($3, $4, $6))
- MYSQL_YYABORT;
- }
- | INSTALL_SYM SONAME_SYM TEXT_STRING_sys
- {
- Lex->stmt_install_plugin($3);
- }
- ;
-
-uninstall:
- UNINSTALL_SYM PLUGIN_SYM opt_if_exists ident
- {
- if (Lex->stmt_uninstall_plugin_by_name($3, $4))
- MYSQL_YYABORT;
- }
- | UNINSTALL_SYM SONAME_SYM opt_if_exists TEXT_STRING_sys
- {
- if (Lex->stmt_uninstall_plugin_by_soname($3, $4))
- MYSQL_YYABORT;
- }
- ;
-
-/* Avoid compiler warning from sql_yacc.cc where yyerrlab1 is not used */
-keep_gcc_happy:
- IMPOSSIBLE_ACTION
- {
- YYERROR;
- }
- ;
-
-/**
- @} (end of group Parser)
-*/
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 99ff9c50588..c488ab1afbb 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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
@@ -45,11 +46,12 @@
static const char field_separator=',';
-ulonglong find_set(TYPELIB *lib, const char *str, size_t length, CHARSET_INFO *cs,
+ulonglong find_set(const TYPELIB *lib,
+ const char *str, size_t length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning)
{
CHARSET_INFO *strip= cs ? cs : &my_charset_latin1;
- const char *end= str + strip->cset->lengthsp(strip, str, length);
+ const char *end= str + strip->lengthsp(str, length);
ulonglong found= 0;
*err_pos= 0; // No error yet
*err_len= 0;
@@ -67,8 +69,8 @@ ulonglong find_set(TYPELIB *lib, const char *str, size_t length, CHARSET_INFO *c
for ( ; pos < end; pos+= mblen)
{
my_wc_t wc;
- if ((mblen= cs->cset->mb_wc(cs, &wc, (const uchar *) pos,
- (const uchar *) end)) < 1)
+ if ((mblen= cs->mb_wc(&wc, (const uchar *) pos,
+ (const uchar *) end)) < 1)
mblen= 1; // Not to hang on a wrong multibyte sequence
if (wc == (my_wc_t) field_separator)
break;
@@ -169,8 +171,8 @@ uint find_type2(const TYPELIB *typelib, const char *x, size_t length,
for (pos=0 ; (j=typelib->type_names[pos]) ; pos++)
{
- if (!my_strnncoll(cs, (const uchar*) x, length,
- (const uchar*) j, typelib->type_lengths[pos]))
+ if (!cs->strnncoll(x, length,
+ j, typelib->type_lengths[pos]))
DBUG_RETURN(pos+1);
}
DBUG_PRINT("exit",("Couldn't find type"));
@@ -337,8 +339,8 @@ int find_string_in_array(LEX_CSTRING * const haystack, LEX_CSTRING * const needl
{
const LEX_CSTRING *pos;
for (pos= haystack; pos->str; pos++)
- if (!cs->coll->strnncollsp(cs, (uchar *) pos->str, pos->length,
- (uchar *) needle->str, needle->length))
+ if (!cs->strnncollsp(pos->str, pos->length,
+ needle->str, needle->length))
{
return (int)(pos - haystack);
}
diff --git a/sql/strfunc.h b/sql/strfunc.h
index d66d4c63444..b2b293e153f 100644
--- a/sql/strfunc.h
+++ b/sql/strfunc.h
@@ -18,7 +18,8 @@
typedef struct st_typelib TYPELIB;
-ulonglong find_set(TYPELIB *lib, const char *x, size_t length, CHARSET_INFO *cs,
+ulonglong find_set(const TYPELIB *lib,
+ const char *x, size_t length, CHARSET_INFO *cs,
char **err_pos, uint *err_len, bool *set_warning);
ulonglong find_set_from_flags(TYPELIB *lib, uint default_name,
ulonglong cur_set, ulonglong default_set,
diff --git a/sql/structs.h b/sql/structs.h
index c47e4802452..27acb9e200e 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -164,6 +164,7 @@ typedef struct st_key {
double actual_rec_per_key(uint i);
+ bool without_overlaps;
} KEY;
@@ -618,6 +619,8 @@ public:
m_handler= handler;
Lex_length_and_dec_st::operator=(length_and_dec);
}
+ void set_handler_length_flags(const Type_handler *handler, const char *length,
+ uint32 flags);
void set(const Type_handler *handler, const char *length)
{
set(handler, length, 0);
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 8e3b49bb6f7..64040243df0 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -45,8 +45,7 @@
#include "mysqld.h"
#include "lock.h"
#include "sql_time.h" // known_date_time_formats
-#include "sql_acl.h" // SUPER_ACL,
- // mysql_user_table_is_in_short_password_format
+#include "sql_acl.h" // 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
@@ -67,6 +66,9 @@
#include "semisync_slave.h"
#include <ssl_compat.h>
+#define PCRE2_STATIC 1 /* Important on Windows */
+#include "pcre2.h" /* pcre2 header file */
+
/*
The rule for this file: everything should be 'static'. When a sys_var
variable or a function from this file is - in very rare cases - needed
@@ -78,8 +80,7 @@
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
static Sys_var_mybool Sys_pfs_enabled(
- "performance_schema",
- "Enable the performance schema.",
+ "performance_schema", "Enable the performance schema.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_enabled),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
@@ -87,10 +88,9 @@ 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."
" Use 0 to disable, -1 for automated sizing.",
- PARSED_EARLY READ_ONLY
- GLOBAL_VAR(pfs_param.m_events_waits_history_long_sizing),
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_waits_history_long_sizing),
CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_events_waits_history_size(
"performance_schema_events_waits_history_size",
@@ -98,7 +98,7 @@ static Sys_var_long Sys_pfs_events_waits_history_size(
" 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(-1, 1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_cond_classes(
"performance_schema_max_cond_classes",
@@ -113,7 +113,23 @@ static Sys_var_long Sys_pfs_max_cond_instances(
" 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(-1, 1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_program_instances(
+ "performance_schema_max_program_instances",
+ "Maximum number of instrumented programs."
+ " Use 0 to disable, -1 for automated scaling.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_program_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_prepared_stmt_instances(
+ "performance_schema_max_prepared_statements_instances",
+ "Maximum number of instrumented prepared statements."
+ " Use 0 to disable, -1 for automated scaling.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_prepared_stmt_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_file_classes(
"performance_schema_max_file_classes",
@@ -135,7 +151,7 @@ static Sys_var_long Sys_pfs_max_file_instances(
" 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(-1, 1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_max_sockets(
"performance_schema_max_socket_instances",
@@ -143,16 +159,14 @@ static Sys_var_long Sys_pfs_max_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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), 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));
+ DEFAULT(PFS_MAX_SOCKET_CLASS), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_mutex_classes(
"performance_schema_max_mutex_classes",
@@ -167,7 +181,7 @@ static Sys_var_long Sys_pfs_max_mutex_instances(
" 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(-1, 100*1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_rwlock_classes(
"performance_schema_max_rwlock_classes",
@@ -182,7 +196,7 @@ static Sys_var_long Sys_pfs_max_rwlock_instances(
" 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(-1, 100*1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_max_table_handles(
"performance_schema_max_table_handles",
@@ -190,7 +204,7 @@ static Sys_var_long Sys_pfs_max_table_handles(
" 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(-1, 1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_max_table_instances(
"performance_schema_max_table_instances",
@@ -198,7 +212,23 @@ static Sys_var_long Sys_pfs_max_table_instances(
" 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(-1, 1024*1024),
- DEFAULT(-1), BLOCK_SIZE(1));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_table_lock_stat(
+ "performance_schema_max_table_lock_stat",
+ "Maximum number of lock statistics for instrumented tables."
+ " Use 0 to disable, -1 for automated scaling.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_lock_stat_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_index_stat(
+ "performance_schema_max_index_stat",
+ "Maximum number of index statistics for instrumented tables."
+ " Use 0 to disable, -1 for automated scaling.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_index_stat_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_thread_classes(
"performance_schema_max_thread_classes",
@@ -213,23 +243,21 @@ static Sys_var_long Sys_pfs_max_thread_instances(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_setup_actors_size(
+static Sys_var_long 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));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_setup_objects_size(
+static Sys_var_long 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_SETUP_OBJECT),
- BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_accounts_size(
"performance_schema_accounts_size",
@@ -237,8 +265,7 @@ static Sys_var_long Sys_pfs_accounts_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_hosts_size(
"performance_schema_hosts_size",
@@ -246,8 +273,7 @@ static Sys_var_long Sys_pfs_hosts_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_users_size(
"performance_schema_users_size",
@@ -255,16 +281,14 @@ static Sys_var_long Sys_pfs_users_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), 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));
+ 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",
@@ -272,8 +296,7 @@ static Sys_var_long Sys_pfs_events_stages_history_long_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_events_stages_history_size(
"performance_schema_events_stages_history_size",
@@ -281,26 +304,27 @@ static Sys_var_long Sys_pfs_events_stages_history_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
/**
Variable performance_schema_max_statement_classes.
The default number of statement classes is the sum of:
+ - SQLCOM_END for all regular "statement/sql/...",
+ - SP_PSI_STATEMENT_INFO_COUNT for "statement/sp/...".
- (COM_END - mariadb gap) 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.
+ - 1 for "statement/scheduler/event", for scheduled events.
*/
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 -(COM_MDB_GAP_END - COM_MDB_GAP_BEG + 1)) + 4),
+ DEFAULT((ulong) SQLCOM_END + SP_PSI_STATEMENT_INFO_COUNT +
+ (ulong) (COM_END -(COM_MDB_GAP_END - COM_MDB_GAP_BEG + 1)) + 5),
BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_events_statements_history_long_size(
@@ -309,8 +333,7 @@ static Sys_var_long Sys_pfs_events_statements_history_long_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_events_statements_history_size(
"performance_schema_events_statements_history_size",
@@ -318,25 +341,52 @@ static Sys_var_long Sys_pfs_events_statements_history_size(
" 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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_statement_stack_size(
+ "performance_schema_max_statement_stack",
+ "Number of rows per thread in EVENTS_STATEMENTS_CURRENT.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_statement_stack_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(1, 256),
+ DEFAULT(PFS_STATEMENTS_STACK_SIZE), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_memory_classes(
+ "performance_schema_max_memory_classes",
+ "Maximum number of memory pool instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_memory_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024),
+ DEFAULT(PFS_MAX_MEMORY_CLASS), 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, 1024 * 1024),
- DEFAULT(-1),
- BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_events_transactions_history_long_size(
+ "performance_schema_events_transactions_history_long_size",
+ "Number of rows in EVENTS_TRANSACTIONS_HISTORY_LONG."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_transactions_history_long_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(PFS_AUTOSIZE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_events_transactions_history_size(
+ "performance_schema_events_transactions_history_size",
+ "Number of rows per thread in EVENTS_TRANSACTIONS_HISTORY."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_transactions_history_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024),
+ DEFAULT(PFS_AUTOSIZE_VALUE), 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));
+ DEFAULT(1024), BLOCK_SIZE(1));
static Sys_var_long Sys_pfs_connect_attrs_size(
"performance_schema_session_connect_attrs_size",
@@ -345,8 +395,22 @@ static Sys_var_long Sys_pfs_connect_attrs_size(
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));
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_metadata_locks(
+ "performance_schema_max_metadata_locks",
+ "Maximum number of metadata locks."
+ " Use 0 to disable, -1 for automated scaling.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_metadata_lock_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 100*1024*1024),
+ DEFAULT(PFS_AUTOSCALE_VALUE), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_sql_text_length(
+ "performance_schema_max_sql_text_length",
+ "Maximum length of displayed sql text.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_max_sql_text_length),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024 * 1024),
+ DEFAULT(1024), BLOCK_SIZE(1));
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
@@ -437,16 +501,16 @@ static Sys_var_ulong Sys_back_log(
AUTO_SET READ_ONLY GLOBAL_VAR(back_log), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 65535), DEFAULT(150), BLOCK_SIZE(1));
-static Sys_var_charptr Sys_basedir(
+static Sys_var_charptr_fscs Sys_basedir(
"basedir", "Path to installation directory. All paths are "
"usually resolved relative to this",
READ_ONLY GLOBAL_VAR(mysql_home_ptr), CMD_LINE(REQUIRED_ARG, 'b'),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_my_bind_addr(
+static Sys_var_charptr_fscs Sys_my_bind_addr(
"bind_address", "IP address to bind to.",
READ_ONLY GLOBAL_VAR(my_bind_addr_str), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
const char *Sys_var_vers_asof::asof_keywords[]= {"DEFAULT", NULL};
static Sys_var_vers_asof Sys_vers_asof_timestamp(
@@ -462,7 +526,9 @@ static Sys_var_enum Sys_vers_alter_history(
SESSION_VAR(vers_alter_history), CMD_LINE(REQUIRED_ARG),
vers_alter_history_keywords, DEFAULT(VERS_ALTER_HISTORY_ERROR));
-static Sys_var_ulonglong Sys_binlog_cache_size(
+static Sys_var_on_access_global<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_CACHE_SIZE>
+Sys_binlog_cache_size(
"binlog_cache_size", "The size of the transactional cache for "
"updates to transactional engines for the binary log. "
"If you often use transactions containing many statements, "
@@ -471,14 +537,18 @@ static Sys_var_ulonglong Sys_binlog_cache_size(
CMD_LINE(REQUIRED_ARG),
VALID_RANGE(IO_SIZE, SIZE_T_MAX), DEFAULT(32768), BLOCK_SIZE(IO_SIZE));
-static Sys_var_ulonglong Sys_binlog_file_cache_size(
+static Sys_var_on_access_global<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_FILE_CACHE_SIZE>
+Sys_binlog_file_cache_size(
"binlog_file_cache_size",
"The size of file cache for the binary log",
GLOBAL_VAR(binlog_file_cache_size),
CMD_LINE(REQUIRED_ARG),
VALID_RANGE(IO_SIZE*2, SIZE_T_MAX), DEFAULT(IO_SIZE*4), BLOCK_SIZE(IO_SIZE));
-static Sys_var_ulonglong Sys_binlog_stmt_cache_size(
+static Sys_var_on_access_global<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_STMT_CACHE_SIZE>
+Sys_binlog_stmt_cache_size(
"binlog_stmt_cache_size", "The size of the statement cache for "
"updates to non-transactional engines for the binary log. "
"If you often use statements updating a great number of rows, "
@@ -520,7 +590,8 @@ bool check_has_super(sys_var *self, THD *thd, set_var *var)
{
DBUG_ASSERT(self->scope() != sys_var::GLOBAL);// don't abuse check_has_super()
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!(thd->security_ctx->master_access & SUPER_ACL))
+ if (!(thd->security_ctx->master_access &
+ PRIV_SET_RESTRICTED_SESSION_SYSTEM_VARIABLE))
{
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
return true;
@@ -536,9 +607,6 @@ static Sys_var_bit Sys_core_file("core_file", "write a core-file on crashes",
static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
{
- if (check_has_super(self, thd, var))
- return true;
-
/*
MariaDB Galera does not support STATEMENT or MIXED binlog format currently.
*/
@@ -609,7 +677,10 @@ static bool fix_binlog_format_after_update(sys_var *self, THD *thd,
return false;
}
-static Sys_var_enum Sys_binlog_format(
+static Sys_var_on_access<Sys_var_enum,
+ PRIV_SET_SYSTEM_VAR_BINLOG_FORMAT,
+ PRIV_SET_SYSTEM_VAR_BINLOG_FORMAT>
+Sys_binlog_format(
"binlog_format", "What form of binary logging the master will "
"use: either ROW for row-based binary logging, STATEMENT "
"for statement-based binary logging, or MIXED. MIXED is statement-"
@@ -624,9 +695,6 @@ static Sys_var_enum Sys_binlog_format(
static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var)
{
- if (check_has_super(self, thd, var))
- return true;
-
if (var->type == OPT_GLOBAL)
return false;
@@ -638,7 +706,10 @@ static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var)
return false;
}
-static Sys_var_mybool Sys_binlog_direct(
+static Sys_var_on_access<Sys_var_mybool,
+ PRIV_SET_SYSTEM_VAR_BINLOG_DIRECT_NON_TRANSACTIONAL_UPDATES,
+ PRIV_SET_SYSTEM_VAR_BINLOG_DIRECT_NON_TRANSACTIONAL_UPDATES>
+Sys_binlog_direct(
"binlog_direct_non_transactional_updates",
"Causes updates to non-transactional engines using statement format to "
"be written directly to binary log. Before using this option make sure "
@@ -665,10 +736,10 @@ static Sys_var_ulonglong Sys_bulk_insert_buff_size(
SESSION_VAR(bulk_insert_buff_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, SIZE_T_MAX), DEFAULT(8192*1024), BLOCK_SIZE(1));
-static Sys_var_charptr Sys_character_sets_dir(
+static Sys_var_charptr_fscs Sys_character_sets_dir(
"character_sets_dir", "Directory where character sets are",
READ_ONLY GLOBAL_VAR(charsets_dir), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static bool check_not_null(sys_var *self, THD *thd, set_var *var)
{
@@ -914,24 +985,26 @@ static Sys_var_enum Sys_concurrent_insert(
GLOBAL_VAR(myisam_concurrent_insert), CMD_LINE(OPT_ARG),
concurrent_insert_names, DEFAULT(1));
-static Sys_var_ulong Sys_connect_timeout(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_CONNECT_TIMEOUT>
+Sys_connect_timeout(
"connect_timeout",
"The number of seconds the mysqld server is waiting for a connect "
"packet before responding with 'Bad handshake'",
GLOBAL_VAR(connect_timeout), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(2, LONG_TIMEOUT), DEFAULT(CONNECT_TIMEOUT), BLOCK_SIZE(1));
-static Sys_var_charptr Sys_datadir(
+static Sys_var_charptr_fscs Sys_datadir(
"datadir", "Path to the database root directory",
READ_ONLY GLOBAL_VAR(mysql_real_data_home_ptr),
- CMD_LINE(REQUIRED_ARG, 'h'), IN_FS_CHARSET, DEFAULT(mysql_real_data_home));
+ CMD_LINE(REQUIRED_ARG, 'h'), DEFAULT(mysql_real_data_home));
#ifndef DBUG_OFF
static Sys_var_dbug Sys_dbug(
"debug", "Built-in DBUG debugger", sys_var::SESSION,
CMD_LINE(OPT_ARG, '#'), DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_has_super), ON_UPDATE(0),
- DEPRECATED("'@@debug_dbug'"));
+ DEPRECATED("'@@debug_dbug'")); // since 5.5.37
static Sys_var_dbug Sys_debug_dbug(
"debug_dbug", "Built-in DBUG debugger", sys_var::SESSION,
@@ -1083,7 +1156,9 @@ static Sys_var_enum Sys_event_scheduler(
ON_CHECK(event_scheduler_check), ON_UPDATE(event_scheduler_update));
#endif
-static Sys_var_ulong Sys_expire_logs_days(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_EXPIRE_LOGS_DAYS>
+Sys_expire_logs_days(
"expire_logs_days",
"If non-zero, binary logs will be purged after expire_logs_days "
"days; possible purges happen at startup and at binary log rotation",
@@ -1120,7 +1195,7 @@ static Sys_var_charptr Sys_ft_boolean_syntax(
"ft_boolean_syntax", "List of operators for "
"MATCH ... AGAINST ( ... IN BOOLEAN MODE)",
GLOBAL_VAR(ft_boolean_syntax),
- CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ CMD_LINE(REQUIRED_ARG),
DEFAULT(DEFAULT_FTB_SYNTAX), NO_MUTEX_GUARD,
NOT_IN_BINLOG, ON_CHECK(check_ftb_syntax), ON_UPDATE(query_cache_flush));
@@ -1147,11 +1222,11 @@ static Sys_var_ulong Sys_ft_query_expansion_limit(
CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1000), DEFAULT(20), BLOCK_SIZE(1));
-static Sys_var_charptr Sys_ft_stopword_file(
+static Sys_var_charptr_fscs Sys_ft_stopword_file(
"ft_stopword_file",
"Use stopwords from this file instead of built-in list",
READ_ONLY GLOBAL_VAR(ft_stopword_file), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_mybool Sys_ignore_builtin_innodb(
"ignore_builtin_innodb",
@@ -1169,10 +1244,13 @@ static bool check_init_string(sys_var *self, THD *thd, set_var *var)
return false;
}
static PolyLock_rwlock PLock_sys_init_connect(&LOCK_sys_init_connect);
-static Sys_var_lexstring Sys_init_connect(
+
+static Sys_var_on_access_global<Sys_var_lexstring,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_INIT_CONNECT>
+Sys_init_connect(
"init_connect", "Command(s) that are executed for each "
"new connection (unless the user has SUPER privilege)",
- GLOBAL_VAR(opt_init_connect), CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ GLOBAL_VAR(opt_init_connect), CMD_LINE(REQUIRED_ARG),
DEFAULT(""), &PLock_sys_init_connect, NOT_IN_BINLOG,
ON_CHECK(check_init_string));
@@ -1192,11 +1270,11 @@ 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,
+ NO_CMD_LINE,
DEFAULT(""), MAX_CONNECTION_NAME, ON_CHECK(check_master_connection));
#endif
-static Sys_var_charptr Sys_init_file(
+static Sys_var_charptr_fscs Sys_init_file(
"init_file", "Read SQL commands from this file at startup",
READ_ONLY GLOBAL_VAR(opt_init_file),
#ifdef DISABLE_GRANT_OPTIONS
@@ -1204,13 +1282,15 @@ static Sys_var_charptr Sys_init_file(
#else
CMD_LINE(REQUIRED_ARG),
#endif
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static PolyLock_rwlock PLock_sys_init_slave(&LOCK_sys_init_slave);
-static Sys_var_lexstring Sys_init_slave(
+static Sys_var_on_access_global<Sys_var_lexstring,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_INIT_SLAVE>
+Sys_init_slave(
"init_slave", "Command(s) that are executed by a slave server "
"each time the SQL thread starts", GLOBAL_VAR(opt_init_slave),
- CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ CMD_LINE(REQUIRED_ARG),
DEFAULT(""), &PLock_sys_init_slave,
NOT_IN_BINLOG, ON_CHECK(check_init_string));
@@ -1287,19 +1367,22 @@ static Sys_var_mybool Sys_large_files_support(
static Sys_var_uint Sys_large_page_size(
"large_page_size",
- "If large page support is enabled, this shows the size of memory pages",
+ "Previously showed the size of large memory pages, unused since "
+ "multiple page size support was added",
READ_ONLY GLOBAL_VAR(opt_large_page_size), NO_CMD_LINE,
- VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED(""));
static Sys_var_mybool Sys_large_pages(
"large_pages", "Enable support for large pages",
READ_ONLY GLOBAL_VAR(opt_large_pages),
- IF_WIN(NO_CMD_LINE, CMD_LINE(OPT_ARG)), DEFAULT(FALSE));
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-static Sys_var_charptr Sys_language(
+static Sys_var_charptr_fscs Sys_language(
"lc_messages_dir", "Directory where error messages are",
READ_ONLY GLOBAL_VAR(lc_messages_dir_ptr), CMD_LINE(REQUIRED_ARG, 'L'),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_mybool Sys_local_infile(
"local_infile", "Enable LOAD DATA LOCAL INFILE",
@@ -1323,19 +1406,25 @@ static Sys_var_mybool Sys_log_bin(
"log_bin", "Whether the binary log is enabled",
READ_ONLY GLOBAL_VAR(opt_bin_log), NO_CMD_LINE, DEFAULT(FALSE));
-static Sys_var_mybool Sys_log_bin_compress(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_LOG_BIN_COMPRESS>
+Sys_log_bin_compress(
"log_bin_compress", "Whether the binary log can be compressed",
GLOBAL_VAR(opt_bin_log_compress), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
/* the min length is 10, means that Begin/Commit/Rollback would never be compressed! */
-static Sys_var_uint Sys_log_bin_compress_min_len(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_LOG_BIN_COMPRESS_MIN_LEN>
+Sys_log_bin_compress_min_len(
"log_bin_compress_min_len",
"Minimum length of sql statement(in statement mode) or record(in row mode)"
"that can be compressed.",
GLOBAL_VAR(opt_bin_log_compress_min_len),
CMD_LINE(OPT_ARG), VALID_RANGE(10, 1024), DEFAULT(256), BLOCK_SIZE(1));
-static Sys_var_mybool Sys_trust_function_creators(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_LOG_BIN_TRUST_FUNCTION_CREATORS>
+Sys_trust_function_creators(
"log_bin_trust_function_creators",
"If set to FALSE (the default), then when --log-bin is used, creation "
"of a stored function (or trigger) is allowed only to users having the "
@@ -1347,14 +1436,14 @@ static Sys_var_mybool Sys_trust_function_creators(
GLOBAL_VAR(trust_function_creators),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-static Sys_var_charptr Sys_log_error(
+static Sys_var_charptr_fscs Sys_log_error(
"log_error",
"Log errors to file (instead of stdout). If file name is not specified "
"then 'datadir'/'log-basename'.err or the 'pid-file' path with extension "
".err is used",
READ_ONLY GLOBAL_VAR(log_error_file_ptr),
CMD_LINE(OPT_ARG, OPT_LOG_ERROR),
- IN_FS_CHARSET, DEFAULT(disabled_my_option));
+ DEFAULT(disabled_my_option));
static Sys_var_bit Sys_log_queries_not_using_indexes(
"log_queries_not_using_indexes",
@@ -1509,36 +1598,41 @@ static Sys_var_ulong Sys_max_allowed_packet(
BLOCK_SIZE(1024), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_max_allowed_packet));
-static Sys_var_ulong Sys_slave_max_allowed_packet(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_MAX_ALLOWED_PACKET>
+Sys_slave_max_allowed_packet(
"slave_max_allowed_packet",
"The maximum packet length to sent successfully from the master to slave.",
GLOBAL_VAR(slave_max_allowed_packet), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1024, MAX_MAX_ALLOWED_PACKET),
- DEFAULT(MAX_MAX_ALLOWED_PACKET),
- BLOCK_SIZE(1024));
+ DEFAULT(MAX_MAX_ALLOWED_PACKET), BLOCK_SIZE(1024));
-static Sys_var_ulonglong Sys_max_binlog_cache_size(
+static Sys_var_on_access_global<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_BINLOG_CACHE_SIZE>
+Sys_max_binlog_cache_size(
"max_binlog_cache_size",
"Sets the total size of the transactional cache",
GLOBAL_VAR(max_binlog_cache_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(IO_SIZE, SIZE_T_MAX),
- DEFAULT((SIZE_T_MAX/IO_SIZE)*IO_SIZE),
- BLOCK_SIZE(IO_SIZE));
+ DEFAULT((SIZE_T_MAX/IO_SIZE)*IO_SIZE), BLOCK_SIZE(IO_SIZE));
-static Sys_var_ulonglong Sys_max_binlog_stmt_cache_size(
+static Sys_var_on_access_global<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_BINLOG_STMT_CACHE_SIZE>
+Sys_max_binlog_stmt_cache_size(
"max_binlog_stmt_cache_size",
"Sets the total size of the statement cache",
GLOBAL_VAR(max_binlog_stmt_cache_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(IO_SIZE, SIZE_T_MAX),
- DEFAULT((SIZE_T_MAX/IO_SIZE)*IO_SIZE),
- BLOCK_SIZE(IO_SIZE));
+ DEFAULT((SIZE_T_MAX/IO_SIZE)*IO_SIZE), BLOCK_SIZE(IO_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);
return false;
}
-static Sys_var_ulong Sys_max_binlog_size(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_BINLOG_SIZE>
+Sys_max_binlog_size(
"max_binlog_size",
"Binary log will be rotated automatically when the size exceeds this "
"value.",
@@ -1558,7 +1652,9 @@ static bool fix_max_connections(sys_var *self, THD *thd, enum_var_type type)
// Default max_connections of 151 is larger than Apache's default max
// children, to avoid "too many connections" error in a common setup
-static Sys_var_ulong Sys_max_connections(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_CONNECTIONS>
+Sys_max_connections(
"max_connections", "The number of simultaneous clients allowed",
PARSED_EARLY GLOBAL_VAR(max_connections), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(10, 100000),
@@ -1575,7 +1671,9 @@ static Sys_var_uint Sys_default_password_lifetime(
GLOBAL_VAR(default_password_lifetime), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
-static Sys_var_mybool Sys_disconnect_on_expired_password(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_DISCONNECT_ON_EXPIRED_PASSWORD>
+Sys_disconnect_on_expired_password(
"disconnect_on_expired_password",
"This variable controls how the server handles clients that are not "
"aware of the sandbox mode. If enabled, the server disconnects the "
@@ -1583,7 +1681,9 @@ static Sys_var_mybool Sys_disconnect_on_expired_password(
GLOBAL_VAR(disconnect_on_expired_password), CMD_LINE(OPT_ARG),
DEFAULT(FALSE));
-static Sys_var_ulong Sys_max_connect_errors(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_CONNECT_ERRORS>
+Sys_max_connect_errors(
"max_connect_errors",
"If there is more than this number of interrupted connections from "
"a host this host will be blocked from further connections",
@@ -1591,7 +1691,9 @@ static Sys_var_ulong Sys_max_connect_errors(
VALID_RANGE(1, UINT_MAX), DEFAULT(MAX_CONNECT_ERRORS),
BLOCK_SIZE(1));
-static Sys_var_uint Sys_max_password_errors(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MAX_PASSWORD_ERRORS>
+Sys_max_password_errors(
"max_password_errors",
"If there is more than this number of failed connect attempts "
"due to invalid password, user will be blocked from further connections until FLUSH_PRIVILEGES.",
@@ -1659,19 +1761,18 @@ static Sys_var_ulong Sys_metadata_locks_hash_instances(
VALID_RANGE(1, 1024), DEFAULT(8),
BLOCK_SIZE(1));
-static Sys_var_ulonglong Sys_pseudo_thread_id(
+static Sys_var_on_access_session<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_SESSION_VAR_PSEUDO_THREAD_ID>
+Sys_pseudo_thread_id(
"pseudo_thread_id",
"This variable is for internal server use",
SESSION_ONLY(pseudo_thread_id),
NO_CMD_LINE, VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0),
- BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
- ON_CHECK(check_has_super));
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG);
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,
@@ -1682,7 +1783,10 @@ check_gtid_domain_id(sys_var *self, THD *thd, set_var *var)
}
-static Sys_var_uint Sys_gtid_domain_id(
+static Sys_var_on_access<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_DOMAIN_ID,
+ PRIV_SET_SYSTEM_SESSION_VAR_GTID_DOMAIN_ID>
+Sys_gtid_domain_id(
"gtid_domain_id",
"Used with global transaction ID to identify logically independent "
"replication streams. When events can propagate through multiple "
@@ -1700,8 +1804,6 @@ 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 (unlikely(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)))
@@ -1719,7 +1821,9 @@ static bool check_gtid_seq_no(sys_var *self, THD *thd, set_var *var)
}
-static Sys_var_ulonglong Sys_gtid_seq_no(
+static Sys_var_on_access_session<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_SESSION_VAR_GTID_SEQ_NO>
+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 "
@@ -1883,7 +1987,9 @@ static Sys_var_gtid_slave_pos Sys_gtid_slave_pos(
GLOBAL_VAR(opt_gtid_slave_pos_dummy), NO_CMD_LINE);
-static Sys_var_mybool Sys_gtid_strict_mode(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_STRICT_MODE>
+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 "
@@ -1932,7 +2038,8 @@ Sys_var_gtid_binlog_state::do_check(THD *thd, set_var *var)
my_error(ER_INCORRECT_GTID_STATE, MYF(0));
return true;
}
- if (!(data= (gtid_binlog_state_data *)my_malloc(sizeof(*data), MYF(0))))
+ if (!(data= (gtid_binlog_state_data *)my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(*data), MYF(0))))
{
my_free(list);
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -2026,7 +2133,9 @@ Sys_var_last_gtid::session_value_ptr(THD *thd, const LEX_CSTRING *base)
}
-static Sys_var_uint Sys_gtid_cleanup_batch_size(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_CLEANUP_BATCH_SIZE>
+Sys_gtid_cleanup_batch_size(
"gtid_cleanup_batch_size",
"Normally does not need tuning. How many old rows must accumulate in "
"the mysql.gtid_slave_pos table before a background job will be run to "
@@ -2058,7 +2167,9 @@ fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
}
-static Sys_var_ulong Sys_slave_parallel_threads(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_THREADS>
+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 "
@@ -2071,7 +2182,9 @@ static Sys_var_ulong Sys_slave_parallel_threads(
ON_UPDATE(fix_slave_parallel_threads));
/* Alias for @@slave_parallel_threads to match what MySQL 5.7 uses. */
-static Sys_var_ulong Sys_slave_parallel_workers(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_WORKERS>
+Sys_slave_parallel_workers(
"slave_parallel_workers",
"Alias for slave_parallel_threads",
GLOBAL_VAR(opt_slave_parallel_threads), CMD_LINE(REQUIRED_ARG),
@@ -2099,7 +2212,9 @@ fix_slave_domain_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
}
-static Sys_var_ulong Sys_slave_domain_parallel_threads(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_DOMAIN_PARALLEL_THREADS>
+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 "
@@ -2113,7 +2228,9 @@ static Sys_var_ulong Sys_slave_domain_parallel_threads(
ON_UPDATE(fix_slave_domain_parallel_threads));
-static Sys_var_ulong Sys_slave_parallel_max_queued(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_MAX_QUEUED>
+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 "
@@ -2210,7 +2327,9 @@ export TYPELIB slave_parallel_mode_typelib = {
NULL
};
-static Sys_var_slave_parallel_mode Sys_slave_parallel_mode(
+static Sys_var_on_access_global<Sys_var_slave_parallel_mode,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_PARALLEL_MODE>
+Sys_slave_parallel_mode(
"slave_parallel_mode",
"Controls what transactions are applied in parallel when using "
"--slave-parallel-threads. Possible values: \"optimistic\" tries to "
@@ -2221,7 +2340,7 @@ static Sys_var_slave_parallel_mode Sys_slave_parallel_mode(
"\"minimal\" only parallelizes the commit steps of transactions. "
"\"none\" disables parallel apply completely.",
GLOBAL_VAR(opt_slave_parallel_mode), NO_CMD_LINE,
- slave_parallel_mode_names, DEFAULT(SLAVE_PARALLEL_CONSERVATIVE));
+ slave_parallel_mode_names, DEFAULT(SLAVE_PARALLEL_OPTIMISTIC));
static Sys_var_bit Sys_skip_parallel_replication(
@@ -2254,7 +2373,9 @@ fix_gtid_ignore_duplicates(sys_var *self, THD *thd, enum_var_type type)
}
-static Sys_var_mybool Sys_gtid_ignore_duplicates(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_IGNORE_DUPLICATES>
+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 "
@@ -2270,7 +2391,9 @@ static Sys_var_mybool Sys_gtid_ignore_duplicates(
#endif
-static Sys_var_ulong Sys_binlog_commit_wait_count(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_COMMIT_WAIT_COUNT>
+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 "
@@ -2281,7 +2404,9 @@ static Sys_var_ulong Sys_binlog_commit_wait_count(
VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_binlog_commit_wait_usec(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_COMMIT_WAIT_USEC>
+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 "
@@ -2320,18 +2445,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_ulong Sys_max_long_data_size(
- "max_long_data_size",
- "The maximum BLOB length to send to server from "
- "mysql_send_long_data API. Deprecated option; "
- "use max_allowed_packet instead.",
- READ_ONLY GLOBAL_VAR(max_long_data_size),
- CMD_LINE(REQUIRED_ARG, OPT_MAX_LONG_DATA_SIZE),
- VALID_RANGE(1024, UINT_MAX32), DEFAULT(1024*1024),
- BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(0), ON_UPDATE(0),
- DEPRECATED("'@@max_allowed_packet'"));
-
static PolyLock_mutex PLock_prepared_stmt_count(&LOCK_prepared_stmt_count);
static Sys_var_uint Sys_max_prepared_stmt_count(
"max_prepared_stmt_count",
@@ -2388,7 +2501,7 @@ static Sys_var_ulong Sys_max_tmp_tables(
SESSION_VAR(max_tmp_tables), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, UINT_MAX), DEFAULT(32), BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
- DEPRECATED(""));
+ DEPRECATED("")); // since 10.1.2
static Sys_var_ulong Sys_max_write_lock_count(
"max_write_lock_count",
@@ -2495,7 +2608,9 @@ static Sys_var_enum Sys_old_alter_table(
"old_alter_table", "Alias for alter_algorithm. "
"Deprecated. Use --alter-algorithm instead.",
SESSION_VAR(alter_algorithm), CMD_LINE(OPT_ARG),
- alter_algorithm_modes, DEFAULT(0));
+ alter_algorithm_modes, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("'@@alter_algorithm'")); // Since 10.5.1
static bool check_old_passwords(sys_var *self, THD *thd, set_var *var)
{
@@ -2598,6 +2713,7 @@ export const char *optimizer_switch_names[]=
"condition_pushdown_for_subquery",
"rowid_filter",
"condition_pushdown_from_having",
+ "not_null_range_scan",
"default",
NullS
};
@@ -2609,7 +2725,7 @@ static bool fix_optimizer_switch(sys_var *self, THD *thd,
push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
- "engine_condition_pushdown=on");
+ "engine_condition_pushdown=on"); // since 10.1.1
return false;
}
static bool check_legal_optimizer_switch(sys_var *self, THD *thd,
@@ -2648,15 +2764,15 @@ static Sys_var_ulong Sys_optimizer_trace_max_mem_size(
SESSION_VAR(optimizer_trace_max_mem_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, ULONG_MAX), DEFAULT(1024 * 1024), BLOCK_SIZE(1));
-static Sys_var_charptr Sys_pid_file(
+static Sys_var_charptr_fscs Sys_pid_file(
"pid_file", "Pid file used by safe_mysqld",
READ_ONLY GLOBAL_VAR(pidfile_name_ptr), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_plugin_dir(
+static Sys_var_charptr_fscs Sys_plugin_dir(
"plugin_dir", "Directory for plugins",
READ_ONLY GLOBAL_VAR(opt_plugin_dir_ptr), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_uint Sys_port(
"port",
@@ -2682,12 +2798,10 @@ static Sys_var_uint Sys_protocol_version(
VALID_RANGE(0, ~0U), DEFAULT(PROTOCOL_VERSION), BLOCK_SIZE(1));
static Sys_var_proxy_user Sys_proxy_user(
- "proxy_user", "The proxy user account name used when logging in",
- IN_SYSTEM_CHARSET);
+ "proxy_user", "The proxy user account name used when logging in");
static Sys_var_external_user Sys_exterenal_user(
- "external_user", "The external user account used when logging in",
- IN_SYSTEM_CHARSET);
+ "external_user", "The external user account used when logging in");
static Sys_var_ulong Sys_read_buff_size(
"read_buffer_size",
@@ -2781,7 +2895,9 @@ static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type)
transition (especially when transitioning from false to true) and
synchronizes both booleans in the end.
*/
-static Sys_var_mybool Sys_readonly(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_READ_ONLY>
+Sys_readonly(
"read_only",
"Make all non-temporary tables read-only, with the exception for "
"replication (slave) threads and users with the SUPER privilege",
@@ -2820,13 +2936,6 @@ static Sys_var_ulong Sys_range_alloc_block_size(
VALID_RANGE(RANGE_ALLOC_BLOCK_SIZE, UINT_MAX),
DEFAULT(RANGE_ALLOC_BLOCK_SIZE), BLOCK_SIZE(1024));
-static Sys_var_ulong Sys_multi_range_count(
- "multi_range_count", "Ignored. Use mrr_buffer_size instead",
- SESSION_VAR(multi_range_count), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, ULONG_MAX), DEFAULT(256), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
- DEPRECATED("'@@mrr_buffer_size'"));
-
static bool fix_thd_mem_root(sys_var *self, THD *thd, enum_var_type type)
{
if (type != OPT_GLOBAL)
@@ -2875,27 +2984,10 @@ static Sys_var_mybool Sys_skip_show_database(
READ_ONLY GLOBAL_VAR(opt_skip_show_db), CMD_LINE(OPT_ARG),
DEFAULT(FALSE));
-static Sys_var_charptr Sys_socket(
+static Sys_var_charptr_fscs Sys_socket(
"socket", "Socket file to use for connection",
READ_ONLY GLOBAL_VAR(mysqld_unix_port), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
-
-/*
- thread_concurrency is a no-op on all platforms since
- MySQL 5.1. It will be removed in the context of
- WL#5265
-*/
-static Sys_var_ulong Sys_thread_concurrency(
- "thread_concurrency",
- "Permits the application to give the threads system a hint for "
- "the desired number of threads that should be run at the same time."
- "This variable has no effect, and is deprecated. "
- "It will be removed in a future release.",
- READ_ONLY GLOBAL_VAR(concurrency),
- CMD_LINE(REQUIRED_ARG, OPT_THREAD_CONCURRENCY),
- VALID_RANGE(1, 512), DEFAULT(DEFAULT_CONCURRENCY), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
- DEPRECATED(""));
+ DEFAULT(0));
static Sys_var_ulonglong Sys_thread_stack(
"thread_stack", "The stack size for each thread",
@@ -2903,7 +2995,7 @@ static Sys_var_ulonglong Sys_thread_stack(
VALID_RANGE(128*1024, ULONGLONG_MAX), DEFAULT(DEFAULT_THREAD_STACK),
BLOCK_SIZE(1024));
-static Sys_var_charptr Sys_tmpdir(
+static Sys_var_charptr_fscs Sys_tmpdir(
"tmpdir", "Path for temporary files. Several paths may "
"be specified, separated by a "
#if defined(__WIN__)
@@ -2913,12 +3005,12 @@ static Sys_var_charptr Sys_tmpdir(
#endif
", in this case they are used in a round-robin fashion",
READ_ONLY GLOBAL_VAR(opt_mysql_tmpdir), CMD_LINE(REQUIRED_ARG, 't'),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static bool fix_trans_mem_root(sys_var *self, THD *thd, enum_var_type type)
{
if (type != OPT_GLOBAL)
- reset_root_defaults(&thd->transaction.mem_root,
+ reset_root_defaults(&thd->transaction->mem_root,
thd->variables.trans_alloc_block_size,
thd->variables.trans_prealloc_size);
return false;
@@ -3075,19 +3167,55 @@ static Sys_var_mybool Sys_query_cache_wlock_invalidate(
DEFAULT(FALSE));
#endif /* HAVE_QUERY_CACHE */
-static Sys_var_mybool Sys_secure_auth(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SECURE_AUTH>
+Sys_secure_auth(
"secure_auth",
"Disallow authentication for accounts that have old (pre-4.1) "
"passwords",
GLOBAL_VAR(opt_secure_auth), CMD_LINE(OPT_ARG),
DEFAULT(TRUE));
-static Sys_var_charptr Sys_secure_file_priv(
+static bool check_require_secure_transport(sys_var *self, THD *thd, set_var *var)
+{
+#ifndef _WIN32
+ /*
+ Always allow require_secure_transport to be enabled on
+ Linux, because it always has Unix domain sockets that are secure:
+ */
+ return false;
+#else
+ /*
+ Check SSL is enabled before turning require_secure_transport ON,
+ otherwise no connections will be allowed on Windows:
+ */
+ if (!var->save_result.ulonglong_value)
+ return false;
+ if (opt_use_ssl || opt_enable_named_pipe)
+ return false;
+ /* reject if SSL is disabled: */
+ my_error(ER_NO_SECURE_TRANSPORTS_CONFIGURED, MYF(0));
+ return true;
+#endif
+}
+
+static Sys_var_mybool Sys_require_secure_transport(
+ "require_secure_transport",
+ "When this option is enabled, connections attempted using insecure "
+ "transport will be rejected. Secure transports are SSL/TLS, "
+ "Unix sockets or named pipes.",
+ GLOBAL_VAR(opt_require_secure_transport),
+ CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_require_secure_transport), ON_UPDATE(0));
+
+static Sys_var_charptr_fscs Sys_secure_file_priv(
"secure_file_priv",
"Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files "
"within specified directory",
PREALLOCATED READ_ONLY GLOBAL_VAR(opt_secure_file_priv),
- CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(0));
+ CMD_LINE(REQUIRED_ARG), DEFAULT(0));
static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type)
{
@@ -3104,15 +3232,20 @@ static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type)
}
return false;
}
-static Sys_var_ulong Sys_server_id(
+static Sys_var_on_access<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SERVER_ID,
+ PRIV_SET_SYSTEM_SESSION_VAR_SERVER_ID>
+Sys_server_id(
"server_id",
"Uniquely identifies the server instance in the community of "
"replication partners",
SESSION_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
VALID_RANGE(1, UINT_MAX32), DEFAULT(1), BLOCK_SIZE(1), NO_MUTEX_GUARD,
- NOT_IN_BINLOG, ON_CHECK(check_has_super), ON_UPDATE(fix_server_id));
+ NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_server_id));
-static Sys_var_mybool Sys_slave_compressed_protocol(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_COMPRESSED_PROTOCOL>
+Sys_slave_compressed_protocol(
"slave_compressed_protocol",
"Use compression on master/slave protocol",
GLOBAL_VAR(opt_slave_compressed_protocol), CMD_LINE(OPT_ARG),
@@ -3120,7 +3253,9 @@ static Sys_var_mybool Sys_slave_compressed_protocol(
#ifdef HAVE_REPLICATION
static const char *slave_exec_mode_names[]= {"STRICT", "IDEMPOTENT", 0};
-static Sys_var_enum Slave_exec_mode(
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_EXEC_MODE>
+Slave_exec_mode(
"slave_exec_mode",
"How replication events should be executed. Legal values "
"are STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, "
@@ -3132,7 +3267,9 @@ static Sys_var_enum Slave_exec_mode(
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(
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_DDL_EXEC_MODE>
+Slave_ddl_exec_mode(
"slave_ddl_exec_mode",
"How replication events should be executed. Legal values "
"are STRICT and IDEMPOTENT (default). In IDEMPOTENT mode, "
@@ -3143,22 +3280,27 @@ static Sys_var_enum Slave_ddl_exec_mode(
slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT));
static const char *slave_run_triggers_for_rbr_names[]=
- {"NO", "YES", "LOGGING", 0};
-static Sys_var_enum Slave_run_triggers_for_rbr(
+ {"NO", "YES", "LOGGING", "ENFORCE", 0};
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_RUN_TRIGGERS_FOR_RBR>
+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 "
+ "executed. Legal values are NO (default), YES, LOGGING and ENFORCE. 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.",
+ "the binlog. ENFORCE means that triggers will always be run on the slave, "
+ "even if there are triggers on the master. ENFORCE implies LOGGING.",
GLOBAL_VAR(slave_run_triggers_for_rbr), CMD_LINE(REQUIRED_ARG),
slave_run_triggers_for_rbr_names,
DEFAULT(SLAVE_RUN_TRIGGERS_FOR_RBR_NO));
static const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0};
-static Sys_var_set Slave_type_conversions(
+static Sys_var_on_access_global<Sys_var_set,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_TYPE_CONVERSIONS>
+Slave_type_conversions(
"slave_type_conversions",
"Set of slave type conversions that are enabled."
" If the variable is empty, no conversions are"
@@ -3167,7 +3309,9 @@ static Sys_var_set Slave_type_conversions(
slave_type_conversions_name,
DEFAULT(0));
-static Sys_var_mybool Sys_slave_sql_verify_checksum(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_SQL_VERIFY_CHECKSUM>
+Sys_slave_sql_verify_checksum(
"slave_sql_verify_checksum",
"Force checksum verification of replication events after reading them "
"from relay log. Note: Events are always checksum-verified by slave on "
@@ -3175,7 +3319,9 @@ static Sys_var_mybool Sys_slave_sql_verify_checksum(
GLOBAL_VAR(opt_slave_sql_verify_checksum), CMD_LINE(OPT_ARG),
DEFAULT(TRUE));
-static Sys_var_mybool Sys_master_verify_checksum(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_MASTER_VERIFY_CHECKSUM>
+Sys_master_verify_checksum(
"master_verify_checksum",
"Force checksum verification of logged events in the binary log before "
"sending them to slaves or printing them in the output of "
@@ -3201,7 +3347,9 @@ Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var)
DBUG_RETURN(result);
}
-static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
+static Sys_var_on_access_global<Sys_var_replicate_events_marked_for_skip,
+ PRIV_SET_SYSTEM_GLOBAL_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 "
"@@skip_replication=1 on the master. Default REPLICATE (no events are "
@@ -3268,7 +3416,9 @@ static bool fix_rpl_semi_sync_master_wait_no_slave(sys_var *self, THD *thd,
return false;
}
-static Sys_var_mybool Sys_semisync_master_enabled(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_ENABLED>
+Sys_semisync_master_enabled(
"rpl_semi_sync_master_enabled",
"Enable semi-synchronous replication master (disabled by default).",
GLOBAL_VAR(rpl_semi_sync_master_enabled),
@@ -3276,7 +3426,9 @@ static Sys_var_mybool Sys_semisync_master_enabled(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_master_enabled));
-static Sys_var_ulong Sys_semisync_master_timeout(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_TIMEOUT>
+Sys_semisync_master_timeout(
"rpl_semi_sync_master_timeout",
"The timeout value (in ms) for semi-synchronous replication in the "
"master",
@@ -3286,7 +3438,9 @@ static Sys_var_ulong Sys_semisync_master_timeout(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_master_timeout));
-static Sys_var_mybool Sys_semisync_master_wait_no_slave(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_WAIT_NO_SLAVE>
+Sys_semisync_master_wait_no_slave(
"rpl_semi_sync_master_wait_no_slave",
"Wait until timeout when no semi-synchronous replication slave "
"available (enabled by default).",
@@ -3295,7 +3449,9 @@ static Sys_var_mybool Sys_semisync_master_wait_no_slave(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_master_wait_no_slave));
-static Sys_var_ulong Sys_semisync_master_trace_level(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_TRACE_LEVEL>
+Sys_semisync_master_trace_level(
"rpl_semi_sync_master_trace_level",
"The tracing level for semi-sync replication.",
GLOBAL_VAR(rpl_semi_sync_master_trace_level),
@@ -3307,7 +3463,9 @@ static Sys_var_ulong Sys_semisync_master_trace_level(
static const char *repl_semisync_wait_point[]=
{"AFTER_SYNC", "AFTER_COMMIT", NullS};
-static Sys_var_enum Sys_semisync_master_wait_point(
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_MASTER_WAIT_POINT>
+Sys_semisync_master_wait_point(
"rpl_semi_sync_master_wait_point",
"Should transaction wait for semi-sync ack after having synced binlog, "
"or after having committed in storage engine.",
@@ -3345,7 +3503,9 @@ static bool fix_rpl_semi_sync_slave_kill_conn_timeout(sys_var *self, THD *thd,
return false;
}
-static Sys_var_mybool Sys_semisync_slave_enabled(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_ENABLED>
+Sys_semisync_slave_enabled(
"rpl_semi_sync_slave_enabled",
"Enable semi-synchronous replication slave (disabled by default).",
GLOBAL_VAR(rpl_semi_sync_slave_enabled),
@@ -3353,7 +3513,9 @@ static Sys_var_mybool Sys_semisync_slave_enabled(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_slave_enabled));
-static Sys_var_ulong Sys_semisync_slave_trace_level(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_TRACE_LEVEL>
+Sys_semisync_slave_trace_level(
"rpl_semi_sync_slave_trace_level",
"The tracing level for semi-sync replication.",
GLOBAL_VAR(rpl_semi_sync_slave_trace_level),
@@ -3362,7 +3524,9 @@ static Sys_var_ulong Sys_semisync_slave_trace_level(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_slave_trace_level));
-static Sys_var_mybool Sys_semisync_slave_delay_master(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_DELAY_MASTER>
+Sys_semisync_slave_delay_master(
"rpl_semi_sync_slave_delay_master",
"Only write master info file when ack is needed.",
GLOBAL_VAR(rpl_semi_sync_slave_delay_master),
@@ -3370,7 +3534,9 @@ static Sys_var_mybool Sys_semisync_slave_delay_master(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_rpl_semi_sync_slave_delay_master));
-static Sys_var_uint Sys_semisync_slave_kill_conn_timeout(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RPL_SEMI_SYNC_SLAVE_KILL_CONN_TIMEOUT>
+Sys_semisync_slave_kill_conn_timeout(
"rpl_semi_sync_slave_kill_conn_timeout",
"Timeout for the mysql connection used to kill the slave io_thread's "
"connection on master. This timeout comes into play when stop slave "
@@ -3382,7 +3548,9 @@ static Sys_var_uint Sys_semisync_slave_kill_conn_timeout(
ON_UPDATE(fix_rpl_semi_sync_slave_kill_conn_timeout));
#endif /* HAVE_REPLICATION */
-static Sys_var_ulong Sys_slow_launch_time(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLOW_LAUNCH_TIME>
+Sys_slow_launch_time(
"slow_launch_time",
"If creating the thread takes longer than this value (in seconds), "
"the Slow_launch_threads counter will be incremented",
@@ -3401,7 +3569,7 @@ export sql_mode_t expand_sql_mode(sql_mode_t sql_mode)
if (sql_mode & MODE_ANSI)
{
/*
- Note that we dont set
+ Note that we don't set
MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | MODE_NO_FIELD_OPTIONS
to allow one to get full use of MySQL in this mode.
@@ -3545,44 +3713,44 @@ static Sys_var_set Sys_old_behavior(
#define SSL_OPT(X) NO_CMD_LINE
#endif
-static Sys_var_charptr Sys_ssl_ca(
+static Sys_var_charptr_fscs Sys_ssl_ca(
"ssl_ca",
"CA file in PEM format (check OpenSSL docs, implies --ssl)",
READ_ONLY GLOBAL_VAR(opt_ssl_ca), SSL_OPT(OPT_SSL_CA),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_ssl_capath(
+static Sys_var_charptr_fscs Sys_ssl_capath(
"ssl_capath",
"CA directory (check OpenSSL docs, implies --ssl)",
READ_ONLY GLOBAL_VAR(opt_ssl_capath), SSL_OPT(OPT_SSL_CAPATH),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_ssl_cert(
+static Sys_var_charptr_fscs Sys_ssl_cert(
"ssl_cert", "X509 cert in PEM format (implies --ssl)",
READ_ONLY GLOBAL_VAR(opt_ssl_cert), SSL_OPT(OPT_SSL_CERT),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_ssl_cipher(
+static Sys_var_charptr_fscs Sys_ssl_cipher(
"ssl_cipher", "SSL cipher to use (implies --ssl)",
READ_ONLY GLOBAL_VAR(opt_ssl_cipher), SSL_OPT(OPT_SSL_CIPHER),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_ssl_key(
+static Sys_var_charptr_fscs Sys_ssl_key(
"ssl_key", "X509 key in PEM format (implies --ssl)",
READ_ONLY GLOBAL_VAR(opt_ssl_key), SSL_OPT(OPT_SSL_KEY),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_ssl_crl(
+static Sys_var_charptr_fscs 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));
+ DEFAULT(0));
-static Sys_var_charptr Sys_ssl_crlpath(
+static Sys_var_charptr_fscs 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));
+ DEFAULT(0));
static const char *tls_version_names[]=
{
@@ -3636,7 +3804,7 @@ static Sys_var_charptr Sys_system_time_zone(
"system_time_zone", "The server system time zone",
READ_ONLY GLOBAL_VAR(system_time_zone_ptr),
CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(system_time_zone));
+ DEFAULT(system_time_zone));
/*
If One use views with prepared statements this should be bigger than
@@ -3725,7 +3893,9 @@ static bool fix_threadpool_stall_limit(sys_var*, THD*, enum_var_type)
}
#ifdef _WIN32
-static Sys_var_uint Sys_threadpool_min_threads(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_min_threads(
"thread_pool_min_threads",
"Minimum number of threads in the thread pool.",
GLOBAL_VAR(threadpool_min_threads), CMD_LINE(REQUIRED_ARG),
@@ -3735,7 +3905,9 @@ static Sys_var_uint Sys_threadpool_min_threads(
);
static const char *threadpool_mode_names[]={ "windows", "generic", 0 };
-static Sys_var_enum Sys_threadpool_mode(
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_mode(
"thread_pool_mode",
"Chose implementation of the threadpool",
READ_ONLY GLOBAL_VAR(threadpool_mode), CMD_LINE(REQUIRED_ARG),
@@ -3744,27 +3916,35 @@ static Sys_var_enum Sys_threadpool_mode(
#endif
static const char *threadpool_priority_names[]={ "high", "low", "auto", 0 };
-static Sys_var_enum Sys_thread_pool_priority(
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_thread_pool_priority(
"thread_pool_priority",
"Threadpool priority. High priority connections usually start executing earlier than low priority."
"If priority set to 'auto', the the actual priority(low or high) is determined based on whether or not connection is inside transaction.",
SESSION_VAR(threadpool_priority), CMD_LINE(REQUIRED_ARG),
threadpool_priority_names, DEFAULT(TP_PRIORITY_AUTO));
-static Sys_var_uint Sys_threadpool_idle_thread_timeout(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_idle_thread_timeout(
"thread_pool_idle_timeout",
"Timeout in seconds for an idle thread in the thread pool."
"Worker thread will be shut down after timeout",
GLOBAL_VAR(threadpool_idle_timeout), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, UINT_MAX), DEFAULT(60), BLOCK_SIZE(1)
);
-static Sys_var_uint Sys_threadpool_oversubscribe(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_oversubscribe(
"thread_pool_oversubscribe",
"How many additional active worker threads in a group are allowed.",
GLOBAL_VAR(threadpool_oversubscribe), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, 1000), DEFAULT(3), BLOCK_SIZE(1)
);
-static Sys_var_uint Sys_threadpool_size(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_size(
"thread_pool_size",
"Number of thread groups in the pool. "
"This parameter is roughly equivalent to maximum number of concurrently "
@@ -3774,19 +3954,23 @@ static Sys_var_uint Sys_threadpool_size(
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_threadpool_size),
ON_UPDATE(fix_threadpool_size)
);
-static Sys_var_uint Sys_threadpool_stall_limit(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_stall_limit(
"thread_pool_stall_limit",
"Maximum query execution time in milliseconds,"
"before an executing non-yielding thread is considered stalled."
"If a worker thread is stalled, additional worker thread "
"may be created to handle remaining clients.",
GLOBAL_VAR(threadpool_stall_limit), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(10, UINT_MAX), DEFAULT(500), BLOCK_SIZE(1),
+ VALID_RANGE(1, UINT_MAX), DEFAULT(DEFAULT_THREADPOOL_STALL_LIMIT), BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_threadpool_stall_limit)
);
-static Sys_var_uint Sys_threadpool_max_threads(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_max_threads(
"thread_pool_max_threads",
"Maximum allowed number of worker threads in the thread pool",
GLOBAL_VAR(threadpool_max_threads), CMD_LINE(REQUIRED_ARG),
@@ -3795,12 +3979,32 @@ static Sys_var_uint Sys_threadpool_max_threads(
ON_UPDATE(fix_tp_max_threads)
);
-static Sys_var_uint Sys_threadpool_threadpool_prio_kickup_timer(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_threadpool_prio_kickup_timer(
"thread_pool_prio_kickup_timer",
"The number of milliseconds before a dequeued low-priority statement is moved to the high-priority queue",
GLOBAL_VAR(threadpool_prio_kickup_timer), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(1000), BLOCK_SIZE(1)
);
+
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_exact_stats(
+ "thread_pool_exact_stats",
+ "If set to 1, provides better statistics in information_schema threadpool tables",
+ GLOBAL_VAR(threadpool_exact_stats), CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG
+);
+
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_THREAD_POOL>
+Sys_threadpool_dedicated_listener(
+ "thread_pool_dedicated_listener",
+ "If set to 1,listener thread will not pick up queries",
+ GLOBAL_VAR(threadpool_dedicated_listener), CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG
+);
#endif /* HAVE_POOL_OF_THREADS */
/**
@@ -3883,7 +4087,7 @@ static Sys_var_ulonglong Sys_tmp_table_size(
"If an internal in-memory temporary table exceeds this size, MariaDB "
"will automatically convert it to an on-disk MyISAM or Aria table.",
SESSION_VAR(tmp_memory_table_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1024, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
+ VALID_RANGE(0, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
BLOCK_SIZE(1));
static Sys_var_ulonglong Sys_tmp_memory_table_size(
@@ -3892,7 +4096,7 @@ static Sys_var_ulonglong Sys_tmp_memory_table_size(
"will automatically convert it to an on-disk MyISAM or Aria table. "
"Same as tmp_table_size.",
SESSION_VAR(tmp_memory_table_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1024, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
+ VALID_RANGE(0, (ulonglong)~(intptr)0), DEFAULT(16*1024*1024),
BLOCK_SIZE(1));
static Sys_var_ulonglong Sys_tmp_disk_table_size(
@@ -3900,15 +4104,7 @@ static Sys_var_ulonglong Sys_tmp_disk_table_size(
"Max size for data for an internal temporary on-disk MyISAM or Aria table.",
SESSION_VAR(tmp_disk_table_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1024, (ulonglong)~(intptr)0),
- DEFAULT((ulonglong)~(intptr)0),
- BLOCK_SIZE(1));
-
-static Sys_var_mybool Sys_timed_mutexes(
- "timed_mutexes",
- "Specify whether to time mutexes. Deprecated, has no effect.",
- GLOBAL_VAR(timed_mutexes), CMD_LINE(OPT_ARG), DEFAULT(0),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
- DEPRECATED(""));
+ DEFAULT((ulonglong)~(intptr)0), BLOCK_SIZE(1));
static Sys_var_charptr Sys_version(
"version", "Server version number. It may also include a suffix "
@@ -3918,7 +4114,7 @@ static Sys_var_charptr Sys_version(
"enabled, for example 10.1.1-MariaDB-mariadb1precise-log.",
READ_ONLY GLOBAL_VAR(server_version_ptr),
CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(server_version));
+ DEFAULT(server_version));
static char *server_version_comment_ptr;
static Sys_var_charptr Sys_version_comment(
@@ -3927,14 +4123,14 @@ static Sys_var_charptr Sys_version_comment(
"mariadb.org binary distribution.",
READ_ONLY GLOBAL_VAR(server_version_comment_ptr),
CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(MYSQL_COMPILATION_COMMENT));
+ DEFAULT(MYSQL_COMPILATION_COMMENT));
static char *server_version_compile_machine_ptr;
static Sys_var_charptr Sys_version_compile_machine(
"version_compile_machine", "The machine type or architecture "
"MariaDB was built on, for example i686.",
READ_ONLY GLOBAL_VAR(server_version_compile_machine_ptr),
- CMD_LINE_HELP_ONLY, IN_SYSTEM_CHARSET, DEFAULT(DEFAULT_MACHINE));
+ CMD_LINE_HELP_ONLY, DEFAULT(DEFAULT_MACHINE));
static char *server_version_compile_os_ptr;
static Sys_var_charptr Sys_version_compile_os(
@@ -3942,7 +4138,7 @@ static Sys_var_charptr Sys_version_compile_os(
"on, for example debian-linux-gnu.",
READ_ONLY GLOBAL_VAR(server_version_compile_os_ptr),
CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE));
+ DEFAULT(SYSTEM_TYPE));
#include <source_revision.h>
static char *server_version_source_revision;
@@ -3950,19 +4146,19 @@ static Sys_var_charptr Sys_version_source_revision(
"version_source_revision", "Source control revision id for MariaDB source code",
READ_ONLY GLOBAL_VAR(server_version_source_revision),
CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(SOURCE_REVISION));
+ DEFAULT(SOURCE_REVISION));
static char *malloc_library;
static Sys_var_charptr Sys_malloc_library(
"version_malloc_library", "Version of the used malloc library",
READ_ONLY GLOBAL_VAR(malloc_library), CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(guess_malloc_library()));
+ DEFAULT(guess_malloc_library()));
static char *ssl_library;
static Sys_var_charptr Sys_ssl_library(
"version_ssl_library", "Version of the used SSL library",
READ_ONLY GLOBAL_VAR(ssl_library), CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(SSL_LIBRARY));
+ DEFAULT(SSL_LIBRARY));
static Sys_var_ulong Sys_net_wait_timeout(
"wait_timeout",
@@ -3999,12 +4195,12 @@ static Sys_var_plugin Sys_default_storage_engine(
MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&default_storage_engine),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_not_null));
-// Alias for @@default_storage_engine
static Sys_var_plugin Sys_storage_engine(
"storage_engine", "Alias for @@default_storage_engine. Deprecated",
SESSION_VAR(table_plugin), NO_CMD_LINE,
MYSQL_STORAGE_ENGINE_PLUGIN, DEFAULT(&default_storage_engine),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_not_null));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_not_null), ON_UPDATE(0),
+ DEPRECATED("'@@default_storage_engine'")); // since 10.5.1
static Sys_var_plugin Sys_default_tmp_storage_engine(
"default_tmp_storage_engine", "The default storage engine for user-created temporary tables",
@@ -4015,7 +4211,8 @@ static Sys_var_plugin Sys_enforce_storage_engine(
"enforce_storage_engine", "Force the use of a storage engine for new tables",
SESSION_VAR(enforced_table_plugin),
NO_CMD_LINE, MYSQL_STORAGE_ENGINE_PLUGIN,
- DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
+ DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_has_super));
#ifdef HAVE_REPLICATION
@@ -4043,7 +4240,9 @@ check_gtid_pos_auto_engines(sys_var *self, THD *thd, set_var *var)
}
-static Sys_var_pluginlist Sys_gtid_pos_auto_engines(
+static Sys_var_on_access_global<Sys_var_pluginlist,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_POS_AUTO_ENGINES>
+Sys_gtid_pos_auto_engines(
"gtid_pos_auto_engines",
"List of engines for which to automatically create a "
"mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine "
@@ -4087,23 +4286,26 @@ static Sys_var_debug_sync Sys_debug_sync(
static Sys_var_charptr Sys_date_format(
"date_format", "The DATE format (ignored)",
READ_ONLY GLOBAL_VAR(global_date_format.format.str),
- CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ CMD_LINE(REQUIRED_ARG),
DEFAULT(known_date_time_formats[ISO_FORMAT].date_format),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), DEPRECATED(""));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("")); // since 10.1.2
static Sys_var_charptr Sys_datetime_format(
"datetime_format", "The DATETIME format (ignored)",
READ_ONLY GLOBAL_VAR(global_datetime_format.format.str),
- CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ CMD_LINE(REQUIRED_ARG),
DEFAULT(known_date_time_formats[ISO_FORMAT].datetime_format),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), DEPRECATED(""));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("")); // since 10.1.2
static Sys_var_charptr Sys_time_format(
"time_format", "The TIME format (ignored)",
READ_ONLY GLOBAL_VAR(global_time_format.format.str),
- CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ CMD_LINE(REQUIRED_ARG),
DEFAULT(known_date_time_formats[ISO_FORMAT].time_format),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0), DEPRECATED(""));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("")); // since 10.1.2
static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
{
@@ -4141,8 +4343,8 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
thd->variables.option_bits&=
~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT |
OPTION_GTID_BEGIN);
- thd->transaction.all.modified_non_trans_table= false;
- thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
+ thd->transaction->all.modified_non_trans_table= false;
+ thd->transaction->all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
return false;
}
@@ -4151,8 +4353,8 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
(OPTION_AUTOCOMMIT |OPTION_NOT_AUTOCOMMIT)) == 0)
{
// disabling autocommit
- thd->transaction.all.modified_non_trans_table= false;
- thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
+ thd->transaction->all.modified_non_trans_table= false;
+ thd->transaction->all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT;
thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
thd->variables.option_bits|= OPTION_NOT_AUTOCOMMIT;
return false;
@@ -4174,9 +4376,10 @@ export sys_var *Sys_autocommit_ptr= &Sys_autocommit; // for sql_yacc.yy
static Sys_var_mybool Sys_big_tables(
"big_tables", "Old variable, which if set to 1, allows large result sets "
"by saving all temporary sets to disk, avoiding 'table full' errors. No "
- "longer needed, as the server now handles this automatically. "
- "sql_big_tables is a synonym.",
- SESSION_VAR(big_tables), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+ "longer needed, as the server now handles this automatically.",
+ SESSION_VAR(big_tables), CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
+ DEPRECATED("")); // since 10.5.0
static Sys_var_bit Sys_big_selects(
"sql_big_selects", "If set to 0, MariaDB will not perform large SELECTs."
@@ -4191,7 +4394,8 @@ static Sys_var_bit Sys_log_off(
"query log is done for the client. Only clients with the SUPER privilege "
"can update this variable.",
NO_SET_STMT SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_LOG_OFF,
- DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
+ DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_has_super));
/**
This function sets the session variable thd->variables.sql_log_bin
@@ -4239,9 +4443,6 @@ static bool check_session_only_variable(sys_var *self, THD *,set_var *var)
*/
static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var)
{
- if (check_has_super(self, thd, var))
- return true;
-
if (check_session_only_variable(self, thd, var))
return true;
@@ -4253,7 +4454,10 @@ static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var)
return false;
}
-static Sys_var_mybool Sys_log_binlog(
+static Sys_var_on_access<Sys_var_mybool,
+ PRIV_SET_SYSTEM_VAR_SQL_LOG_BIN,
+ PRIV_SET_SYSTEM_VAR_SQL_LOG_BIN>
+Sys_sql_log_bin(
"sql_log_bin", "If set to 0 (1 is the default), no logging to the binary "
"log is done for the client. Only clients with the SUPER privilege can "
"update this variable. Can have unintended consequences if set globally, "
@@ -4285,6 +4489,11 @@ static Sys_var_bit Sys_auto_is_null(
SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_AUTO_IS_NULL,
DEFAULT(FALSE), NO_MUTEX_GUARD, IN_BINLOG);
+static Sys_var_bit Sys_if_exists(
+ "sql_if_exists", "If set to 1 adds an implicate IF EXISTS to ALTER, RENAME and DROP of TABLES, VIEWS, FUNCTIONS and PACKAGES",
+ SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_IF_EXISTS,
+ DEFAULT(FALSE), NO_MUTEX_GUARD, IN_BINLOG);
+
static Sys_var_bit Sys_safe_updates(
"sql_safe_updates", "If set to 1, UPDATEs and DELETEs need either a key in "
"the WHERE clause, or a LIMIT clause, or else they will aborted. Prevents "
@@ -4406,12 +4615,18 @@ static Sys_var_harows Sys_select_limit(
VALID_RANGE(0, HA_POS_ERROR), DEFAULT(HA_POS_ERROR), BLOCK_SIZE(1));
static const char *secure_timestamp_levels[]= {"NO", "SUPER", "REPLICATION", "YES", 0};
-static bool check_timestamp(sys_var *self, THD *thd, set_var *var)
+bool Sys_var_timestamp::on_check_access_session(THD *thd) const
{
- if (opt_secure_timestamp == SECTIME_NO)
+ switch (opt_secure_timestamp) {
+ case SECTIME_NO:
return false;
- if (opt_secure_timestamp == SECTIME_SUPER)
- return check_has_super(self, thd, var);
+ case SECTIME_SUPER:
+ return check_global_access(thd, SUPER_ACL | BINLOG_REPLAY_ACL);
+ case SECTIME_REPL:
+ return check_global_access(thd, BINLOG_REPLAY_ACL);
+ case SECTIME_YES:
+ break;
+ }
char buf[1024];
strxnmov(buf, sizeof(buf), "--secure-timestamp=",
secure_timestamp_levels[opt_secure_timestamp], NULL);
@@ -4422,7 +4637,7 @@ static Sys_var_timestamp Sys_timestamp(
"timestamp", "Set the time for this client",
sys_var::ONLY_SESSION, NO_CMD_LINE,
VALID_RANGE(0, TIMESTAMP_MAX_VALUE),
- NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_timestamp));
+ NO_MUTEX_GUARD, IN_BINLOG);
static bool update_last_insert_id(THD *thd, set_var *var)
{
@@ -4580,7 +4795,7 @@ static char *glob_hostname_ptr;
static Sys_var_charptr Sys_hostname(
"hostname", "Server host name",
READ_ONLY GLOBAL_VAR(glob_hostname_ptr), NO_CMD_LINE,
- IN_SYSTEM_CHARSET, DEFAULT(glob_hostname));
+ DEFAULT(glob_hostname));
#ifndef EMBEDDED_LIBRARY
static Sys_var_charptr Sys_repl_report_host(
@@ -4593,21 +4808,21 @@ static Sys_var_charptr Sys_repl_report_host(
"NAT and other routing issues, that IP may not be valid for connecting "
"to the slave from the master or other hosts",
READ_ONLY GLOBAL_VAR(report_host), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_charptr Sys_repl_report_user(
"report_user",
"The account user name of the slave to be reported to the master "
"during slave registration",
READ_ONLY GLOBAL_VAR(report_user), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_charptr Sys_repl_report_password(
"report_password",
"The account password of the slave to be reported to the master "
"during slave registration",
READ_ONLY GLOBAL_VAR(report_password), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_uint Sys_repl_report_port(
"report_port",
@@ -4628,7 +4843,7 @@ static Sys_var_mybool Sys_keep_files_on_create(
static char *license;
static Sys_var_charptr Sys_license(
"license", "The type of license the server has",
- READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE, IN_SYSTEM_CHARSET,
+ READ_ONLY GLOBAL_VAR(license), NO_CMD_LINE,
DEFAULT(STRINGIFY_ARG(LICENSE)));
#include <proxy_protocol.h>
@@ -4647,7 +4862,9 @@ static bool fix_proxy_protocol_networks(sys_var *, THD *, enum_var_type)
}
-static Sys_var_charptr Sys_proxy_protocol_networks(
+static Sys_var_on_access_global<Sys_var_charptr_fscs,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_PROXY_PROTOCOL_NETWORKS>
+Sys_proxy_protocol_networks(
"proxy_protocol_networks", "Enable proxy protocol for these source "
"networks. The syntax is a comma separated list of IPv4 and IPv6 "
"networks. If the network doesn't contain mask, it is considered to be "
@@ -4655,7 +4872,7 @@ static Sys_var_charptr Sys_proxy_protocol_networks(
"directive on the line. String \"localhost\" represents non-TCP "
"local connections (Unix domain socket, Windows named pipe or shared memory).",
GLOBAL_VAR(my_proxy_protocol_networks), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_proxy_protocol_networks), ON_UPDATE(fix_proxy_protocol_networks));
@@ -4752,10 +4969,10 @@ static bool fix_general_log_file(sys_var *self, THD *thd, enum_var_type type)
return fix_log(&opt_logname, opt_log_basename, ".log", opt_log,
reopen_general_log);
}
-static Sys_var_charptr Sys_general_log_path(
+static Sys_var_charptr_fscs Sys_general_log_path(
"general_log_file", "Log connections and queries to given file",
PREALLOCATED GLOBAL_VAR(opt_logname), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_log_path), ON_UPDATE(fix_general_log_file));
static void reopen_slow_log(char* name)
@@ -4768,12 +4985,12 @@ static bool fix_slow_log_file(sys_var *self, THD *thd, enum_var_type type)
return fix_log(&opt_slow_logname, opt_log_basename, "-slow.log",
global_system_variables.sql_log_slow, reopen_slow_log);
}
-static Sys_var_charptr Sys_slow_log_path(
+static Sys_var_charptr_fscs Sys_slow_log_path(
"slow_query_log_file", "Log slow queries to given log file. "
"Defaults logging to 'hostname'-slow.log. Must be enabled to activate "
"other slow log options",
PREALLOCATED GLOBAL_VAR(opt_slow_logname), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_log_path), ON_UPDATE(fix_slow_log_file));
static Sys_var_have Sys_have_compress(
@@ -4834,6 +5051,16 @@ static Sys_var_have Sys_have_symlink(
"--skip-symbolic-links option.",
READ_ONLY GLOBAL_VAR(have_symlink), NO_CMD_LINE);
+#ifdef __SANITIZE_ADDRESS__
+static char *have_sanitizer;
+static Sys_var_charptr_fscs Sys_have_santitizer(
+ "have_sanitizer",
+ "If the server is compiled with ASan (Address sanitizer) this will be "
+ "set to ASAN",
+ READ_ONLY GLOBAL_VAR(have_sanitizer), NO_CMD_LINE,
+ DEFAULT("ASAN"));
+#endif
+
static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type);
static Sys_var_mybool Sys_general_log(
@@ -4924,56 +5151,60 @@ static Sys_var_mybool Sys_log_slave_updates(
READ_ONLY GLOBAL_VAR(opt_log_slave_updates), CMD_LINE(OPT_ARG),
DEFAULT(0));
-static Sys_var_charptr Sys_relay_log(
+static Sys_var_charptr_fscs Sys_relay_log(
"relay_log", "The location and name to use for relay logs.",
READ_ONLY GLOBAL_VAR(opt_relay_logname), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
/*
Uses NO_CMD_LINE since the --relay-log-index option set
opt_relaylog_index_name variable and computes a value for the
relay_log_index variable.
*/
-static Sys_var_charptr Sys_relay_log_index(
+static Sys_var_charptr_fscs Sys_relay_log_index(
"relay_log_index", "The location and name to use for the file "
"that keeps a list of the last relay logs.",
READ_ONLY GLOBAL_VAR(relay_log_index), NO_CMD_LINE,
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
/*
Uses NO_CMD_LINE since the --log-bin-index option set
opt_binlog_index_name variable and computes a value for the
log_bin_index variable.
*/
-static Sys_var_charptr Sys_binlog_index(
+static Sys_var_charptr_fscs Sys_binlog_index(
"log_bin_index", "File that holds the names for last binary log files.",
READ_ONLY GLOBAL_VAR(log_bin_index), NO_CMD_LINE,
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_relay_log_basename(
+static Sys_var_charptr_fscs Sys_relay_log_basename(
"relay_log_basename",
"The full path of the relay log file names, excluding the extension.",
READ_ONLY GLOBAL_VAR(relay_log_basename), NO_CMD_LINE,
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_log_bin_basename(
+static Sys_var_charptr_fscs Sys_log_bin_basename(
"log_bin_basename",
"The full path of the binary log file names, excluding the extension.",
READ_ONLY GLOBAL_VAR(log_bin_basename), NO_CMD_LINE,
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_charptr Sys_relay_log_info_file(
+static Sys_var_charptr_fscs Sys_relay_log_info_file(
"relay_log_info_file", "The location and name of the file that "
"remembers where the SQL replication thread is in the relay logs.",
READ_ONLY GLOBAL_VAR(relay_log_info_file), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_mybool Sys_relay_log_purge(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RELAY_LOG_PURGE>
+Sys_relay_log_purge(
"relay_log_purge", "if disabled - do not purge relay logs. "
"if enabled - purge them as soon as they are no more needed.",
GLOBAL_VAR(relay_log_purge), CMD_LINE(OPT_ARG), DEFAULT(TRUE));
-static Sys_var_mybool Sys_relay_log_recovery(
+static Sys_var_on_access_global<Sys_var_mybool,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_RELAY_LOG_RECOVERY>
+Sys_relay_log_recovery(
"relay_log_recovery", "Enables automatic relay log recovery "
"right after the database startup, which means that the IO Thread "
"starts re-fetching from the master right after the last transaction "
@@ -5105,12 +5336,14 @@ static Sys_var_rpl_filter Sys_replicate_do_db(
"statement-based replication, only the default database (that "
"is, the one selected by USE) is considered, not any explicitly "
"mentioned tables in the query. For row-based replication, the "
- "actual names of table(s) being updated are checked.");
+ "actual names of table(s) being updated are checked.",
+ PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_DO_DB);
static Sys_var_rpl_filter Sys_replicate_do_table(
"replicate_do_table", OPT_REPLICATE_DO_TABLE,
"Tells the slave to restrict replication to tables in the "
- "comma-separated list.");
+ "comma-separated list.",
+ PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_DO_TABLE);
static Sys_var_rpl_filter Sys_replicate_ignore_db(
"replicate_ignore_db", OPT_REPLICATE_IGNORE_DB,
@@ -5119,32 +5352,38 @@ static Sys_var_rpl_filter Sys_replicate_ignore_db(
"statement-based replication, only the default database (that "
"is, the one selected by USE) is considered, not any explicitly "
"mentioned tables in the query. For row-based replication, the "
- "actual names of table(s) being updated are checked.");
+ "actual names of table(s) being updated are checked.",
+ PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_IGNORE_DB);
static Sys_var_rpl_filter Sys_replicate_ignore_table(
"replicate_ignore_table", OPT_REPLICATE_IGNORE_TABLE,
"Tells the slave thread not to replicate any statement that "
"updates the specified table, even if any other tables might be "
- "updated by the same statement.");
+ "updated by the same statement.",
+ PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_IGNORE_TABLE);
static Sys_var_rpl_filter Sys_replicate_wild_do_table(
"replicate_wild_do_table", OPT_REPLICATE_WILD_DO_TABLE,
"Tells the slave thread to restrict replication to statements "
"where any of the updated tables match the specified database "
- "and table name patterns.");
+ "and table name patterns.",
+ PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_WILD_DO_TABLE);
static Sys_var_rpl_filter Sys_replicate_wild_ignore_table(
"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.");
+ "match the given wildcard pattern.",
+ PRIV_SET_SYSTEM_GLOBAL_VAR_REPLICATE_WILD_IGNORE_TABLE);
-static Sys_var_charptr Sys_slave_load_tmpdir(
+static Sys_var_charptr_fscs Sys_slave_load_tmpdir(
"slave_load_tmpdir", "The location where the slave should put "
"its temporary files when replicating a LOAD DATA INFILE command",
READ_ONLY GLOBAL_VAR(slave_load_tmpdir), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_uint Sys_slave_net_timeout(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_NET_TIMEOUT>
+Sys_slave_net_timeout(
"slave_net_timeout", "Number of seconds to wait for more data "
"from any master/slave connection before aborting the read",
GLOBAL_VAR(slave_net_timeout), CMD_LINE(REQUIRED_ARG),
@@ -5260,9 +5499,11 @@ static Sys_var_charptr Sys_slave_skip_errors(
"replication when a query event returns an error from the "
"provided list",
READ_ONLY GLOBAL_VAR(opt_slave_skip_errors), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(0));
+ DEFAULT(0));
-static Sys_var_ulonglong Sys_read_binlog_speed_limit(
+static Sys_var_on_access_global<Sys_var_ulonglong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_READ_BINLOG_SPEED_LIMIT>
+Sys_read_binlog_speed_limit(
"read_binlog_speed_limit", "Maximum speed(KB/s) to read binlog from"
" master (0 = no limit)",
GLOBAL_VAR(opt_read_binlog_speed_limit), CMD_LINE(REQUIRED_ARG),
@@ -5276,20 +5517,24 @@ static Sys_var_charptr Sys_slave_transaction_retry_errors(
"connect error and 2 types of lost connection error are automatically "
"added to this list",
READ_ONLY GLOBAL_VAR(opt_slave_transaction_retry_errors), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_ulonglong Sys_relay_log_space_limit(
"relay_log_space_limit", "Maximum space to use for all relay logs",
READ_ONLY GLOBAL_VAR(relay_log_space_limit), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0), BLOCK_SIZE(1));
-static Sys_var_uint Sys_sync_relaylog_period(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_RELAY_LOG>
+Sys_sync_relaylog_period(
"sync_relay_log", "Synchronously flush relay log to disk after "
"every #th event. Use 0 to disable synchronous flushing",
GLOBAL_VAR(sync_relaylog_period), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(10000), BLOCK_SIZE(1));
-static Sys_var_uint Sys_sync_relayloginfo_period(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_RELAY_LOG_INFO>
+Sys_sync_relayloginfo_period(
"sync_relay_log_info", "Synchronously flush relay log info "
"to disk after every #th transaction. Use 0 to disable "
"synchronous flushing",
@@ -5297,13 +5542,17 @@ static Sys_var_uint Sys_sync_relayloginfo_period(
VALID_RANGE(0, UINT_MAX), DEFAULT(10000), BLOCK_SIZE(1));
#endif
-static Sys_var_uint Sys_sync_binlog_period(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_BINLOG>
+Sys_sync_binlog_period(
"sync_binlog", "Synchronously flush binary log to disk after "
"every #th event. Use 0 (default) to disable synchronous flushing",
GLOBAL_VAR(sync_binlog_period), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
-static Sys_var_uint Sys_sync_masterinfo_period(
+static Sys_var_on_access_global<Sys_var_uint,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SYNC_MASTER_INFO>
+Sys_sync_masterinfo_period(
"sync_master_info", "Synchronously flush master info to disk "
"after every #th event. Use 0 to disable synchronous flushing",
GLOBAL_VAR(sync_masterinfo_period), CMD_LINE(REQUIRED_ARG),
@@ -5318,7 +5567,9 @@ static Sys_var_ulong Sys_slave_trans_retries(
GLOBAL_VAR(slave_trans_retries), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(10), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_slave_trans_retry_interval(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_SLAVE_TRANSACTION_RETRY_INTERVAL>
+Sys_slave_trans_retry_interval(
"slave_transaction_retry_interval", "Interval of the slave SQL "
"thread will retry a transaction in case it failed with a deadlock "
"or elapsed lock wait timeout or listed in "
@@ -5416,10 +5667,10 @@ static Sys_var_tz Sys_time_zone(
#include "wsrep_sst.h"
#include "wsrep_binlog.h"
-static Sys_var_charptr Sys_wsrep_provider(
+static Sys_var_charptr_fscs Sys_wsrep_provider(
"wsrep_provider", "Path to replication provider library",
PREALLOCATED GLOBAL_VAR(wsrep_provider), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(WSREP_NONE),
+ DEFAULT(WSREP_NONE),
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_provider_check), ON_UPDATE(wsrep_provider_update));
@@ -5428,19 +5679,19 @@ static Sys_var_charptr Sys_wsrep_provider_options(
"options (see wsrep_provider_options documentation).",
PREALLOCATED GLOBAL_VAR(wsrep_provider_options),
CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_provider_options_check),
ON_UPDATE(wsrep_provider_options_update));
-static Sys_var_charptr Sys_wsrep_data_home_dir(
+static Sys_var_charptr_fscs Sys_wsrep_data_home_dir(
"wsrep_data_home_dir", "home directory for wsrep provider",
READ_ONLY GLOBAL_VAR(wsrep_data_home_dir), CMD_LINE(REQUIRED_ARG),
- IN_FS_CHARSET, DEFAULT(mysql_real_data_home));
+ DEFAULT(mysql_real_data_home));
static Sys_var_charptr Sys_wsrep_cluster_name(
"wsrep_cluster_name", "Name for the cluster",
PREALLOCATED GLOBAL_VAR(wsrep_cluster_name), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(WSREP_CLUSTER_NAME),
+ DEFAULT(WSREP_CLUSTER_NAME),
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_cluster_name_check),
ON_UPDATE(wsrep_cluster_name_update));
@@ -5450,7 +5701,7 @@ static Sys_var_charptr Sys_wsrep_cluster_address (
"wsrep_cluster_address", "Address to initially connect to cluster",
PREALLOCATED GLOBAL_VAR(wsrep_cluster_address),
CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(""),
+ DEFAULT(""),
&PLock_wsrep_cluster_config, NOT_IN_BINLOG,
ON_CHECK(wsrep_cluster_address_check),
ON_UPDATE(wsrep_cluster_address_update));
@@ -5460,7 +5711,7 @@ static Sys_var_charptr Sys_wsrep_node_name (
"wsrep_sst_donor as a preferred donor. Note that multiple nodes "
"in a cluster can have the same name.",
PREALLOCATED GLOBAL_VAR(wsrep_node_name), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(glob_hostname), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(glob_hostname), NO_MUTEX_GUARD, NOT_IN_BINLOG,
wsrep_node_name_check, wsrep_node_name_update);
static Sys_var_charptr Sys_wsrep_node_address (
@@ -5468,7 +5719,7 @@ static Sys_var_charptr Sys_wsrep_node_address (
"the format ip address[:port]. Used in situations where autoguessing "
"is not reliable. As of MariaDB 10.1.8, supports IPv6.",
PREALLOCATED GLOBAL_VAR(wsrep_node_address), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(""),
+ DEFAULT(""),
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_node_address_check),
ON_UPDATE(wsrep_node_address_update));
@@ -5476,7 +5727,7 @@ static Sys_var_charptr Sys_wsrep_node_address (
static Sys_var_charptr Sys_wsrep_node_incoming_address(
"wsrep_node_incoming_address", "Client connection address",
PREALLOCATED GLOBAL_VAR(wsrep_node_incoming_address),CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(WSREP_NODE_INCOMING_AUTO));
+ DEFAULT(WSREP_NODE_INCOMING_AUTO));
static Sys_var_ulong Sys_wsrep_slave_threads(
"wsrep_slave_threads", "Number of slave appliers to launch",
@@ -5489,7 +5740,7 @@ static Sys_var_ulong Sys_wsrep_slave_threads(
static Sys_var_charptr Sys_wsrep_dbug_option(
"wsrep_dbug_option", "DBUG options to provider library",
GLOBAL_VAR(wsrep_dbug_option),CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(""));
+ DEFAULT(""));
static const char *wsrep_debug_names[]=
{ "NONE", "SERVER", "TRANSACTION", "STREAMING", "CLIENT", NullS };
@@ -5571,14 +5822,14 @@ static Sys_var_mybool Sys_wsrep_drupal_282555_workaround(
static Sys_var_charptr sys_wsrep_sst_method(
"wsrep_sst_method", "State snapshot transfer method",
GLOBAL_VAR(wsrep_sst_method),CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(WSREP_SST_DEFAULT), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(WSREP_SST_DEFAULT), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_sst_method_check));
static Sys_var_charptr Sys_wsrep_sst_receive_address(
"wsrep_sst_receive_address", "Address where node is waiting for "
"SST contact",
GLOBAL_VAR(wsrep_sst_receive_address),CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(WSREP_SST_ADDRESS_AUTO), NO_MUTEX_GUARD,
+ DEFAULT(WSREP_SST_ADDRESS_AUTO), NO_MUTEX_GUARD,
NOT_IN_BINLOG,
ON_CHECK(wsrep_sst_receive_address_check),
ON_UPDATE(wsrep_sst_receive_address_update));
@@ -5586,7 +5837,7 @@ static Sys_var_charptr Sys_wsrep_sst_receive_address(
static Sys_var_charptr Sys_wsrep_sst_auth(
"wsrep_sst_auth", "Authentication for SST connection",
PREALLOCATED GLOBAL_VAR(wsrep_sst_auth), CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(NULL), NO_MUTEX_GUARD,
+ DEFAULT(NULL), NO_MUTEX_GUARD,
NOT_IN_BINLOG,
ON_CHECK(wsrep_sst_auth_check),
ON_UPDATE(wsrep_sst_auth_update));
@@ -5594,7 +5845,7 @@ static Sys_var_charptr Sys_wsrep_sst_auth(
static Sys_var_charptr Sys_wsrep_sst_donor(
"wsrep_sst_donor", "preferred donor node for the SST",
GLOBAL_VAR(wsrep_sst_donor),CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ DEFAULT(""), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_sst_donor_check),
ON_UPDATE(wsrep_sst_donor_update));
@@ -5616,7 +5867,7 @@ static Sys_var_charptr Sys_wsrep_start_position (
"wsrep_start_position", "global transaction position to start from ",
PREALLOCATED GLOBAL_VAR(wsrep_start_position),
CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(WSREP_START_POSITION_ZERO),
+ DEFAULT(WSREP_START_POSITION_ZERO),
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(wsrep_start_position_check),
ON_UPDATE(wsrep_start_position_update));
@@ -5636,7 +5887,7 @@ static Sys_var_ulong Sys_wsrep_max_ws_rows (
static Sys_var_charptr Sys_wsrep_notify_cmd(
"wsrep_notify_cmd", "",
GLOBAL_VAR(wsrep_notify_cmd),CMD_LINE(REQUIRED_ARG),
- IN_SYSTEM_CHARSET, DEFAULT(""));
+ DEFAULT(""));
static Sys_var_mybool Sys_wsrep_certify_nonPK(
"wsrep_certify_nonPK", "Certify tables with no primary key",
@@ -5663,7 +5914,7 @@ static Sys_var_mybool Sys_wsrep_causal_reads(
CMD_LINE(OPT_ARG, OPT_WSREP_CAUSAL_READS), DEFAULT(FALSE),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(wsrep_causal_reads_update),
- DEPRECATED("'@@wsrep_sync_wait=1'"));
+ DEPRECATED("'@@wsrep_sync_wait=1'")); // since 10.1.3
static Sys_var_uint Sys_wsrep_sync_wait(
"wsrep_sync_wait", "Ensure \"synchronous\" read view before executing "
@@ -5685,12 +5936,20 @@ static Sys_var_enum Sys_wsrep_OSU_method(
static PolyLock_mutex PLock_wsrep_desync(&LOCK_wsrep_desync);
static Sys_var_mybool Sys_wsrep_desync (
"wsrep_desync", "To desynchronize the node from the cluster",
- GLOBAL_VAR(wsrep_desync),
+ GLOBAL_VAR(wsrep_desync),
CMD_LINE(OPT_ARG), DEFAULT(FALSE),
&PLock_wsrep_desync, NOT_IN_BINLOG,
ON_CHECK(wsrep_desync_check),
ON_UPDATE(wsrep_desync_update));
+static Sys_var_mybool Sys_wsrep_strict_ddl (
+ "wsrep_strict_ddl", "If set, reject DDL on affected tables not supporting Galera replication",
+ GLOBAL_VAR(wsrep_strict_ddl),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0),
+ ON_UPDATE(0));
+
static const char *wsrep_reject_queries_names[]= { "NONE", "ALL", "ALL_KILL", NullS };
static Sys_var_enum Sys_wsrep_reject_queries(
"wsrep_reject_queries", "Variable to set to reject queries",
@@ -5729,7 +5988,7 @@ static Sys_var_mybool Sys_wsrep_load_data_splitting(
"transaction after every 10K rows inserted (deprecated)",
GLOBAL_VAR(wsrep_load_data_splitting),
CMD_LINE(OPT_ARG), DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(0), ON_UPDATE(0), DEPRECATED(""));
+ ON_CHECK(0), ON_UPDATE(0), DEPRECATED("")); // since 10.4.3
static Sys_var_mybool Sys_wsrep_slave_FK_checks(
"wsrep_slave_FK_checks", "Should slave thread do "
@@ -5792,9 +6051,17 @@ static Sys_var_uint Sys_wsrep_gtid_domain_id(
"wsrep_gtid_domain_id", "When wsrep_gtid_mode is set, this value is "
"used as gtid_domain_id for galera transactions and also copied to the "
"joiner nodes during state transfer. It is ignored, otherwise.",
- GLOBAL_VAR(wsrep_gtid_domain_id), CMD_LINE(REQUIRED_ARG),
+ GLOBAL_VAR(wsrep_gtid_server.domain_id), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1));
+static Sys_var_ulonglong Sys_wsrep_gtid_seq_no(
+ "wsrep_gtid_seq_no",
+ "Internal server usage, manually set WSREP GTID seqno.",
+ SESSION_ONLY(wsrep_gtid_seq_no),
+ NO_CMD_LINE, VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(wsrep_gtid_seq_no_check));
+
static Sys_var_mybool Sys_wsrep_gtid_mode(
"wsrep_gtid_mode", "Automatically update the (joiner) node's "
"wsrep_gtid_domain_id value with that of donor's (received during "
@@ -5807,7 +6074,7 @@ static char *wsrep_patch_version_ptr;
static Sys_var_charptr Sys_wsrep_patch_version(
"wsrep_patch_version", "Wsrep patch version, for example wsrep_25.10.",
READ_ONLY GLOBAL_VAR(wsrep_patch_version_ptr), CMD_LINE_HELP_ONLY,
- IN_SYSTEM_CHARSET, DEFAULT(WSREP_PATCH_VERSION));
+ DEFAULT(WSREP_PATCH_VERSION));
#endif /* WITH_WSREP */
@@ -5822,8 +6089,7 @@ static Sys_var_ulong Sys_host_cache_size(
"How many host names should be cached to avoid resolving.",
AUTO_SET GLOBAL_VAR(host_cache_size),
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 65536),
- DEFAULT(HOST_CACHE_SIZE),
- BLOCK_SIZE(1),
+ DEFAULT(HOST_CACHE_SIZE), BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_host_cache_size));
@@ -5874,14 +6140,14 @@ static Sys_var_mybool Sys_tcp_nodelay(
ON_CHECK(check_session_only_variable),
ON_UPDATE(update_tcp_nodelay));
-static Sys_var_charptr Sys_ignore_db_dirs(
+static Sys_var_charptr_fscs Sys_ignore_db_dirs(
"ignore_db_dirs",
"Specifies a directory to add to the ignore list when collecting "
"database names from the datadir. Put a blank argument to reset "
"the list accumulated so far.",
READ_ONLY GLOBAL_VAR(opt_ignore_db_dirs),
CMD_LINE(REQUIRED_ARG, OPT_IGNORE_DB_DIRECTORY),
- IN_FS_CHARSET, DEFAULT(0));
+ DEFAULT(0));
static Sys_var_ulong Sys_sp_cache_size(
"stored_program_cache",
@@ -5932,7 +6198,9 @@ static Sys_var_uint Sys_extra_port(
READ_ONLY GLOBAL_VAR(mysqld_extra_port), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_extra_max_connections(
+static Sys_var_on_access_global<Sys_var_ulong,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_EXTRA_MAX_CONNECTIONS>
+Sys_extra_max_connections(
"extra_max_connections", "The number of connections on extra-port",
GLOBAL_VAR(extra_max_connections), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, 100000), DEFAULT(1), BLOCK_SIZE(1), NO_MUTEX_GUARD,
@@ -5992,34 +6260,52 @@ static Sys_var_set Sys_log_disabled_statements(
DEFAULT(LOG_DISABLE_SP),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
+#define NOT_SUPPORTED_YET -2
+#ifndef PCRE2_EXTENDED_MORE
+#define PCRE2_EXTENDED_MORE NOT_SUPPORTED_YET
+#endif
+
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)
+ "EXTENDED_MORE",//(?xx) Ignore white space and # comments inside cheracter
+ "EXTRA", // means nothing since PCRE2
"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,
+ PCRE2_DOTALL,
+ PCRE2_DUPNAMES,
+ PCRE2_EXTENDED,
+ PCRE2_EXTENDED_MORE,
+ -1, /* EXTRA flag not available since PCRE2 */
+ PCRE2_MULTILINE,
+ PCRE2_UNGREEDY,
0
};
-int default_regex_flags_pcre(const THD *thd)
+int default_regex_flags_pcre(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 & (1ULL << i))
+ {
+ if (default_regex_flags_to_pcre[i] < 0)
+ {
+ const char *msg= default_regex_flags_to_pcre[i] == NOT_SUPPORTED_YET
+ ? "Your version of PCRE2 does not support the %s flag. Ignored."
+ : "PCRE2 doesn't support the %s flag. Ignored.";
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_UNKNOWN_ERROR, msg, default_regex_flags_names[i]);
+ continue;
+ }
res|= default_regex_flags_to_pcre[i];
+ }
}
return res;
}
@@ -6074,7 +6360,10 @@ static Sys_var_mybool Sys_userstat(
GLOBAL_VAR(opt_userstat_running),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-static Sys_var_mybool Sys_binlog_annotate_row_events(
+static Sys_var_on_access<Sys_var_mybool,
+ PRIV_SET_SYSTEM_VAR_BINLOG_ANNOTATE_ROW_EVENTS,
+ PRIV_SET_SYSTEM_VAR_BINLOG_ANNOTATE_ROW_EVENTS>
+Sys_binlog_annotate_row_events(
"binlog_annotate_row_events",
"Tells the master to annotate RBR events with the statement that "
"caused these events",
@@ -6188,7 +6477,10 @@ static Sys_var_mybool Sys_binlog_encryption(
DEFAULT(FALSE));
static const char *binlog_row_image_names[]= {"MINIMAL", "NOBLOB", "FULL", NullS};
-static Sys_var_enum Sys_binlog_row_image(
+static Sys_var_on_access<Sys_var_enum,
+ PRIV_SET_SYSTEM_VAR_BINLOG_ROW_IMAGE,
+ PRIV_SET_SYSTEM_VAR_BINLOG_ROW_IMAGE>
+Sys_binlog_row_image(
"binlog_row_image",
"Controls whether rows should be logged in 'FULL', 'NOBLOB' or "
"'MINIMAL' formats. 'FULL', means that all columns in the before "
@@ -6201,6 +6493,21 @@ static Sys_var_enum Sys_binlog_row_image(
SESSION_VAR(binlog_row_image), CMD_LINE(REQUIRED_ARG),
binlog_row_image_names, DEFAULT(BINLOG_ROW_IMAGE_FULL));
+static const char *binlog_row_metadata_names[]= {"NO_LOG", "MINIMAL", "FULL", NullS};
+static Sys_var_on_access_global<Sys_var_enum,
+ PRIV_SET_SYSTEM_GLOBAL_VAR_BINLOG_ROW_METADATA>
+Sys_binlog_row_metadata(
+ "binlog_row_metadata",
+ "Controls whether metadata is logged using FULL , MINIMAL format and NO_LOG."
+ "FULL causes all metadata to be logged; MINIMAL means that only "
+ "metadata actually required by slave is logged; NO_LOG NO metadata will be logged."
+ "Default: NO_LOG.",
+ GLOBAL_VAR(binlog_row_metadata), CMD_LINE(REQUIRED_ARG),
+ binlog_row_metadata_names, DEFAULT(Table_map_log_event::BINLOG_ROW_METADATA_NO_LOG),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ ON_UPDATE(NULL));
+
+
static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
{
longlong previous_val= thd->variables.pseudo_slave_mode;
@@ -6279,8 +6586,7 @@ static Sys_var_ulong Sys_log_tc_size(
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()));
+ DEFAULT(my_getpagesize() * 6), BLOCK_SIZE(my_getpagesize()));
#endif
static Sys_var_ulonglong Sys_max_thread_mem(
@@ -6295,7 +6601,7 @@ static Sys_var_ulonglong Sys_max_thread_mem(
static Sys_var_sesvartrack Sys_track_session_sys_vars(
"session_track_system_variables",
"Track changes in registered system variables. ",
- CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ CMD_LINE(REQUIRED_ARG),
DEFAULT("autocommit,character_set_client,character_set_connection,"
"character_set_results,time_zone"));
@@ -6357,6 +6663,24 @@ static Sys_var_mybool Sys_session_track_state_change(
ON_CHECK(0),
ON_UPDATE(update_session_track_state_change));
+
+#ifdef USER_VAR_TRACKING
+static bool update_session_track_user_variables(sys_var *self, THD *thd,
+ enum_var_type type)
+{
+ return thd->session_tracker.user_variables.update(thd, 0);
+}
+
+static Sys_var_mybool Sys_session_track_user_variables(
+ "session_track_user_variables",
+ "Track changes to user variables.",
+ SESSION_VAR(session_track_user_variables),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(0),
+ ON_UPDATE(update_session_track_user_variables));
+#endif // USER_VAR_TRACKING
+
#endif //EMBEDDED_LIBRARY
static Sys_var_uint Sys_in_subquery_conversion_threshold(
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index 9ab424b3f8d..c21a4a83165 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -1,5 +1,5 @@
/* Copyright (c) 2002, 2011, Oracle and/or its affiliates.
- Copyright (c) 2010, 2019, MariaDB Corporation.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
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
@@ -31,6 +31,7 @@
#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
#include "rpl_mi.h" // For Multi-Source Replication
#include "debug_sync.h"
+#include "sql_acl.h" // check_global_access()
/*
a set of mostly trivial (as in f(X)=X) defines below to make system variable
@@ -97,11 +98,48 @@
exit(255); \
}
-enum charset_enum {IN_SYSTEM_CHARSET, IN_FS_CHARSET};
static const char *bool_values[3]= {"OFF", "ON", 0};
TYPELIB bool_typelib={ array_elements(bool_values)-1, "", bool_values, 0 };
+
+template<class BASE, privilege_t GLOBAL_PRIV, privilege_t SESSION_PRIV>
+class Sys_var_on_access: public BASE
+{
+ using BASE::BASE;
+ bool on_check_access_global(THD *thd) const override
+ {
+ return check_global_access(thd, GLOBAL_PRIV);
+ }
+ bool on_check_access_session(THD *thd) const override
+ {
+ return check_global_access(thd, SESSION_PRIV);
+ }
+};
+
+
+template<class BASE, privilege_t GLOBAL_PRIV>
+class Sys_var_on_access_global: public BASE
+{
+ using BASE::BASE;
+ bool on_check_access_global(THD *thd) const override
+ {
+ return check_global_access(thd, GLOBAL_PRIV);
+ }
+};
+
+
+template<class BASE, privilege_t SESSION_PRIV>
+class Sys_var_on_access_session: public BASE
+{
+ using BASE::BASE;
+ bool on_check_access_session(THD *thd) const override
+ {
+ return check_global_access(thd, SESSION_PRIV);
+ }
+};
+
+
/**
A small wrapper class to pass getopt arguments as a pair
to the Sys_var_* constructors. It improves type safety and helps
@@ -449,9 +487,6 @@ public:
or not. The state of the initial value is specified in the constructor,
after that it's managed automatically. The value of NULL is supported.
- Class specific constructor arguments:
- enum charset_enum is_os_charset_arg
-
Backing store: char*
@note
@@ -465,7 +500,6 @@ public:
Sys_var_charptr_base(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, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
@@ -476,7 +510,6 @@ public:
lock, binlog_status_arg, on_check_func, on_update_func,
substitute)
{
- is_os_charset= is_os_charset_arg == IN_FS_CHARSET;
/*
use GET_STR_ALLOC - if ALLOCATED it must be *always* allocated,
otherwise (GET_STR) you'll never know whether to free it or not.
@@ -532,7 +565,8 @@ public:
size_t len=var->save_result.string_value.length;
if (ptr)
{
- new_val= (char*)my_memdup(ptr, len+1, MYF(MY_WME));
+ new_val= (char*)my_memdup(key_memory_Sys_var_charptr_value,
+ ptr, len+1, MYF(MY_WME));
if (!new_val) return 0;
new_val[len]=0;
}
@@ -568,14 +602,13 @@ public:
Sys_var_charptr(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, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0) :
Sys_var_charptr_base(name_arg, comment, flag_args, off, size, getopt,
- is_os_charset_arg, def_val, lock, binlog_status_arg,
+ def_val, lock, binlog_status_arg,
on_check_func, on_update_func, substitute)
{
SYSVAR_ASSERT(scope() == GLOBAL);
@@ -591,6 +624,18 @@ public:
{ DBUG_ASSERT(FALSE); }
};
+
+class Sys_var_charptr_fscs: public Sys_var_charptr
+{
+ using Sys_var_charptr::Sys_var_charptr;
+public:
+ CHARSET_INFO *charset(THD *thd) const override
+ {
+ return thd->variables.character_set_filesystem;
+ }
+};
+
+
#ifndef EMBEDDED_LIBRARY
class Sys_var_sesvartrack: public Sys_var_charptr_base
{
@@ -598,11 +643,10 @@ public:
Sys_var_sesvartrack(const char *name_arg,
const char *comment,
CMD_LINE getopt,
- enum charset_enum is_os_charset_arg,
const char *def_val, PolyLock *lock= 0) :
Sys_var_charptr_base(name_arg, comment,
SESSION_VAR(session_track_system_variables),
- getopt, is_os_charset_arg, def_val, lock,
+ getopt, def_val, lock,
VARIABLE_NOT_IN_BINLOG, 0, 0, 0)
{}
bool do_check(THD *thd, set_var *var)
@@ -651,14 +695,12 @@ public:
class Sys_var_proxy_user: public sys_var
{
public:
- Sys_var_proxy_user(const char *name_arg,
- const char *comment, enum charset_enum is_os_charset_arg)
+ Sys_var_proxy_user(const char *name_arg, const char *comment)
: sys_var(&all_sys_vars, name_arg, comment,
sys_var::READONLY+sys_var::ONLY_SESSION, 0, NO_GETOPT,
NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL)
{
- is_os_charset= is_os_charset_arg == IN_FS_CHARSET;
option.var_type|= GET_STR;
}
bool do_check(THD *thd, set_var *var)
@@ -691,9 +733,8 @@ protected:
class Sys_var_external_user : public Sys_var_proxy_user
{
public:
- Sys_var_external_user(const char *name_arg, const char *comment_arg,
- enum charset_enum is_os_charset_arg)
- : Sys_var_proxy_user (name_arg, comment_arg, is_os_charset_arg)
+ Sys_var_external_user(const char *name_arg, const char *comment_arg)
+ : Sys_var_proxy_user (name_arg, comment_arg)
{}
protected:
@@ -708,36 +749,44 @@ class Sys_var_rpl_filter: public sys_var
{
private:
int opt_id;
+ privilege_t m_access_global;
public:
- Sys_var_rpl_filter(const char *name, int getopt_id, const char *comment)
+ Sys_var_rpl_filter(const char *name, int getopt_id, const char *comment,
+ privilege_t access_global)
: sys_var(&all_sys_vars, name, comment, sys_var::GLOBAL, 0, NO_GETOPT,
NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
- NULL, NULL, NULL), opt_id(getopt_id)
+ NULL, NULL, NULL), opt_id(getopt_id),
+ m_access_global(access_global)
{
option.var_type|= GET_STR | GET_ASK_ADDR;
}
- bool do_check(THD *thd, set_var *var)
+ bool do_check(THD *thd, set_var *var) override
{
return Sys_var_charptr::do_string_check(thd, var, charset(thd));
}
- void session_save_default(THD *thd, set_var *var)
+ void session_save_default(THD *, set_var *) override
{ DBUG_ASSERT(FALSE); }
- void global_save_default(THD *thd, set_var *var)
+ void global_save_default(THD *, set_var *) override
{ DBUG_ASSERT(FALSE); }
- bool session_update(THD *thd, set_var *var)
+ bool session_update(THD *, set_var *) override
{
DBUG_ASSERT(FALSE);
return true;
}
- bool global_update(THD *thd, set_var *var);
+ bool global_update(THD *thd, set_var *var) override;
+
+ bool on_check_access_global(THD *thd) const override
+ {
+ return check_global_access(thd, m_access_global);
+ }
protected:
- uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
+ uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) override;
bool set_filter_value(const char *value, Master_info *mi);
};
@@ -745,9 +794,6 @@ protected:
The class for string variables. Useful for strings that aren't necessarily
\0-terminated. Otherwise the same as Sys_var_charptr.
- Class specific constructor arguments:
- enum charset_enum is_os_charset_arg
-
Backing store: LEX_CSTRING
@note
@@ -759,14 +805,13 @@ public:
Sys_var_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, PolyLock *lock=0,
enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
on_check_function on_check_func=0,
on_update_function on_update_func=0,
const char *substitute=0)
: Sys_var_charptr(name_arg, comment, flag_args, off, sizeof(char*),
- getopt, is_os_charset_arg, def_val, lock, binlog_status_arg,
+ getopt, def_val, lock, binlog_status_arg,
on_check_func, on_update_func, substitute)
{
global_var(LEX_CSTRING).length= strlen(def_val);
@@ -795,7 +840,6 @@ 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)
@@ -1974,6 +2018,7 @@ public:
thd->sys_var_tmp.double_value= 0;
return (uchar*) &thd->sys_var_tmp.double_value;
}
+ bool on_check_access_session(THD *thd) const;
};
@@ -2512,6 +2557,10 @@ public:
uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
uchar *default_value_ptr(THD *thd)
{ return 0; }
+ bool on_check_access_global(THD *thd) const
+ {
+ return check_global_access(thd, PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_SLAVE_POS);
+ }
};
@@ -2554,6 +2603,11 @@ public:
uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base);
uchar *default_value_ptr(THD *thd)
{ return 0; }
+ bool on_check_access_global(THD *thd) const
+ {
+ return
+ check_global_access(thd, PRIV_SET_SYSTEM_GLOBAL_VAR_GTID_BINLOG_STATE);
+ }
};
diff --git a/sql/table.cc b/sql/table.cc
index 38777de34a7..c48a6fed89a 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -25,9 +25,9 @@
// primary_key_name
#include "sql_parse.h" // free_items
#include "strfunc.h" // unhex_type2
-#include "sql_partition.h" // mysql_unpack_partition,
+#include "ha_partition.h" // PART_EXT
+ // mysql_unpack_partition,
// fix_partition_func, partition_info
-#include "sql_acl.h" // *_ACL, acl_getroot_no_password
#include "sql_base.h"
#include "create_options.h"
#include "sql_trigger.h"
@@ -70,6 +70,8 @@ struct extra2_fields
LEX_CUSTRING field_flags;
LEX_CUSTRING system_period;
LEX_CUSTRING application_period;
+ LEX_CUSTRING field_data_type_info;
+ LEX_CUSTRING without_overlaps;
void reset()
{ bzero((void*)this, sizeof(*this)); }
};
@@ -286,7 +288,7 @@ TABLE_CATEGORY get_table_category(const LEX_CSTRING *db,
if (is_infoschema_db(db))
return TABLE_CATEGORY_INFORMATION;
- if (lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, db))
+ if (is_perfschema_db(db))
return TABLE_CATEGORY_PERFORMANCE;
if (lex_string_eq(&MYSQL_SCHEMA_NAME, db))
@@ -336,7 +338,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
path_length= build_table_filename(path, sizeof(path) - 1,
db, table_name, "", 0);
- init_sql_alloc(&mem_root, "table_share", TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ init_sql_alloc(key_memory_table_share, &mem_root, TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(0));
if (multi_alloc_root(&mem_root,
&share, sizeof(*share),
&key_buff, key_length,
@@ -359,11 +362,10 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
if (share->table_category == TABLE_CATEGORY_LOG)
share->no_replicate= 1;
if (key_length > 6 &&
- my_strnncoll(table_alias_charset, (const uchar*) key, 6,
- (const uchar*) "mysql", 6) == 0)
+ table_alias_charset->strnncoll(key, 6, "mysql", 6) == 0)
share->not_usable_by_query_cache= 1;
- init_sql_alloc(&share->stats_cb.mem_root, "share_stats",
+ init_sql_alloc(PSI_INSTRUMENT_ME, &share->stats_cb.mem_root,
TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
@@ -425,8 +427,9 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
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, "tmp_table_share", TABLE_ALLOC_BLOCK_SIZE,
- 0, MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC));
+ init_sql_alloc(key_memory_table_share, &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;
@@ -626,9 +629,13 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
path);
if (flags & GTS_FORCE_DISCOVERY)
{
+ const char *path2= share->normalized_path.str;
DBUG_ASSERT(flags & GTS_TABLE);
DBUG_ASSERT(flags & GTS_USE_DISCOVERY);
- mysql_file_delete_with_symlink(key_file_frm, path, "", MYF(0));
+ /* Delete .frm and .par files */
+ mysql_file_delete_with_symlink(key_file_frm, path2, reg_ext, MYF(0));
+ mysql_file_delete_with_symlink(key_file_partition_ddl_log, path2, PAR_EXT,
+ MYF(0));
file= -1;
}
else
@@ -686,7 +693,8 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
frmlen= uint4korr(head+10);
set_if_smaller(frmlen, FRM_MAX_SIZE); // safety
- if (!(buf= (uchar*)my_malloc(frmlen, MYF(MY_THREAD_SPECIFIC|MY_WME))))
+ if (!(buf= (uchar*)my_malloc(PSI_INSTRUMENT_ME, frmlen,
+ MYF(MY_THREAD_SPECIFIC|MY_WME))))
goto err;
memcpy(buf, head, sizeof(head));
@@ -987,6 +995,38 @@ void Column_definition_attributes::frm_unpack_basic(const uchar *buff)
}
+void Column_definition_attributes::frm_pack_numeric_with_dec(uchar *buff) const
+{
+ DBUG_ASSERT(f_decimals(pack_flag) == 0);
+ uint tmp_pack_flag= pack_flag | (decimals << FIELDFLAG_DEC_SHIFT);
+ int2store(buff + 3, length);
+ int2store(buff + 8, tmp_pack_flag);
+ buff[10]= (uchar) unireg_check;
+}
+
+
+bool
+Column_definition_attributes::frm_unpack_numeric_with_dec(TABLE_SHARE *share,
+ const uchar *buff)
+{
+ frm_unpack_basic(buff);
+ decimals= f_decimals(pack_flag);
+ pack_flag&= ~FIELDFLAG_DEC_MASK;
+ return frm_unpack_charset(share, buff);
+}
+
+
+bool
+Column_definition_attributes::frm_unpack_temporal_with_dec(TABLE_SHARE *share,
+ uint intlen,
+ const uchar *buff)
+{
+ frm_unpack_basic(buff);
+ decimals= temporal_dec(intlen);
+ return frm_unpack_charset(share, buff);
+}
+
+
void Column_definition_attributes::frm_pack_charset(uchar *buff) const
{
buff[11]= (uchar) (charset->number >> 8);
@@ -1100,7 +1140,6 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
Field **vfield_ptr= table->vfield;
Field **dfield_ptr= table->default_field;
Virtual_column_info **check_constraint_ptr= table->check_constraints;
- sql_mode_t saved_mode= thd->variables.sql_mode;
Query_arena backup_arena;
Virtual_column_info *vcol= 0;
StringBuffer<MAX_FIELD_WIDTH> expr_str;
@@ -1127,7 +1166,7 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
thd->stmt_arena= table->expr_arena;
thd->update_charset(&my_charset_utf8mb4_general_ci, table->s->table_charset);
expr_str.append(&parse_vcol_keyword);
- thd->variables.sql_mode &= ~MODE_NO_BACKSLASH_ESCAPES;
+ Sql_mode_instant_remove sms(thd, MODE_NO_BACKSLASH_ESCAPES);
while (pos < end)
{
@@ -1228,10 +1267,10 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
for (key_index= 0; key_index < table->s->keys; key_index++)
{
key=table->key_info + key_index;
- parts= key->user_defined_key_parts;
+ parts= key->user_defined_key_parts;
if (key->key_part[parts].fieldnr == field->field_index + 1)
- break;
- }
+ break;
+ }
if (!key || key->algorithm != HA_KEY_ALG_LONG_HASH)
goto end;
KEY_PART_INFO *keypart;
@@ -1310,7 +1349,6 @@ end:
thd->stmt_arena= backup_stmt_arena_ptr;
if (save_character_set_client)
thd->update_charset(save_character_set_client, save_collation);
- thd->variables.sql_mode= saved_mode;
DBUG_RETURN(res);
}
@@ -1484,6 +1522,14 @@ static size_t extra2_read_len(const uchar **extra2, const uchar *extra2_end)
return length;
}
+static
+bool read_extra2_section_once(const uchar *extra2, size_t len, LEX_CUSTRING *section)
+{
+ if (section->str)
+ return true;
+ *section= {extra2, len};
+ return false;
+}
static
bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields)
@@ -1503,6 +1549,8 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields)
size_t length= extra2_read_len(&extra2, e2end);
if (!length)
DBUG_RETURN(true);
+
+ bool fail= false;
switch (type) {
case EXTRA2_TABLEDEF_VERSION:
if (fields->version.str) // see init_from_sql_statement_string()
@@ -1517,43 +1565,38 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields)
}
break;
case EXTRA2_ENGINE_TABLEOPTS:
- if (fields->options.str)
- DBUG_RETURN(true);
- fields->options.str= extra2;
- fields->options.length= length;
+ fail= read_extra2_section_once(extra2, length, &fields->options);
break;
case EXTRA2_DEFAULT_PART_ENGINE:
fields->engine.set((const char*)extra2, length);
break;
case EXTRA2_GIS:
- if (fields->gis.str)
- DBUG_RETURN(true);
- fields->gis.str= extra2;
- fields->gis.length= length;
+ fail= read_extra2_section_once(extra2, length, &fields->gis);
break;
case EXTRA2_PERIOD_FOR_SYSTEM_TIME:
- if (fields->system_period.str || length != 2 * frm_fieldno_size)
- DBUG_RETURN(true);
- fields->system_period.str = extra2;
- fields->system_period.length= length;
+ fail= read_extra2_section_once(extra2, length, &fields->system_period)
+ || length != 2 * frm_fieldno_size;
break;
case EXTRA2_FIELD_FLAGS:
- if (fields->field_flags.str)
- DBUG_RETURN(true);
- fields->field_flags.str= extra2;
- fields->field_flags.length= length;
+ fail= read_extra2_section_once(extra2, length, &fields->field_flags);
break;
case EXTRA2_APPLICATION_TIME_PERIOD:
- if (fields->application_period.str)
- DBUG_RETURN(true);
- fields->application_period.str= extra2;
- fields->application_period.length= length;
+ fail= read_extra2_section_once(extra2, length, &fields->application_period);
+ break;
+ case EXTRA2_PERIOD_WITHOUT_OVERLAPS:
+ fail= read_extra2_section_once(extra2, length, &fields->without_overlaps);
+ break;
+ case EXTRA2_FIELD_DATA_TYPE_INFO:
+ fail= read_extra2_section_once(extra2, length, &fields->field_data_type_info);
break;
default:
/* abort frm parsing if it's an unknown but important extra2 value */
if (type >= EXTRA2_ENGINE_IMPORTANT)
DBUG_RETURN(true);
}
+ if (fail)
+ DBUG_RETURN(true);
+
extra2+= length;
}
if (extra2 != e2end)
@@ -1563,9 +1606,92 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields)
}
+class Field_data_type_info_array
+{
+public:
+ class Elem
+ {
+ LEX_CSTRING m_type_info;
+ public:
+ void set(const LEX_CSTRING &type_info)
+ {
+ m_type_info= type_info;
+ }
+ const LEX_CSTRING &type_info() const
+ {
+ return m_type_info;
+ }
+ };
+private:
+ Elem *m_array;
+ uint m_count;
+ bool alloc(MEM_ROOT *root, uint count)
+ {
+ DBUG_ASSERT(!m_array);
+ DBUG_ASSERT(!m_count);
+ size_t nbytes= sizeof(Elem) * count;
+ if (!(m_array= (Elem*) alloc_root(root, nbytes)))
+ return true;
+ m_count= count;
+ bzero((void*) m_array, nbytes);
+ return false;
+ }
+ static uint32 read_length(uchar **pos, const uchar *end)
+ {
+ ulonglong num= safe_net_field_length_ll(pos, end - *pos);
+ if (num > UINT_MAX32)
+ return 0;
+ return (uint32) num;
+ }
+ static bool read_string(LEX_CSTRING *to, uchar **pos, const uchar *end)
+ {
+ to->length= read_length(pos, end);
+ if (*pos + to->length > end)
+ return true; // Not enough data
+ to->str= (const char *) *pos;
+ *pos+= to->length;
+ return false;
+ }
+public:
+ Field_data_type_info_array()
+ :m_array(NULL), m_count(0)
+ { }
+ uint count() const
+ {
+ return m_count;
+ }
+ const Elem& element(uint i) const
+ {
+ DBUG_ASSERT(i < m_count);
+ return m_array[i];
+ }
+ bool parse(MEM_ROOT *root, uint count, LEX_CUSTRING &image)
+ {
+ const uchar *pos= image.str;
+ const uchar *end= pos + image.length;
+ if (alloc(root, count))
+ return true;
+ for (uint i= 0; i < count && pos < end; i++)
+ {
+ LEX_CSTRING type_info;
+ uint fieldnr= read_length((uchar**) &pos, end);
+ if ((fieldnr == 0 && i > 0) || fieldnr >= count)
+ return true; // Bad data
+ if (read_string(&type_info, (uchar**) &pos, end) || type_info.length == 0)
+ return true; // Bad data
+ m_array[fieldnr].set(type_info);
+ }
+ return pos < end; // Error if some data is still left
+ }
+};
+
+
/**
Read data from a binary .frm file image into a TABLE_SHARE
+ @param write Write the .frm and .par file. These are not created if
+ the function returns an error.
+
@note
frm bytes at the following offsets are unused in MariaDB 10.0:
@@ -1576,12 +1702,13 @@ bool read_extra2(const uchar *frm_image, size_t len, extra2_fields *fields)
42..46 are unused since 5.0 (were for RAID support)
Also, there're few unused bytes in forminfo.
-
*/
int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
const uchar *frm_image,
- size_t frm_length)
+ size_t frm_length,
+ const uchar *par_image,
+ size_t par_length)
{
TABLE_SHARE *share= this;
uint new_frm_ver, field_pack_length, new_field_pack_flag;
@@ -1615,23 +1742,31 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
uint len;
uint ext_key_parts= 0;
plugin_ref se_plugin= 0;
- bool vers_can_native= false;
-
+ bool vers_can_native= false, frm_created= 0;
+ Field_data_type_info_array field_data_type_info_array;
MEM_ROOT *old_root= thd->mem_root;
Virtual_column_info **table_check_constraints;
extra2_fields extra2;
-
DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image");
keyinfo= &first_keyinfo;
thd->mem_root= &share->mem_root;
- if (write && write_frm_image(frm_image, frm_length))
- goto err;
-
if (frm_length < FRM_HEADER_SIZE + FRM_FORMINFO_SIZE)
goto err;
+ if (write)
+ {
+ frm_created= 1;
+ if (write_frm_image(frm_image, frm_length))
+ goto err;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (par_image)
+ if (write_par_image(par_image, par_length))
+ goto err;
+#endif
+ }
+
share->frm_version= frm_image[2];
/*
Check if .frm file created by MySQL 5.0. In this case we want to
@@ -1734,7 +1869,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
{
const CHARSET_INFO *cs= thd->variables.collation_database;
/* unknown charset in frm_image[38] or pre-3.23 frm */
- if (use_mb(cs))
+ if (cs->use_mb())
{
/* Warn that we may be changing the size of character columns */
sql_print_warning("'%s' had no or invalid character set, "
@@ -1811,7 +1946,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
name.length= str_db_type_length;
plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name, false);
- if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, se_plugin))
+ if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, se_plugin) &&
+ legacy_db_type != DB_TYPE_S3)
{
if (se_plugin)
{
@@ -1967,6 +2103,17 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (keyinfo->algorithm == HA_KEY_ALG_LONG_HASH)
hash_fields++;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (par_image && plugin_data(se_plugin, handlerton*) == partition_hton)
+ {
+ /*
+ Discovery returned a partition plugin. Change to use it. The partition
+ engine will then use discovery to find the rest of the plugin tables,
+ which may be in the original engine used for discovery
+ */
+ share->db_plugin= se_plugin;
+ }
+#endif
if (share->db_plugin && !plugin_equals(share->db_plugin, se_plugin))
goto err; // wrong engine (someone changed the frm under our feet?)
@@ -2106,10 +2253,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
if (use_hash)
- use_hash= !my_hash_init(&share->name_hash,
- system_charset_info,
- share->fields,0,0,
- (my_hash_get_key) get_field_name,0,0);
+ use_hash= !my_hash_init(PSI_INSTRUMENT_ME, &share->name_hash,
+ system_charset_info, share->fields, 0, 0,
+ (my_hash_get_key) get_field_name, 0, 0);
if (share->mysql_version >= 50700 && share->mysql_version < 100000 &&
vcol_screen_length)
@@ -2162,9 +2308,37 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
if (init_period_from_extra2(&period, pos, end))
goto err;
+ if (extra2_str_size(period.name.length)
+ + extra2_str_size(period.constr_name.length)
+ + 2 * frm_fieldno_size
+ != extra2.application_period.length)
+ goto err;
status_var_increment(thd->status_var.feature_application_time_periods);
}
+ if (extra2.without_overlaps.str)
+ {
+ if (extra2.application_period.str == NULL)
+ goto err;
+ const uchar *key_pos= extra2.without_overlaps.str;
+ period.unique_keys= read_frm_keyno(key_pos);
+ for (uint k= 0; k < period.unique_keys; k++)
+ {
+ key_pos+= frm_keyno_size;
+ uint key_nr= read_frm_keyno(key_pos);
+ key_info[key_nr].without_overlaps= true;
+ }
+
+ if ((period.unique_keys + 1) * frm_keyno_size
+ != extra2.without_overlaps.length)
+ goto err;
+ }
+
+ if (extra2.field_data_type_info.length &&
+ field_data_type_info_array.parse(old_root, share->fields,
+ extra2.field_data_type_info))
+ goto err;
+
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
{
uint interval_nr= 0, recpos;
@@ -2193,7 +2367,8 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
comment_pos+= comment_length;
}
- if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL)
+ if ((uchar) strpos[13] == (uchar) MYSQL_TYPE_VIRTUAL
+ && likely(share->mysql_version >= 100000))
{
/*
MariaDB version 10.0 version.
@@ -2243,11 +2418,53 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
interval_nr= (uint) strpos[12];
enum_field_types field_type= (enum_field_types) strpos[13];
if (!(handler= Type_handler::get_handler_by_real_type(field_type)))
- goto err; // Not supported field type
+ {
+ if (field_type == 245 &&
+ share->mysql_version >= 50700) // a.k.a MySQL 5.7 JSON
+ {
+ share->incompatible_version|= HA_CREATE_USED_ENGINE;
+ const LEX_CSTRING mysql_json{STRING_WITH_LEN("MYSQL_JSON")};
+ handler= Type_handler::handler_by_name_or_error(thd, mysql_json);
+ }
+
+ if (!handler)
+ goto err; // Not supported field type
+ }
+ handler= handler->type_handler_frm_unpack(strpos);
if (handler->Column_definition_attributes_frm_unpack(&attr, share,
strpos,
&extra2.gis))
goto err;
+
+ if (field_data_type_info_array.count())
+ {
+ const LEX_CSTRING &info= field_data_type_info_array.
+ element(i).type_info();
+ DBUG_EXECUTE_IF("frm_data_type_info",
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR, "DBUG: [%u] name='%s' type_info='%.*s'",
+ i, share->fieldnames.type_names[i],
+ (uint) info.length, info.str););
+
+ if (info.length)
+ {
+ const Type_handler *h= Type_handler::handler_by_name_or_error(thd,
+ info);
+ /*
+ This code will eventually be extended here:
+ - If the handler was not found by name, we could
+ still open the table using the fallback type handler "handler",
+ at least for a limited set of commands.
+ - If the handler was found by name, we could check
+ that "h" and "handler" have the same type code
+ (and maybe some other properties) to make sure
+ that the FRM data is consistent.
+ */
+ if (!h)
+ goto err;
+ handler= h;
+ }
+ }
}
if (((uint) strpos[10]) & MYSQL57_GENERATED_FIELD)
@@ -2279,6 +2496,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
attr.length= (uint) strpos[3];
recpos= uint2korr(strpos+4),
attr.pack_flag= uint2korr(strpos+6);
+ if (f_is_num(attr.pack_flag))
+ {
+ attr.decimals= f_decimals(attr.pack_flag);
+ attr.pack_flag&= ~FIELDFLAG_DEC_MASK;
+ }
attr.pack_flag&= ~FIELDFLAG_NO_DEFAULT; // Safety for old files
attr.unireg_check= (Field::utype) MTYP_TYPENR((uint) strpos[8]);
interval_nr= (uint) strpos[10];
@@ -2607,7 +2829,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part +
(keyinfo-1)->ext_key_parts;
uint add_keyparts_for_this_key= add_first_key_parts;
- uint length_bytes= 0, len_null_byte= 0, ext_key_length= 0;
+ uint len_null_byte= 0, ext_key_length= 0;
Field *field;
if ((keyinfo-1)->algorithm == HA_KEY_ALG_LONG_HASH)
@@ -2619,19 +2841,15 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
*/
for (i= 0; i < keyinfo->user_defined_key_parts; i++)
{
+ uint length_bytes= 0;
uint fieldnr= keyinfo->key_part[i].fieldnr;
field= share->field[fieldnr-1];
if (field->null_ptr)
len_null_byte= HA_KEY_NULL_LENGTH;
- if ((field->type() == MYSQL_TYPE_BLOB ||
- field->real_type() == MYSQL_TYPE_VARCHAR ||
- field->type() == MYSQL_TYPE_GEOMETRY) &&
- keyinfo->algorithm != HA_KEY_ALG_LONG_HASH )
- {
- length_bytes= HA_KEY_BLOB_LENGTH;
- }
+ if (keyinfo->algorithm != HA_KEY_ALG_LONG_HASH)
+ length_bytes= field->key_part_length_bytes();
ext_key_length+= keyinfo->key_part[i].length + len_null_byte
+ length_bytes;
@@ -2720,20 +2938,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
keyinfo->flags|=HA_NULL_PART_KEY;
keyinfo->key_length+= HA_KEY_NULL_LENGTH;
}
- if (field->type() == MYSQL_TYPE_BLOB ||
- field->real_type() == MYSQL_TYPE_VARCHAR ||
- field->type() == MYSQL_TYPE_GEOMETRY)
- {
- if (field->type() == MYSQL_TYPE_BLOB ||
- field->type() == MYSQL_TYPE_GEOMETRY)
- key_part->key_part_flag|= HA_BLOB_PART;
- else
- key_part->key_part_flag|= HA_VAR_LENGTH_PART;
- key_part->store_length+=HA_KEY_BLOB_LENGTH;
- keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
- }
- if (field->type() == MYSQL_TYPE_BIT)
- key_part->key_part_flag|= HA_BIT_PART;
+
+ key_part->key_part_flag|= field->key_part_flag();
+ uint16 key_part_length_bytes= field->key_part_length_bytes();
+ key_part->store_length+= key_part_length_bytes;
+ keyinfo->key_length+= key_part_length_bytes;
if (i == 0 && key != primary_key)
field->flags |= (((keyinfo->flags & HA_NOSAME ||
@@ -3055,6 +3264,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
DBUG_RETURN(0);
err:
+ if (frm_created)
+ {
+ char path[FN_REFLEN+1];
+ strxnmov(path, FN_REFLEN, normalized_path.str, reg_ext, NullS);
+ my_delete(path, MYF(0));
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (par_image)
+ {
+ strxnmov(path, FN_REFLEN, normalized_path.str, PAR_EXT, NullS);
+ my_delete(path, MYF(0));
+ }
+#endif
+ }
share->db_plugin= NULL;
share->error= OPEN_FRM_CORRUPTED;
share->open_errno= my_errno;
@@ -3128,7 +3350,6 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
const char *sql, size_t sql_length)
{
- sql_mode_t saved_mode= thd->variables.sql_mode;
CHARSET_INFO *old_cs= thd->variables.character_set_client;
Parser_state parser_state;
bool error;
@@ -3156,7 +3377,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
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;
+ Sql_mode_instant_set sms(thd, 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;
@@ -3205,7 +3426,6 @@ ret:
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 (unlikely(thd->is_error() || error))
{
@@ -3222,7 +3442,19 @@ ret:
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);
+ char file_name[FN_REFLEN+1];
+ strxnmov(file_name, sizeof(file_name)-1, normalized_path.str, reg_ext,
+ NullS);
+ return writefile(file_name, db.str, table_name.str, false,
+ frm, len);
+}
+
+bool TABLE_SHARE::write_par_image(const uchar *par, size_t len)
+{
+ char file_name[FN_REFLEN+1];
+ strxnmov(file_name, sizeof(file_name)-1, normalized_path.str, PAR_EXT,
+ NullS);
+ return writefile(file_name, db.str, table_name.str, false, par, len);
}
@@ -3366,7 +3598,6 @@ static bool fix_and_check_vcol_expr(THD *thd, TABLE *table,
to be part of the virtual column
*/
Item::vcol_func_processor_result res;
- res.errors= 0;
int error= func_expr->walk(&Item::check_vcol_func_processor, 0, &res);
if (unlikely(error || (res.errors & VCOL_IMPOSSIBLE)))
@@ -3582,6 +3813,55 @@ static void print_long_unique_table(TABLE *table)
}
#endif
+bool copy_keys_from_share(TABLE *outparam, MEM_ROOT *root)
+{
+ TABLE_SHARE *share= outparam->s;
+ if (share->key_parts)
+ {
+ KEY *key_info, *key_info_end;
+ KEY_PART_INFO *key_part;
+
+ if (!multi_alloc_root(root, &key_info, share->keys*sizeof(KEY),
+ &key_part, share->ext_key_parts*sizeof(KEY_PART_INFO),
+ NullS))
+ return 1;
+
+ outparam->key_info= key_info;
+
+ memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys);
+ memcpy(key_part, key_info->key_part, sizeof(*key_part)*share->ext_key_parts);
+
+ my_ptrdiff_t adjust_ptrs= PTR_BYTE_DIFF(key_part, key_info->key_part);
+ for (key_info_end= key_info + share->keys ;
+ key_info < key_info_end ;
+ key_info++)
+ {
+ key_info->table= outparam;
+ key_info->key_part= reinterpret_cast<KEY_PART_INFO*>
+ (reinterpret_cast<char*>(key_info->key_part) + adjust_ptrs);
+ if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
+ key_info->flags&= ~HA_NOSAME;
+ }
+ for (KEY_PART_INFO *key_part_end= key_part+share->ext_key_parts;
+ key_part < key_part_end;
+ key_part++)
+ {
+ Field *field= key_part->field= outparam->field[key_part->fieldnr - 1];
+ if (field->key_length() != key_part->length &&
+ !(field->flags & BLOB_FLAG))
+ {
+ /*
+ We are using only a prefix of the column as a key:
+ Create a new field for the key part that matches the index
+ */
+ field= key_part->field=field->make_new_field(root, outparam, 0);
+ field->field_length= key_part->length;
+ }
+ }
+ }
+ return 0;
+}
+
/*
Open a table based on a TABLE_SHARE
@@ -3635,14 +3915,15 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
outparam->write_row_record= NULL;
if (share->incompatible_version &&
- !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR)))
+ !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR |
+ HA_OPEN_FOR_FLUSH)))
{
/* one needs to run mysql_upgrade on the table */
error= OPEN_FRM_NEEDS_REBUILD;
goto err;
}
- init_sql_alloc(&outparam->mem_root, "table", TABLE_ALLOC_BLOCK_SIZE, 0,
- MYF(0));
+ init_sql_alloc(key_memory_TABLE, &outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE,
+ 0, MYF(0));
/*
We have to store the original alias in mem_root as constraints and virtual
@@ -3720,6 +4001,15 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
sizeof(Field*)))))
goto err; /* purecov: inspected */
+ /* Allocate storage for range optimizer */
+ if (!multi_alloc_root(&outparam->mem_root,
+ &outparam->opt_range,
+ share->keys * sizeof(TABLE::OPT_RANGE),
+ &outparam->const_key_parts,
+ share->keys * sizeof(key_part_map),
+ NullS))
+ goto err;
+
outparam->field= field_ptr;
record= (uchar*) outparam->record[0]-1; /* Fieldstart = 1 */
@@ -3745,57 +4035,8 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
outparam->found_next_number_field=
outparam->field[(uint) (share->found_next_number_field - share->field)];
- /* Fix key->name and key_part->field */
- if (share->key_parts)
- {
- KEY *key_info, *key_info_end;
- KEY_PART_INFO *key_part;
- uint n_length;
- n_length= share->keys*sizeof(KEY) + share->ext_key_parts*sizeof(KEY_PART_INFO);
- if (!(key_info= (KEY*) alloc_root(&outparam->mem_root, n_length)))
- goto err;
- outparam->key_info= key_info;
- key_part= (reinterpret_cast<KEY_PART_INFO*>(key_info+share->keys));
-
- memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys);
- memcpy(key_part, share->key_info[0].key_part, (sizeof(*key_part) *
- share->ext_key_parts));
-
- for (key_info_end= key_info + share->keys ;
- key_info < key_info_end ;
- key_info++)
- {
- KEY_PART_INFO *key_part_end;
-
- key_info->table= outparam;
- key_info->key_part= key_part;
-
- key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts :
- key_info->user_defined_key_parts) ;
- if (key_info->algorithm == HA_KEY_ALG_LONG_HASH)
- {
- key_part_end++;
- key_info->flags&= ~HA_NOSAME;
- }
- for ( ; key_part < key_part_end; key_part++)
- {
- Field *field= key_part->field= outparam->field[key_part->fieldnr - 1];
- if (field->key_length() != key_part->length &&
- !(field->flags & BLOB_FLAG))
- {
- /*
- We are using only a prefix of the column as a key:
- Create a new field for the key part that matches the index
- */
- field= key_part->field=field->make_new_field(&outparam->mem_root,
- outparam, 0);
- const_cast<uint32_t&>(field->field_length)= key_part->length;
- }
- }
- if (!share->use_ext_keys)
- key_part+= key_info->ext_key_parts - key_info->user_defined_key_parts;
- }
- }
+ if (copy_keys_from_share(outparam, &outparam->mem_root))
+ goto err;
/*
Process virtual and default columns, if any.
@@ -3826,7 +4067,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
outparam->check_constraints= check_constraint_ptr;
vcol_init_mode mode= VCOL_INIT_DEPENDENCY_FAILURE_IS_WARNING;
-#if MYSQL_VERSION_ID > 100500
switch (thd->lex->sql_command)
{
case SQLCOM_CREATE_TABLE:
@@ -3841,7 +4081,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
default:
break;
}
-#endif
if (parse_vcol_defs(thd, &outparam->mem_root, outparam,
&error_reported, mode))
@@ -4000,7 +4239,7 @@ partititon_err:
the fact that table doesn't in fact exist and remove
the stray .frm file.
*/
- if (share->db_type()->discover_table &&
+ if (outparam->file->partition_ht()->discover_table &&
(ha_err == ENOENT || ha_err == HA_ERR_NO_SUCH_TABLE))
error= OPEN_FRM_DISCOVER;
@@ -4520,21 +4759,17 @@ bool get_field(MEM_ROOT *mem, Field *field, String *res)
StringBuffer<MAX_FIELD_WIDTH> str;
bool rc;
THD *thd= field->get_thd();
- sql_mode_t sql_mode_backup= thd->variables.sql_mode;
- thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
+ Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
field->val_str(&str);
if ((rc= !str.length() ||
!(to= strmake_root(mem, str.ptr(), str.length()))))
{
res->length(0);
- goto ex;
+ return rc;
}
res->set(to, str.length(), field->charset());
-
-ex:
- thd->variables.sql_mode= sql_mode_backup;
- return rc;
+ return false;
}
@@ -4678,7 +4913,7 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars
{
#if defined(USE_MB) && defined(USE_MB_IDENT)
last_char_is_space= my_isspace(system_charset_info, *name);
- if (use_mb(system_charset_info))
+ if (system_charset_info->use_mb())
{
int len=my_ismbchar(system_charset_info, name, end);
if (len)
@@ -4713,7 +4948,7 @@ bool check_column_name(const char *name)
{
#if defined(USE_MB) && defined(USE_MB_IDENT)
last_char_is_space= my_isspace(system_charset_info, *name);
- if (use_mb(system_charset_info))
+ if (system_charset_info->use_mb())
{
int len=my_ismbchar(system_charset_info, name,
name+system_charset_info->mbmaxlen);
@@ -5181,17 +5416,14 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
range_rowid_filter_cost_info_elems= 0;
range_rowid_filter_cost_info_ptr= NULL;
range_rowid_filter_cost_info= NULL;
- update_handler= NULL;
- check_unique_buf= NULL;
vers_write= s->versioned;
- quick_condition_rows=0;
+ opt_range_condition_rows=0;
no_cache= false;
- initialize_quick_structures();
+ initialize_opt_range_structures();
#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;
@@ -5205,6 +5437,7 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
(*f_ptr)->cond_selectivity= 1.0;
}
+ notnull_cond= 0;
DBUG_ASSERT(!file->keyread_enabled());
restore_record(this, s->default_values);
@@ -6088,7 +6321,7 @@ TABLE_LIST *TABLE_LIST::last_leaf_for_name_resolution()
want_access Acess which we require
*/
-void TABLE_LIST::register_want_access(ulong want_access)
+void TABLE_LIST::register_want_access(privilege_t want_access)
{
/* Remove SHOW_VIEW_ACL, because it will be checked during making view */
want_access&= ~SHOW_VIEW_ACL;
@@ -6143,7 +6376,7 @@ bool TABLE_LIST::prepare_view_security_context(THD *thd)
}
else
{
- if (thd->security_ctx->master_access & SUPER_ACL)
+ if (thd->security_ctx->master_access & PRIV_REVEAL_MISSING_DEFINER)
{
my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str);
@@ -6257,7 +6490,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
thd->security_ctx= save_security_ctx;
#else
while ((tbl= tb++))
- tbl->grant.privilege= ~NO_ACCESS;
+ tbl->grant.privilege= ALL_KNOWN_ACL;
#endif
DBUG_RETURN(FALSE);
}
@@ -6523,8 +6756,8 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
&view->view->first_select_lex()->context:
&thd->lex->first_select_lex()->context);
Item *item= (new (thd->mem_root)
- Item_direct_view_ref(thd, context, field_ref, view->alias.str,
- name, view));
+ Item_direct_view_ref(thd, context, field_ref, view->alias,
+ *name, view));
if (!item)
return NULL;
/*
@@ -7219,8 +7452,7 @@ void TABLE::mark_columns_per_binlog_row_image()
If in RBR we may need to mark some extra columns,
depending on the binlog-row-image command line argument.
*/
- if ((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
- thd->is_current_stmt_binlog_format_row() &&
+ if (file->row_logging &&
!ha_check_storage_engine_flag(s->db_type(), HTON_NO_BINLOG_ROW_OPT))
{
/* if there is no PK, then mark all columns for the BI. */
@@ -7575,12 +7807,28 @@ void TABLE::restore_blob_values(String *blob_storage)
bool TABLE::alloc_keys(uint key_count)
{
- key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*(s->keys+key_count));
+ KEY *new_key_info;
+ key_part_map *new_const_key_parts;
+ DBUG_ASSERT(s->tmp_table == INTERNAL_TMP_TABLE);
+
+ if (!multi_alloc_root(&mem_root,
+ &new_key_info, sizeof(*key_info)*(s->keys+key_count),
+ &new_const_key_parts,
+ sizeof(*new_const_key_parts)*(s->keys+key_count),
+ NullS))
+ return TRUE;
if (s->keys)
- memmove(key_info, s->key_info, sizeof(KEY)*s->keys);
- s->key_info= key_info;
+ {
+ memmove(new_key_info, s->key_info, sizeof(*key_info) * s->keys);
+ memmove(new_const_key_parts, const_key_parts,
+ s->keys * sizeof(const_key_parts));
+ }
+ s->key_info= key_info= new_key_info;
+ const_key_parts= new_const_key_parts;
+ bzero((char*) (const_key_parts + s->keys),
+ sizeof(*const_key_parts) * key_count);
max_keys= s->keys+key_count;
- return !(key_info);
+ return FALSE;
}
@@ -7639,14 +7887,9 @@ void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info,
{
key_part_info->store_length+= HA_KEY_NULL_LENGTH;
}
- if (field->type() == MYSQL_TYPE_BLOB ||
- field->type() == MYSQL_TYPE_GEOMETRY ||
- field->real_type() == MYSQL_TYPE_VARCHAR)
- {
- key_part_info->store_length+= HA_KEY_BLOB_LENGTH;
- key_part_info->key_part_flag|=
- field->type() == MYSQL_TYPE_BLOB ? HA_BLOB_PART: HA_VAR_LENGTH_PART;
- }
+
+ key_part_info->key_part_flag|= field->key_part_flag();
+ key_part_info->store_length+= field->key_part_length_bytes();
key_part_info->type= (uint8) field->key_type();
key_part_info->key_type =
@@ -8061,7 +8304,7 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
(pos= find_type(&tbl->s->keynames, hint->key_name.str,
hint->key_name.length, 1)) <= 0)
{
- my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), hint->key_name.str, alias.str);
+ my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0), hint->key_name.str, alias.str);
return 1;
}
@@ -8166,11 +8409,10 @@ size_t max_row_length(TABLE *table, MY_BITMAP const *cols, const uchar *data)
void init_mdl_requests(TABLE_LIST *table_list)
{
for ( ; table_list ; table_list= table_list->next_global)
- table_list->mdl_request.init(MDL_key::TABLE,
- table_list->db.str, table_list->table_name.str,
- table_list->lock_type >= TL_WRITE_ALLOW_WRITE ?
- MDL_SHARED_WRITE : MDL_SHARED_READ,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&table_list->mdl_request, MDL_key::TABLE,
+ table_list->db.str, table_list->table_name.str,
+ table_list->lock_type >= TL_WRITE_ALLOW_WRITE
+ ? MDL_SHARED_WRITE : MDL_SHARED_READ, MDL_TRANSACTION);
}
@@ -8565,6 +8807,40 @@ void TABLE::evaluate_update_default_function()
DBUG_VOID_RETURN;
}
+/**
+ Compare two records by a specific key (that has WITHOUT OVERLAPS clause)
+
+ @return true, key values are equal and periods overlap
+ false, either key values differ or periods don't overlap
+ */
+bool TABLE::check_period_overlaps(const KEY &key,
+ const uchar *lhs, const uchar *rhs)
+{
+ DBUG_ASSERT(key.without_overlaps);
+ uint base_part_nr= key.user_defined_key_parts - 2;
+ for (uint part_nr= 0; part_nr < base_part_nr; part_nr++)
+ {
+ Field *f= key.key_part[part_nr].field;
+ if (key.key_part[part_nr].null_bit)
+ if (f->is_null_in_record(lhs) || f->is_null_in_record(rhs))
+ return false;
+ uint kp_len= key.key_part[part_nr].length;
+ if (f->cmp_prefix(f->ptr_in_record(lhs), f->ptr_in_record(rhs),
+ kp_len) != 0)
+ return false;
+ }
+
+ uint period_start= key.user_defined_key_parts - 1;
+ uint period_end= key.user_defined_key_parts - 2;
+ const Field *fs= key.key_part[period_start].field;
+ const Field *fe= key.key_part[period_end].field;
+
+ if (fs->cmp(fe->ptr_in_record(lhs), fs->ptr_in_record(rhs)) <= 0)
+ return false;
+ if (fs->cmp(fs->ptr_in_record(lhs), fe->ptr_in_record(rhs)) >= 0)
+ return false;
+ return true;
+}
void TABLE::vers_update_fields()
{
@@ -9130,8 +9406,8 @@ void TABLE_LIST::set_lock_type(THD *thd, enum thr_lock_type lock)
/* we call it only when table is opened and it is "leaf" table*/
DBUG_ASSERT(table);
lock_type= lock;
- /* table->file->get_table() can be 0 for derived tables */
- if (table->file && table->file->get_table())
+ /* If not derived tables */
+ if (table->file && table->file->is_open())
table->file->set_lock_type(lock);
if (is_merged_derived())
{
@@ -9203,35 +9479,6 @@ void re_setup_keyinfo_hash(KEY *key_info)
key_info->ext_key_parts= 1;
key_info->flags&= ~HA_NOSAME;
}
-/**
- @brief clone of current handler.
- Creates a clone of handler used in update for
- unique hash key.
-*/
-void TABLE::clone_handler_for_update()
-{
- if (this->update_handler)
- return;
- handler *update_handler= NULL;
- if (!s->long_unique_table)
- return;
- update_handler= file->clone(s->normalized_path.str,
- in_use->mem_root);
- update_handler->ha_external_lock(in_use, F_RDLCK);
- this->update_handler= update_handler;
- return;
-}
-
-/**
- @brief Deletes update handler object
-*/
-void TABLE::delete_update_handler()
-{
- update_handler->ha_external_lock(in_use, F_UNLCK);
- update_handler->ha_close();
- delete update_handler;
- this->update_handler= NULL;
-}
LEX_CSTRING *fk_option_name(enum_fk_option opt)
{
@@ -9564,7 +9811,7 @@ bool TR_table::check(bool error)
}
Field_enum *iso_level= static_cast<Field_enum *>(table->field[FLD_ISO_LEVEL]);
- st_typelib *typelib= iso_level->typelib;
+ const st_typelib *typelib= iso_level->typelib;
if (typelib->count != 4)
goto wrong_enum;
@@ -9590,12 +9837,12 @@ bool TR_table::check(bool error)
return false;
}
-bool vers_select_conds_t::resolve_units(THD *thd)
+bool vers_select_conds_t::check_units(THD *thd)
{
DBUG_ASSERT(type != SYSTEM_TIME_UNSPECIFIED);
DBUG_ASSERT(start.item);
- return start.resolve_unit(thd) ||
- end.resolve_unit(thd);
+ return start.check_unit(thd) ||
+ end.check_unit(thd);
}
bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const
@@ -9621,22 +9868,21 @@ bool vers_select_conds_t::eq(const vers_select_conds_t &conds) const
}
-bool Vers_history_point::resolve_unit(THD *thd)
+bool Vers_history_point::check_unit(THD *thd)
{
if (!item)
return false;
if (item->fix_fields_if_needed(thd, &item))
return true;
- return item->this_item()->real_type_handler()->
- type_handler_for_system_time()->
- Vers_history_point_resolve_unit(thd, this);
-}
-
-
-void Vers_history_point::bad_expression_data_type_error(const char *type) const
-{
- my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
- type, "FOR SYSTEM_TIME");
+ const Type_handler *t= item->this_item()->real_type_handler();
+ DBUG_ASSERT(t);
+ if (!t->vers())
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
+ t->name().ptr(), "FOR SYSTEM_TIME");
+ return true;
+ }
+ return false;
}
@@ -9710,22 +9956,20 @@ bool TABLE::export_structure(THD *thd, Row_definition_list *defs)
return false;
}
-/*
+/**
@brief
- Initialize all the quick structures that are used to stored the
+ Initialize all the opt_range structures that are used to stored the
estimates when the range optimizer is run.
- @details
- This is specifically needed when we read the TABLE structure from the
- table cache. There can be some garbage data from previous queries
- that need to be reset here.
+ As these are initialized by the range optimizer for all index
+ marked in opt_range_keys, we only mark the memory as undefined
+ to be able to find wrong usage of data with valgrind or MSAN.
*/
-void TABLE::initialize_quick_structures()
+inline void TABLE::initialize_opt_range_structures()
{
- TRASH_ALLOC(quick_rows, sizeof(quick_rows));
- TRASH_ALLOC(quick_key_parts, sizeof(quick_key_parts));
- TRASH_ALLOC(quick_costs, sizeof(quick_costs));
- TRASH_ALLOC(quick_n_ranges, sizeof(quick_n_ranges));
+ TRASH_ALLOC((void*)&opt_range_keys, sizeof(opt_range_keys));
+ TRASH_ALLOC(opt_range, s->keys * sizeof(*opt_range));
+ TRASH_ALLOC(const_key_parts, s->keys * sizeof(*const_key_parts));
}
/*
diff --git a/sql/table.h b/sql/table.h
index 264ece6ac6d..ec865f2e3ce 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -32,6 +32,9 @@
#include "thr_lock.h" /* thr_lock_type */
#include "filesort_utils.h"
#include "parse_file.h"
+#include "sql_i_s.h"
+#include "sql_type.h" /* vers_kind_t */
+#include "privilege.h" /* privilege_t */
/* Structs that defines the TABLE */
@@ -308,19 +311,25 @@ typedef struct st_grant_info
The set is implemented as a bitmap, with the bits defined in sql_acl.h.
*/
- ulong privilege;
+ privilege_t privilege;
/**
@brief the set of privileges that the current user needs to fulfil in
order to carry out the requested operation.
*/
- ulong want_privilege;
+ privilege_t want_privilege;
/**
Stores the requested access acl of top level tables list. Is used to
check access rights to the underlying tables of a view.
*/
- ulong orig_want_privilege;
+ privilege_t orig_want_privilege;
/** The grant state for internal tables. */
GRANT_INTERNAL_INFO m_internal;
+
+ st_grant_info()
+ :privilege(NO_ACL),
+ want_privilege(NO_ACL),
+ orig_want_privilege(NO_ACL)
+ { }
} GRANT_INFO;
enum tmp_table_type
@@ -874,7 +883,7 @@ struct TABLE_SHARE
#endif
/**
- System versioning support.
+ System versioning and application-time periods support.
*/
struct period_info_t
{
@@ -882,6 +891,7 @@ struct TABLE_SHARE
uint16 end_fieldno;
Lex_ident name;
Lex_ident constr_name;
+ uint unique_keys;
Field *start_field(TABLE_SHARE *s) const
{
return s->field[start_fieldno];
@@ -892,7 +902,7 @@ struct TABLE_SHARE
}
};
- vers_sys_type_t versioned;
+ vers_kind_t versioned;
period_info_t vers;
period_info_t period;
@@ -901,14 +911,28 @@ struct TABLE_SHARE
Field *vers_start_field()
{
+ DBUG_ASSERT(versioned);
return field[vers.start_fieldno];
}
Field *vers_end_field()
{
+ DBUG_ASSERT(versioned);
return field[vers.end_fieldno];
}
+ Field *period_start_field() const
+ {
+ DBUG_ASSERT(period.name);
+ return field[period.start_fieldno];
+ }
+
+ Field *period_end_field() const
+ {
+ DBUG_ASSERT(period.name);
+ return field[period.end_fieldno];
+ }
+
/**
Cache the checked structure of this table.
@@ -1094,7 +1118,9 @@ struct TABLE_SHARE
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);
+ const uchar *frm_image, size_t frm_length,
+ const uchar *par_image=0,
+ size_t par_length=0);
/*
populates TABLE_SHARE from the table description, specified as the
@@ -1109,7 +1135,9 @@ struct TABLE_SHARE
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_par_image(const uchar *par_image, size_t par_length);
+ /* Only used by tokudb */
bool write_frm_image(void)
{ return frm_image ? write_frm_image(frm_image->str, frm_image->length) : 0; }
@@ -1146,8 +1174,8 @@ private:
public:
Blob_mem_storage() :truncated_value(false)
{
- init_alloc_root(&storage, "Blob_mem_storage", MAX_FIELD_VARCHARLENGTH, 0,
- MYF(0));
+ init_alloc_root(key_memory_blob_mem_storage,
+ &storage, MAX_FIELD_VARCHARLENGTH, 0, MYF(0));
}
~ Blob_mem_storage()
{
@@ -1193,9 +1221,6 @@ struct st_cond_statistic;
#define CHECK_ROW_FOR_NULLS_TO_REJECT (1 << 0)
#define REJECT_ROW_DUE_TO_NULL_FIELDS (1 << 1)
-/* Bitmap of table's fields */
-typedef Bitmap<MAX_FIELDS> Field_map;
-
class SplM_opt_info;
struct vers_select_conds_t;
@@ -1225,9 +1250,6 @@ public:
THD *in_use; /* Which thread uses this */
uchar *record[3]; /* Pointer to records */
- /* record buf to resolve hash collisions for long UNIQUE constraints */
- uchar *check_unique_buf;
- handler *update_handler; /* Handler used in case of update */
uchar *write_row_record; /* Used as optimisation in
THD::write_row */
uchar *insert_values; /* used by INSERT ... UPDATE */
@@ -1235,8 +1257,7 @@ public:
Map of keys that can be used to retrieve all data from this table
needed by the query without reading the row.
*/
- key_map covering_keys;
- key_map quick_keys, intersect_keys;
+ key_map covering_keys, intersect_keys;
/*
A set of keys that can be used in the query that references this
table.
@@ -1318,28 +1339,29 @@ public:
/* The estimate of the number of records in the table used by optimizer */
ha_rows used_stat_records;
+ key_map opt_range_keys;
/*
- For each key that has quick_keys.is_set(key) == TRUE: estimate of #records
- and max #key parts that range access would use.
+ The following structure is filled for each key that has
+ opt_range_keys.is_set(key) == TRUE
*/
- ha_rows quick_rows[MAX_KEY];
- uint quick_key_parts[MAX_KEY];
-
- double quick_costs[MAX_KEY];
- /*
- If there is a range access by i-th index then the cost of
- index only access for it is stored in quick_index_only_costs[i]
- */
- double quick_index_only_costs[MAX_KEY];
-
+ struct OPT_RANGE
+ {
+ uint key_parts;
+ uint ranges;
+ ha_rows rows;
+ double cost;
+ /*
+ If there is a range access by i-th index then the cost of
+ index only access for it is stored in index_only_costs[i]
+ */
+ double index_only_cost;
+ } *opt_range;
/*
- 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.
+ 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_n_ranges[MAX_KEY];
+ key_part_map *const_key_parts;
/*
Estimate of number of records that satisfy SARGable part of the table
@@ -1349,7 +1371,7 @@ public:
that will pass the table condition (condition that depends on fields of
this table and constants)
*/
- ha_rows quick_condition_rows;
+ ha_rows opt_range_condition_rows;
double cond_selectivity;
List<st_cond_statistic> *cond_selectivity_sampling_explain;
@@ -1503,6 +1525,13 @@ public:
SplM_opt_info *spl_opt_info;
key_map keys_usable_for_splitting;
+ /*
+ Conjunction of the predicates of the form IS NOT NULL(f) where f refers to
+ a column of this TABLE such that they can be inferred from the condition
+ of the WHERE clause or from some ON expression of the processed select
+ and can be useful for range optimizer.
+ */
+ Item *notnull_cond;
inline void reset() { bzero((void*)this, sizeof(*this)); }
void init(THD *thd, TABLE_LIST *tl);
@@ -1615,7 +1644,8 @@ public:
bool is_filled_at_execution();
bool update_const_key_parts(COND *conds);
- void initialize_quick_structures();
+
+ inline void initialize_opt_range_structures();
my_ptrdiff_t default_values_offset() const
{ return (my_ptrdiff_t) (s->default_values - record[0]); }
@@ -1677,13 +1707,11 @@ public:
bool versioned() const
{
- DBUG_ASSERT(s);
return s->versioned;
}
- bool versioned(vers_sys_type_t type) const
+ bool versioned(vers_kind_t type) const
{
- DBUG_ASSERT(s);
DBUG_ASSERT(type);
return s->versioned == type;
}
@@ -1694,7 +1722,7 @@ public:
return versioned() ? vers_write : false;
}
- bool versioned_write(vers_sys_type_t type) const
+ bool versioned_write(vers_kind_t type) const
{
DBUG_ASSERT(type);
DBUG_ASSERT(versioned() || !vers_write);
@@ -1703,16 +1731,29 @@ public:
Field *vers_start_field() const
{
- DBUG_ASSERT(s && s->versioned);
+ DBUG_ASSERT(s->versioned);
return field[s->vers.start_fieldno];
}
Field *vers_end_field() const
{
- DBUG_ASSERT(s && s->versioned);
+ DBUG_ASSERT(s->versioned);
return field[s->vers.end_fieldno];
}
+ Field *period_start_field() const
+ {
+ DBUG_ASSERT(s->period.name);
+ return field[s->period.start_fieldno];
+ }
+
+ Field *period_end_field() const
+ {
+ DBUG_ASSERT(s->period.name);
+ return field[s->period.end_fieldno];
+ }
+
+
ulonglong vers_start_id() const;
ulonglong vers_end_id() const;
@@ -1721,13 +1762,11 @@ public:
int insert_portion_of_time(THD *thd, const vers_select_conds_t &period_conds,
ha_rows *rows_inserted);
bool vers_check_update(List<Item> &items);
-
+ static bool check_period_overlaps(const KEY &key, const uchar *lhs, const uchar *rhs);
int delete_row();
void vers_update_fields();
void vers_update_end();
void find_constraint_correlated_indexes();
- void clone_handler_for_update();
- void delete_update_handler();
/** Number of additional fields used in versioned tables */
#define VERSIONING_FIELDS 2
@@ -1792,71 +1831,6 @@ typedef struct st_foreign_key_info
LEX_CSTRING *fk_option_name(enum_fk_option opt);
bool fk_modifies_child(enum_fk_option opt);
-#define MY_I_S_MAYBE_NULL 1U
-#define MY_I_S_UNSIGNED 2U
-
-
-#define SKIP_OPEN_TABLE 0U // do not open table
-#define OPEN_FRM_ONLY 1U // open FRM file only
-#define OPEN_FULL_TABLE 2U // open FRM,MYD, MYI files
-
-typedef struct st_field_info
-{
- /**
- This is used as column name.
- */
- const char* field_name;
- /**
- For string-type columns, this is the maximum number of
- characters. Otherwise, it is the 'display-length' for the column.
- */
- uint field_length;
- /**
- This denotes data type for the column. For the most part, there seems to
- be one entry in the enum for each SQL data type, although there seem to
- be a number of additional entries in the enum.
- */
- enum enum_field_types field_type;
- int value;
- /**
- This is used to set column attributes. By default, columns are @c NOT
- @c NULL and @c SIGNED, and you can deviate from the default
- by setting the appopriate flags. You can use either one of the flags
- @c MY_I_S_MAYBE_NULL and @cMY_I_S_UNSIGNED or
- combine them using the bitwise or operator @c |. Both flags are
- defined in table.h.
- */
- uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.)
- const char* old_name;
- /**
- This should be one of @c SKIP_OPEN_TABLE,
- @c OPEN_FRM_ONLY or @c OPEN_FULL_TABLE.
- */
- uint open_method;
-} ST_FIELD_INFO;
-
-
-struct TABLE_LIST;
-typedef class Item COND;
-
-typedef struct st_schema_table
-{
- const char *table_name;
- ST_FIELD_INFO *fields_info;
- /* for FLUSH table_name */
- int (*reset_table) ();
- /* Fill table with data */
- int (*fill_table) (THD *thd, TABLE_LIST *tables, COND *cond);
- /* Handle fileds for old SHOW */
- int (*old_format) (THD *thd, struct st_schema_table *schema_table);
- int (*process_table) (THD *thd, TABLE_LIST *tables, TABLE *table,
- bool res, const LEX_CSTRING *db_name,
- const LEX_CSTRING *table_name);
- int idx_field1, idx_field2;
- bool hidden;
- uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */
-} ST_SCHEMA_TABLE;
-
class IS_table_read_plan;
/*
@@ -1926,10 +1900,18 @@ class IS_table_read_plan;
/** number of bytes used by field positional indexes in frm */
constexpr uint frm_fieldno_size= 2;
+/** number of bytes used by key position number in frm */
+constexpr uint frm_keyno_size= 2;
static inline uint16 read_frm_fieldno(const uchar *data)
{ return uint2korr(data); }
-static inline void store_frm_fieldno(const uchar *data, uint16 fieldno)
+static inline void store_frm_fieldno(uchar *data, uint16 fieldno)
+{ int2store(data, fieldno); }
+static inline uint16 read_frm_keyno(const uchar *data)
+{ return uint2korr(data); }
+static inline void store_frm_keyno(uchar *data, uint16 fieldno)
{ int2store(data, fieldno); }
+static inline size_t extra2_str_size(size_t len)
+{ return (len > 255 ? 3 : 1) + len; }
class select_unit;
class TMP_TABLE_PARAM;
@@ -1993,7 +1975,7 @@ class Item_in_subselect;
/* trivial class, for %union in sql_yacc.yy */
struct vers_history_point_t
{
- vers_sys_type_t unit;
+ vers_kind_t unit;
Item *item;
};
@@ -2003,7 +1985,7 @@ class Vers_history_point : public vers_history_point_t
public:
Vers_history_point() { empty(); }
- Vers_history_point(vers_sys_type_t unit_arg, Item *item_arg)
+ Vers_history_point(vers_kind_t unit_arg, Item *item_arg)
{
unit= unit_arg;
item= item_arg;
@@ -2015,21 +1997,9 @@ public:
item= p.item;
fix_item();
}
- void empty() { unit= VERS_UNDEFINED; item= NULL; }
+ void empty() { unit= VERS_TIMESTAMP; item= NULL; }
void print(String *str, enum_query_type, const char *prefix, size_t plen) const;
- bool resolve_unit(THD *thd);
- bool resolve_unit_trx_id(THD *thd)
- {
- if (unit == VERS_UNDEFINED)
- unit= VERS_TRX_ID;
- return false;
- }
- bool resolve_unit_timestamp(THD *thd)
- {
- if (unit == VERS_UNDEFINED)
- unit= VERS_TIMESTAMP;
- return false;
- }
+ bool check_unit(THD *thd);
void bad_expression_data_type_error(const char *type) const;
bool eq(const vers_history_point_t &point) const;
};
@@ -2088,6 +2058,7 @@ struct vers_select_conds_t
{
return type != SYSTEM_TIME_UNSPECIFIED;
}
+ bool check_units(THD *thd);
bool was_set() const
{
return orig_type != SYSTEM_TIME_UNSPECIFIED;
@@ -2096,7 +2067,6 @@ struct vers_select_conds_t
{
return type != SYSTEM_TIME_UNSPECIFIED && type != SYSTEM_TIME_ALL;
}
- bool resolve_units(THD *thd);
bool eq(const vers_select_conds_t &conds) const;
};
@@ -2175,8 +2145,8 @@ struct TABLE_LIST
alias= (alias_arg ? *alias_arg : *table_name_arg);
lock_type= lock_type_arg;
updating= lock_type >= TL_WRITE_ALLOW_WRITE;
- mdl_request.init(MDL_key::TABLE, db.str, table_name.str, mdl_type,
- MDL_TRANSACTION);
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE, db.str, table_name.str,
+ mdl_type, MDL_TRANSACTION);
}
TABLE_LIST(TABLE *table_arg, thr_lock_type lock_type)
@@ -2680,7 +2650,7 @@ struct TABLE_LIST
return FALSE;
}
- void register_want_access(ulong want_access);
+ void register_want_access(privilege_t want_access);
bool prepare_security(THD *thd);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *find_view_security_context(THD *thd);
@@ -2730,22 +2700,10 @@ struct TABLE_LIST
}
/* Set of functions returning/setting state of a derived table/view. */
- inline bool is_non_derived()
- {
- return (!derived_type);
- }
- inline bool is_view_or_derived()
- {
- return (derived_type);
- }
- inline bool is_view()
- {
- return (derived_type & DTYPE_VIEW);
- }
- inline bool is_derived()
- {
- return (derived_type & DTYPE_TABLE);
- }
+ bool is_non_derived() const { return (!derived_type); }
+ bool is_view_or_derived() const { return derived_type; }
+ bool is_view() const { return (derived_type & DTYPE_VIEW); }
+ bool is_derived() const { return (derived_type & DTYPE_TABLE); }
bool is_with_table();
bool is_recursive_with_table();
bool is_with_table_recursive_reference();
@@ -2761,22 +2719,19 @@ struct TABLE_LIST
{
derived_type= DTYPE_TABLE;
}
- inline bool is_merged_derived()
- {
- return (derived_type & DTYPE_MERGE);
- }
+ bool is_merged_derived() const { return (derived_type & DTYPE_MERGE); }
inline void set_merged_derived()
{
DBUG_ENTER("set_merged_derived");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
(alias.str ? alias.str : "<NULL>"),
get_unit()));
- derived_type= ((derived_type & DTYPE_MASK) |
- DTYPE_TABLE | DTYPE_MERGE);
+ derived_type= static_cast<uint8>((derived_type & DTYPE_MASK) |
+ DTYPE_TABLE | DTYPE_MERGE);
set_check_merged();
DBUG_VOID_RETURN;
}
- inline bool is_materialized_derived()
+ bool is_materialized_derived() const
{
return (derived_type & DTYPE_MATERIALIZE);
}
@@ -2787,15 +2742,13 @@ struct TABLE_LIST
(alias.str ? alias.str : "<NULL>"),
get_unit()));
derived= get_unit();
- derived_type= ((derived_type & (derived ? DTYPE_MASK : DTYPE_VIEW)) |
- DTYPE_TABLE | DTYPE_MATERIALIZE);
+ derived_type= static_cast<uint8>((derived_type &
+ (derived ? DTYPE_MASK : DTYPE_VIEW)) |
+ DTYPE_TABLE | DTYPE_MATERIALIZE);
set_check_materialized();
DBUG_VOID_RETURN;
}
- inline bool is_multitable()
- {
- return (derived_type & DTYPE_MULTITABLE);
- }
+ bool is_multitable() const { return (derived_type & DTYPE_MULTITABLE); }
inline void set_multitable()
{
derived_type|= DTYPE_MULTITABLE;
@@ -3166,6 +3119,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
uint ha_open_flags, TABLE *outparam,
bool is_create_table,
List<String> *partitions_to_open= NULL);
+bool copy_keys_from_share(TABLE *outparam, MEM_ROOT *root);
bool fix_session_vcol_expr(THD *thd, Virtual_column_info *vcol);
bool fix_session_vcol_expr_for_read(THD *thd, Field *field,
Virtual_column_info *vcol);
@@ -3228,9 +3182,12 @@ extern LEX_CSTRING MYSQL_PROC_NAME;
inline bool is_infoschema_db(const LEX_CSTRING *name)
{
- return (INFORMATION_SCHEMA_NAME.length == name->length &&
- !my_strcasecmp(system_charset_info,
- INFORMATION_SCHEMA_NAME.str, name->str));
+ return lex_string_eq(&INFORMATION_SCHEMA_NAME, name);
+}
+
+inline bool is_perfschema_db(const LEX_CSTRING *name)
+{
+ return lex_string_eq(&PERFORMANCE_SCHEMA_DB_NAME, name);
}
inline void mark_as_null_row(TABLE *table)
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
index 15255c56083..62ccfba7e7d 100644
--- a/sql/table_cache.cc
+++ b/sql/table_cache.cc
@@ -68,7 +68,6 @@ I_P_List <TDC_element,
I_P_List_null_counter,
I_P_List_fast_push_back<TDC_element> > unused_shares;
-static tdc_version_t tdc_version; /* Increments on each reload */
static bool tdc_inited;
@@ -261,25 +260,12 @@ static void tc_remove_table(TABLE *table)
static void tc_remove_all_unused_tables(TDC_element *element,
- Share_free_tables::List *purge_tables,
- bool mark_flushed)
+ Share_free_tables::List *purge_tables)
{
- TABLE *table;
-
- /*
- 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 (mark_flushed)
- element->flushed= true;
for (uint32 i= 0; i < tc_instances; i++)
{
mysql_mutex_lock(&tc[i].LOCK_table_cache);
- while ((table= element->free_tables[i].list.pop_front()))
+ while (auto table= element->free_tables[i].list.pop_front())
{
tc[i].records--;
tc[i].free_tables.remove(table);
@@ -307,30 +293,22 @@ static void tc_remove_all_unused_tables(TDC_element *element,
periodicly flush all not used tables.
*/
-struct tc_purge_arg
-{
- Share_free_tables::List purge_tables;
- bool mark_flushed;
-};
-
-
-static my_bool tc_purge_callback(TDC_element *element, tc_purge_arg *arg)
+static my_bool tc_purge_callback(TDC_element *element,
+ Share_free_tables::List *purge_tables)
{
mysql_mutex_lock(&element->LOCK_table_share);
- tc_remove_all_unused_tables(element, &arg->purge_tables, arg->mark_flushed);
+ tc_remove_all_unused_tables(element, purge_tables);
mysql_mutex_unlock(&element->LOCK_table_share);
return FALSE;
}
-void tc_purge(bool mark_flushed)
+void tc_purge()
{
- tc_purge_arg argument;
- TABLE *table;
+ Share_free_tables::List purge_tables;
- argument.mark_flushed= mark_flushed;
- tdc_iterate(0, (my_hash_walk_action) tc_purge_callback, &argument);
- while ((table= argument.purge_tables.pop_front()))
+ tdc_iterate(0, (my_hash_walk_action) tc_purge_callback, &purge_tables);
+ while (auto table= purge_tables.pop_front())
intern_close_table(table);
}
@@ -621,7 +599,6 @@ bool tdc_init(void)
tdc_inited= true;
mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
MY_MUTEX_INIT_FAST);
- tdc_version= 1L; /* Increments on each reload */
lf_hash_init(&tdc_hash, sizeof(TDC_element) +
sizeof(Share_free_tables) * (tc_instances - 1),
LF_HASH_UNIQUE, 0, 0,
@@ -654,7 +631,7 @@ void tdc_start_shutdown(void)
tdc_size= 0;
tc_size= 0;
/* Free all cached but unused TABLEs and TABLE_SHAREs. */
- purge_tables(true);
+ purge_tables();
}
DBUG_VOID_RETURN;
}
@@ -778,6 +755,23 @@ void tdc_unlock_share(TDC_element *element)
}
+int tdc_share_is_cached(THD *thd, const char *db, const char *table_name)
+{
+ char key[MAX_DBKEY_LENGTH];
+
+ if (unlikely(fix_thd_pins(thd)))
+ return -1;
+
+ if (lf_hash_search(&tdc_hash, thd->tdc_hash_pins, (uchar*) key,
+ tdc_create_key(key, db, table_name)))
+ {
+ lf_hash_search_unpin(thd->tdc_hash_pins);
+ return 1;
+ }
+ return 0;
+}
+
+
/*
Get TABLE_SHARE for a table.
@@ -847,7 +841,6 @@ retry:
element->share= share;
share->tdc= element;
element->ref_count++;
- element->version= tdc_refresh_version();
element->flushed= false;
mysql_mutex_unlock(&element->LOCK_table_share);
@@ -962,9 +955,9 @@ void tdc_release_share(TABLE_SHARE *share)
mysql_mutex_lock(&share->tdc->LOCK_table_share);
DBUG_PRINT("enter",
- ("share: %p table: %s.%s ref_count: %u version: %lld",
+ ("share: %p table: %s.%s ref_count: %u",
share, share->db.str, share->table_name.str,
- share->tdc->ref_count, share->tdc->version));
+ share->tdc->ref_count));
DBUG_ASSERT(share->tdc->ref_count);
if (share->tdc->ref_count > 1)
@@ -1003,108 +996,45 @@ void tdc_release_share(TABLE_SHARE *share)
}
-/**
- Auxiliary function which allows to kill delayed threads for
- particular table identified by its share.
-
- @param share Table share.
-
- @pre Caller should have TABLE_SHARE::tdc.LOCK_table_share mutex.
-*/
-
-static void kill_delayed_threads_for_table(TDC_element *element)
+void tdc_remove_referenced_share(THD *thd, TABLE_SHARE *share)
{
- All_share_tables_list::Iterator it(element->all_tables);
- TABLE *tab;
-
- mysql_mutex_assert_owner(&element->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)
- {
- in_use->killed= KILL_SYSTEM_THREAD;
- mysql_mutex_lock(&in_use->mysys_var->mutex);
- if (in_use->mysys_var->current_cond)
- {
- mysql_mutex_lock(in_use->mysys_var->current_mutex);
- mysql_cond_broadcast(in_use->mysys_var->current_cond);
- mysql_mutex_unlock(in_use->mysys_var->current_mutex);
- }
- mysql_mutex_unlock(&in_use->mysys_var->mutex);
- }
- }
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, share->db.str,
+ share->table_name.str,
+ MDL_EXCLUSIVE));
+ share->tdc->flush_unused(false);
+ mysql_mutex_lock(&share->tdc->LOCK_table_share);
+ share->tdc->wait_for_refs(1);
+ DBUG_ASSERT(share->tdc->all_tables.is_empty());
+ share->tdc->ref_count--;
+ tdc_delete_share_from_hash(share->tdc);
}
/**
- Remove all or some (depending on parameter) instances of TABLE and
- TABLE_SHARE from the table definition cache.
+ Removes all TABLE instances and corresponding TABLE_SHARE
@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)
+void tdc_remove_table(THD *thd, const char *db, const char *table_name)
{
- Share_free_tables::List purge_tables;
- TABLE *table;
TDC_element *element;
- uint my_refs= 1;
- bool res= false;
DBUG_ENTER("tdc_remove_table");
- DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
+ DBUG_PRINT("enter", ("name: %s", table_name));
- DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
- thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
MDL_EXCLUSIVE));
mysql_mutex_lock(&LOCK_unused_shares);
if (!(element= tdc_lock_share(thd, db, table_name)))
{
mysql_mutex_unlock(&LOCK_unused_shares);
- DBUG_ASSERT(remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
- DBUG_RETURN(false);
+ DBUG_VOID_RETURN;
}
DBUG_ASSERT(element != MY_ERRPTR); // What can we do about it?
@@ -1120,82 +1050,16 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
mysql_mutex_unlock(&LOCK_unused_shares);
tdc_delete_share_from_hash(element);
- DBUG_RETURN(false);
+ DBUG_VOID_RETURN;
}
mysql_mutex_unlock(&LOCK_unused_shares);
element->ref_count++;
-
- tc_remove_all_unused_tables(element, &purge_tables,
- remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
-
- if (kill_delayed_threads)
- kill_delayed_threads_for_table(element);
-
- if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
- remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
- {
- All_share_tables_list::Iterator it(element->all_tables);
- while ((table= it++))
- {
- if (table->in_use == thd)
- my_refs++;
- }
- }
mysql_mutex_unlock(&element->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(&element->LOCK_table_share);
- while (element->ref_count > my_refs)
- mysql_cond_wait(&element->COND_release, &element->LOCK_table_share);
- DBUG_ASSERT(element->all_tables.is_empty() ||
- remove_type != TDC_RT_REMOVE_ALL);
-#ifndef DBUG_OFF
- if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
- remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
- {
- All_share_tables_list::Iterator it(element->all_tables);
- while ((table= it++))
- DBUG_ASSERT(table->in_use == thd);
- }
-#endif
- mysql_mutex_unlock(&element->LOCK_table_share);
- }
- else
- {
- mysql_mutex_lock(&element->LOCK_table_share);
- res= element->ref_count > 1;
- mysql_mutex_unlock(&element->LOCK_table_share);
- }
-
- tdc_release_share(element->share);
-
- DBUG_RETURN(res);
+ /* We have to relock the mutex to avoid code duplication. Sigh. */
+ tdc_remove_referenced_share(thd, element->share);
+ DBUG_VOID_RETURN;
}
@@ -1214,7 +1078,7 @@ bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
*/
int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
- ulong wait_timeout, uint deadlock_weight, tdc_version_t refresh_version)
+ ulong wait_timeout, uint deadlock_weight)
{
TDC_element *element;
@@ -1222,7 +1086,7 @@ int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
return FALSE;
else if (element == MY_ERRPTR)
return TRUE;
- else if (element->flushed && refresh_version > element->version)
+ else if (element->flushed)
{
struct timespec abstime;
set_timespec(abstime, wait_timeout);
@@ -1233,20 +1097,6 @@ int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
}
-tdc_version_t tdc_refresh_version(void)
-{
- return (tdc_version_t)my_atomic_load64_explicit(&tdc_version, MY_MEMORY_ORDER_RELAXED);
-}
-
-
-tdc_version_t tdc_increment_refresh_version(void)
-{
- tdc_version_t v= (tdc_version_t)my_atomic_add64_explicit(&tdc_version, 1, MY_MEMORY_ORDER_RELAXED);
- DBUG_PRINT("tcache", ("incremented global refresh_version to: %lld", v));
- return v + 1;
-}
-
-
/**
Iterate table definition cache.
@@ -1317,10 +1167,10 @@ int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
if (no_dups)
{
- init_alloc_root(&no_dups_argument.root, "no_dups", 4096, 4096,
- MYF(alloc_flags));
- my_hash_init(&no_dups_argument.hash, &my_charset_bin, tdc_records(), 0, 0,
- eliminate_duplicates_get_key, 0, hash_flags);
+ init_alloc_root(PSI_INSTRUMENT_ME, &no_dups_argument.root, 4096, 4096, MYF(alloc_flags));
+ my_hash_init(PSI_INSTRUMENT_ME, &no_dups_argument.hash, &my_charset_bin,
+ tdc_records(), 0, 0, eliminate_duplicates_get_key, 0,
+ hash_flags);
no_dups_argument.action= action;
no_dups_argument.argument= argument;
action= (my_hash_walk_action) eliminate_duplicates;
@@ -1350,3 +1200,94 @@ int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
tc_active_instances.load(std::memory_order_relaxed);
return 0;
}
+
+
+/**
+ Waits until ref_count goes down to given number
+
+ @param my_refs Number of references owned by the caller
+
+ Caller must own at least one TABLE_SHARE reference.
+
+ Even though current thread holds exclusive metadata lock on this share,
+ 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.
+*/
+
+void TDC_element::wait_for_refs(uint my_refs)
+{
+ while (ref_count > my_refs)
+ mysql_cond_wait(&COND_release, &LOCK_table_share);
+}
+
+
+/**
+ Flushes unused TABLE instances
+
+ @param thd Thread context
+ @param mark_flushed Whether to destroy TABLE_SHARE when released
+
+ Caller is allowed to own used TABLE instances.
+ There must be no TABLE objects used by other threads and caller must own
+ exclusive metadata lock on the table.
+*/
+
+void TDC_element::flush(THD *thd, bool mark_flushed)
+{
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, share->db.str,
+ share->table_name.str,
+ MDL_EXCLUSIVE));
+
+ flush_unused(mark_flushed);
+
+ mysql_mutex_lock(&LOCK_table_share);
+ All_share_tables_list::Iterator it(all_tables);
+ uint my_refs= 0;
+ while (auto table= it++)
+ {
+ if (table->in_use == thd)
+ my_refs++;
+ }
+ wait_for_refs(my_refs);
+#ifndef DBUG_OFF
+ it.rewind();
+ while (auto table= it++)
+ DBUG_ASSERT(table->in_use == thd);
+#endif
+ mysql_mutex_unlock(&LOCK_table_share);
+}
+
+
+/**
+ Flushes unused TABLE instances
+*/
+
+void TDC_element::flush_unused(bool mark_flushed)
+{
+ Share_free_tables::List purge_tables;
+
+ mysql_mutex_lock(&LOCK_table_share);
+ if (mark_flushed)
+ flushed= true;
+ tc_remove_all_unused_tables(this, &purge_tables);
+ mysql_mutex_unlock(&LOCK_table_share);
+
+ while (auto table= purge_tables.pop_front())
+ intern_close_table(table);
+}
diff --git a/sql/table_cache.h b/sql/table_cache.h
index 3be7b5fe413..433df5e0328 100644
--- a/sql/table_cache.h
+++ b/sql/table_cache.h
@@ -26,14 +26,11 @@ struct Share_free_tables
char pad[CPU_LEVEL1_DCACHE_LINESIZE];
};
-typedef int64 tdc_version_t;
-#define TDC_VERSION_MAX INT_MAX64
struct TDC_element
{
uchar m_key[NAME_LEN + 1 + NAME_LEN + 1];
uint m_key_length;
- tdc_version_t version;
bool flushed;
TABLE_SHARE *share;
@@ -57,17 +54,13 @@ struct TDC_element
/** Avoid false sharing between TDC_element and free_tables */
char pad[CPU_LEVEL1_DCACHE_LINESIZE];
Share_free_tables free_tables[1];
-};
-
-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
+ inline void wait_for_refs(uint my_refs);
+ void flush(THD *thd, bool mark_flushed);
+ void flush_unused(bool mark_flushed);
};
+
extern ulong tdc_size;
extern ulong tc_size;
extern uint32 tc_instances;
@@ -80,26 +73,23 @@ extern void tdc_purge(bool all);
extern TDC_element *tdc_lock_share(THD *thd, const char *db,
const char *table_name);
extern void tdc_unlock_share(TDC_element *element);
+int tdc_share_is_cached(THD *thd, const char *db, const char *table_name);
extern TABLE_SHARE *tdc_acquire_share(THD *thd, TABLE_LIST *tl, uint flags,
TABLE **out_table= 0);
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);
+void tdc_remove_referenced_share(THD *thd, TABLE_SHARE *share);
+void tdc_remove_table(THD *thd, const char *db, const char *table_name);
extern int tdc_wait_for_old_version(THD *thd, const char *db,
const char *table_name,
- ulong wait_timeout, uint deadlock_weight,
- tdc_version_t refresh_version= TDC_VERSION_MAX);
-extern tdc_version_t tdc_refresh_version(void);
-extern tdc_version_t tdc_increment_refresh_version(void);
+ ulong wait_timeout, uint deadlock_weight);
extern int tdc_iterate(THD *thd, my_hash_walk_action action, void *argument,
bool no_dups= false);
extern uint tc_records(void);
int show_tc_active_instances(THD *thd, SHOW_VAR *var, char *buff,
enum enum_var_type scope);
-extern void tc_purge(bool mark_flushed= false);
+extern void tc_purge();
extern void tc_add_table(THD *thd, TABLE *table);
extern void tc_release_table(TABLE *table);
extern TABLE *tc_acquire_table(THD *thd, TDC_element *element);
diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc
index f2b27b7056b..1a8b5c471bd 100644
--- a/sql/temporary_tables.cc
+++ b/sql/temporary_tables.cc
@@ -338,9 +338,11 @@ bool THD::open_temporary_table(TABLE_LIST *tl)
have invalid db or table name.
Instead THD::open_tables() should be used.
*/
- DBUG_ASSERT(!tl->derived && !tl->schema_table);
+ DBUG_ASSERT(!tl->derived);
+ DBUG_ASSERT(!tl->schema_table);
+ DBUG_ASSERT(has_temporary_tables());
- if (tl->open_type == OT_BASE_ONLY || !has_temporary_tables())
+ if (tl->open_type == OT_BASE_ONLY)
{
DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
DBUG_RETURN(false);
@@ -452,10 +454,13 @@ bool THD::open_temporary_table(TABLE_LIST *tl)
*/
bool THD::open_temporary_tables(TABLE_LIST *tl)
{
+ TABLE_LIST *first_not_own;
DBUG_ENTER("THD::open_temporary_tables");
- TABLE_LIST *first_not_own= lex->first_not_own_table();
+ if (!has_temporary_tables())
+ DBUG_RETURN(0);
+ first_not_own= lex->first_not_own_table();
for (TABLE_LIST *table= tl; table && table != first_not_own;
table= table->next_global)
{
@@ -689,23 +694,19 @@ bool THD::rm_temporary_table(handlerton *base, const char *path)
DBUG_ENTER("THD::rm_temporary_table");
bool error= false;
- handler *file;
char frm_path[FN_REFLEN + 1];
strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS);
- if (mysql_file_delete(key_file_frm, frm_path, MYF(0)))
- {
+ if (mysql_file_delete(key_file_frm, frm_path,
+ MYF(MY_WME | MY_IGNORE_ENOENT)))
error= true;
- }
- file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
- if (file && file->ha_delete_table(path))
+ if (base->drop_table(base, path) > 0)
{
error= true;
sql_print_warning("Could not remove temporary table: '%s', error: %d",
path, my_errno);
}
- delete file;
DBUG_RETURN(error);
}
@@ -748,11 +749,7 @@ void THD::mark_tmp_tables_as_free_for_reuse()
while ((table= tables_it++))
{
if ((table->query_id == query_id) && !table->open_by_handler)
- {
- if (table->update_handler)
- table->delete_update_handler();
mark_tmp_table_as_free_for_reuse(table);
- }
}
}
@@ -872,7 +869,7 @@ void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share)
@return false Temporary tables exist
true No temporary table exist
*/
-inline bool THD::has_temporary_tables()
+bool THD::has_temporary_tables()
{
DBUG_ENTER("THD::has_temporary_tables");
bool result= (rgi_slave
@@ -955,7 +952,8 @@ TMP_TABLE_SHARE *THD::create_temporary_table(LEX_CUSTRING *frm,
/* Create the table definition key for the temporary table. */
key_length= create_tmp_table_def_key(key_cache, db, table_name);
- if (!(share= (TMP_TABLE_SHARE *) my_malloc(sizeof(TMP_TABLE_SHARE) +
+ if (!(share= (TMP_TABLE_SHARE *) my_malloc(key_memory_table_share,
+ sizeof(TMP_TABLE_SHARE) +
strlen(path) + 1 + key_length,
MYF(MY_WME))))
{
@@ -1002,7 +1000,8 @@ TMP_TABLE_SHARE *THD::create_temporary_table(LEX_CUSTRING *frm,
if (!temporary_tables)
{
if ((temporary_tables=
- (All_tmp_tables_list *) my_malloc(sizeof(All_tmp_tables_list),
+ (All_tmp_tables_list *) my_malloc(key_memory_table_share,
+ sizeof(All_tmp_tables_list),
MYF(MY_WME))))
{
temporary_tables->empty();
@@ -1107,7 +1106,8 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
DBUG_ENTER("THD::open_temporary_table");
- if (!(table= (TABLE *) my_malloc(sizeof(TABLE), MYF(MY_WME))))
+ if (!(table= (TABLE *) my_malloc(key_memory_TABLE, sizeof(TABLE),
+ MYF(MY_WME))))
{
DBUG_RETURN(NULL); /* Out of memory */
}
@@ -1125,13 +1125,11 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share,
table->reginfo.lock_type= TL_WRITE; /* Simulate locked */
table->grant.privilege= TMP_TABLE_ACLS;
+ table->query_id= query_id;
share->tmp_table= (table->file->has_transaction_manager() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
share->not_usable_by_query_cache= 1;
- table->pos_in_table_list= 0;
- table->query_id= query_id;
-
/* Add table to the head of table list. */
share->all_tmp_tables.push_front(table);
@@ -1400,7 +1398,7 @@ bool THD::log_events_and_free_tmp_shares()
variables.character_set_client= cs_save;
get_stmt_da()->set_overwrite_status(true);
- transaction.stmt.mark_dropped_temp_table();
+ transaction->stmt.mark_dropped_temp_table();
bool error2= mysql_bin_log.write(&qinfo);
if (unlikely(error|= error2))
{
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index d43e7c4c9b5..89ad359befa 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -58,11 +58,10 @@ extern "C" {
}
}
-void init_sql_alloc(MEM_ROOT *mem_root,
- const char *area_name __attribute__((unused)),
- uint block_size, uint pre_alloc, myf my_flags)
+void init_sql_alloc(PSI_memory_key key, MEM_ROOT *mem_root, uint block_size,
+ uint pre_alloc, myf my_flags)
{
- init_alloc_root(mem_root, area_name, block_size, pre_alloc, my_flags);
+ init_alloc_root(key, mem_root, block_size, pre_alloc, my_flags);
mem_root->error_handler=sql_alloc_error_handler;
}
diff --git a/sql/thr_malloc.h b/sql/thr_malloc.h
index a6ab5477d41..cc56666bcc9 100644
--- a/sql/thr_malloc.h
+++ b/sql/thr_malloc.h
@@ -18,8 +18,8 @@
typedef struct st_mem_root MEM_ROOT;
-void init_sql_alloc(MEM_ROOT *root, const char *area_name, uint block_size,
- uint pre_alloc_size, myf my_flags);
+void init_sql_alloc(PSI_memory_key key, MEM_ROOT *root, uint block_size, uint
+ pre_alloc_size, myf my_flags);
char *sql_strmake_with_convert(THD *thd, const char *str, size_t arg_length,
CHARSET_INFO *from_cs,
size_t max_res_length,
diff --git a/sql/thread_cache.h b/sql/thread_cache.h
new file mode 100644
index 00000000000..5cb6c0fe85c
--- /dev/null
+++ b/sql/thread_cache.h
@@ -0,0 +1,210 @@
+/*
+ Copyright (C) 2020 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-1335 USA
+*/
+
+
+/**
+ MariaDB thread cache for "one thread per connection" scheduler.
+
+ Thread cache allows to re-use threads (as well as THD objects) for
+ subsequent connections.
+*/
+class Thread_cache
+{
+ mutable mysql_cond_t COND_thread_cache;
+ mutable mysql_cond_t COND_flush_thread_cache;
+ mutable mysql_mutex_t LOCK_thread_cache;
+ /** Queue of new connection requests. */
+ I_List<CONNECT> list;
+ /** Number of threads parked in the cache. */
+ ulong cached_thread_count;
+ /** Number of active flush requests. */
+ uint32_t kill_cached_threads;
+ /**
+ PFS stuff, only used during initialization.
+ Unfortunately needs to survive till destruction.
+ */
+ PSI_cond_key key_COND_thread_cache, key_COND_flush_thread_cache;
+ PSI_mutex_key key_LOCK_thread_cache;
+
+public:
+ void init()
+ {
+#ifdef HAVE_PSI_INTERFACE
+ PSI_cond_info conds[]=
+ {
+ { &key_COND_thread_cache, "COND_thread_cache", PSI_FLAG_GLOBAL },
+ { &key_COND_flush_thread_cache, "COND_flush_thread_cache",
+ PSI_FLAG_GLOBAL }
+ };
+ PSI_mutex_info mutexes[]=
+ {
+ { &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL }
+ };
+ mysql_mutex_register("sql", mutexes, array_elements(mutexes));
+ mysql_cond_register("sql", conds, array_elements(conds));
+#endif
+ mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache,
+ MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, 0);
+ mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, 0);
+ list.empty();
+ kill_cached_threads= 0;
+ cached_thread_count= 0;
+ }
+
+
+ void destroy()
+ {
+ DBUG_ASSERT(cached_thread_count == 0);
+ DBUG_ASSERT(list.is_empty());
+ mysql_cond_destroy(&COND_flush_thread_cache);
+ mysql_cond_destroy(&COND_thread_cache);
+ mysql_mutex_destroy(&LOCK_thread_cache);
+ }
+
+
+ /**
+ Flushes thread cache.
+
+ Awakes parked threads and requests them to shutdown.
+ Waits until last parked thread leaves the cache.
+ */
+ void flush()
+ {
+ 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_cache);
+ }
+ kill_cached_threads--;
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ }
+
+
+ /**
+ Flushes thread cache and forbids threads parking in the cache.
+
+ This is a pre-shutdown hook.
+ */
+ void final_flush()
+ {
+ kill_cached_threads++;
+ flush();
+ }
+
+
+ /**
+ Requests parked thread to serve new connection.
+
+ @return
+ @retval true connection is enqueued and parked thread is about to serve it
+ @retval false thread cache is empty
+ */
+ bool enqueue(CONNECT *connect)
+ {
+ mysql_mutex_lock(&LOCK_thread_cache);
+ if (cached_thread_count)
+ {
+ list.push_back(connect);
+ cached_thread_count--;
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ mysql_cond_signal(&COND_thread_cache);
+ return true;
+ }
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ return false;
+ }
+
+
+ /**
+ Parks thread in the cache.
+
+ Thread execution is suspended until either of the following occurs:
+ - thread is requested to serve new connection;
+ - thread cache is flushed;
+ - THREAD_CACHE_TIMEOUT elapsed.
+
+ @return
+ @retval pointer to CONNECT if requested to serve new connection
+ @retval 0 if thread cache is flushed or on timeout
+ */
+ CONNECT *park()
+ {
+ struct timespec abstime;
+ CONNECT *connect;
+ bool flushed= false;
+ DBUG_ENTER("Thread_cache::park");
+ set_timespec(abstime, THREAD_CACHE_TIMEOUT);
+
+ /*
+ Delete the instrumentation for the job that just completed,
+ before parking this pthread in the cache (blocked on COND_thread_cache).
+ */
+ PSI_CALL_delete_current_thread();
+
+#ifndef DBUG_OFF
+ while (_db_is_pushed_())
+ _db_pop_();
+#endif
+
+ mysql_mutex_lock(&LOCK_thread_cache);
+ if ((connect= list.get()))
+ cached_thread_count++;
+ else if (cached_thread_count < thread_cache_size && !kill_cached_threads)
+ {
+ /* Don't kill the thread, just put it in cache for reuse */
+ DBUG_PRINT("info", ("Adding thread to cache"));
+ cached_thread_count++;
+ for (;;)
+ {
+ int error= mysql_cond_timedwait(&COND_thread_cache, &LOCK_thread_cache,
+ &abstime);
+ flushed= kill_cached_threads;
+ if ((connect= list.get()))
+ break;
+ else if (flushed || error == ETIMEDOUT || error == ETIME)
+ {
+ /*
+ If timeout, end thread.
+ If a new thread is requested, we will handle the call, even if we
+ got a timeout (as we are already awake and free)
+ */
+ cached_thread_count--;
+ break;
+ }
+ }
+ }
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ if (flushed)
+ mysql_cond_signal(&COND_flush_thread_cache);
+ DBUG_RETURN(connect);
+ }
+
+
+ /** Returns the number of parked threads. */
+ ulong size() const
+ {
+ mysql_mutex_lock(&LOCK_thread_cache);
+ ulong r= cached_thread_count;
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ return r;
+ }
+};
+
+extern Thread_cache thread_cache;
diff --git a/sql/thread_pool_info.cc b/sql/thread_pool_info.cc
new file mode 100644
index 00000000000..cf9ffe94f94
--- /dev/null
+++ b/sql/thread_pool_info.cc
@@ -0,0 +1,360 @@
+/* Copyright(C) 2019 MariaDB
+
+This program is free software; you can redistribute itand /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 02111 - 1301 USA*/
+
+#include <mysql_version.h>
+#include <mysql/plugin.h>
+
+#include <my_global.h>
+#include <sql_class.h>
+#include <sql_i_s.h>
+#include <mysql/plugin.h>
+#include <sql_show.h>
+#include <threadpool_generic.h>
+
+namespace Show {
+
+static ST_FIELD_INFO groups_fields_info[] =
+{
+ Column("GROUP_ID", SLong(6), NOT_NULL),
+ Column("CONNECTIONS", SLong(6), NOT_NULL),
+ Column("THREADS", SLong(6), NOT_NULL),
+ Column("ACTIVE_THREADS", SLong(6), NOT_NULL),
+ Column("STANDBY_THREADS", SLong(6), NOT_NULL),
+ Column("QUEUE_LENGTH", SLong(6), NOT_NULL),
+ Column("HAS_LISTENER", STiny(1), NOT_NULL),
+ Column("IS_STALLED", STiny(1), NOT_NULL),
+ CEnd()
+};
+
+} // namespace Show
+
+
+static int groups_fill_table(THD* thd, TABLE_LIST* tables, COND*)
+{
+ if (!all_groups)
+ return 0;
+
+ TABLE* table = tables->table;
+ for (uint i = 0; i < threadpool_max_size && all_groups[i].pollfd != INVALID_HANDLE_VALUE; i++)
+ {
+ thread_group_t* group = &all_groups[i];
+ /* ID */
+ table->field[0]->store(i, true);
+ /* CONNECTION_COUNT */
+ table->field[1]->store(group->connection_count, true);
+ /* THREAD_COUNT */
+ table->field[2]->store(group->thread_count, true);
+ /* ACTIVE_THREAD_COUNT */
+ table->field[3]->store(group->active_thread_count, true);
+ /* STANDBY_THREAD_COUNT */
+ table->field[4]->store(group->waiting_threads.elements(), true);
+ /* QUEUE LENGTH */
+ uint queue_len = group->queues[TP_PRIORITY_LOW].elements()
+ + group->queues[TP_PRIORITY_HIGH].elements();
+ table->field[5]->store(queue_len, true);
+ /* HAS_LISTENER */
+ table->field[6]->store((longlong)(group->listener != 0), true);
+ /* IS_STALLED */
+ table->field[7]->store(group->stalled, true);
+
+ if (schema_table_store_record(thd, table))
+ return 1;
+ }
+ return 0;
+}
+
+
+static int groups_init(void* p)
+{
+ ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
+ schema->fields_info = Show::groups_fields_info;
+ schema->fill_table = groups_fill_table;
+ return 0;
+}
+
+
+namespace Show {
+
+static ST_FIELD_INFO queues_field_info[] =
+{
+ Column("GROUP_ID", SLong(6), NOT_NULL),
+ Column("POSITION", SLong(6), NOT_NULL),
+ Column("PRIORITY", SLong(1), NOT_NULL),
+ Column("CONNECTION_ID", ULonglong(19), NOT_NULL),
+ Column("QUEUEING_TIME_MICROSECONDS", SLonglong(19), NOT_NULL),
+ CEnd()
+};
+
+} // namespace Show
+
+typedef connection_queue_t::Iterator connection_queue_iterator;
+
+static int queues_fill_table(THD* thd, TABLE_LIST* tables, COND*)
+{
+ if (!all_groups)
+ return 0;
+
+ TABLE* table = tables->table;
+ for (uint group_id = 0;
+ group_id < threadpool_max_size && all_groups[group_id].pollfd != INVALID_HANDLE_VALUE;
+ group_id++)
+ {
+ thread_group_t* group = &all_groups[group_id];
+
+ mysql_mutex_lock(&group->mutex);
+ bool err = false;
+ int pos = 0;
+ ulonglong now = microsecond_interval_timer();
+ for (uint prio = 0; prio < NQUEUES && !err; prio++)
+ {
+ connection_queue_iterator it(group->queues[prio]);
+ TP_connection_generic* c;
+ while ((c = it++) != 0)
+ {
+ /* GROUP_ID */
+ table->field[0]->store(group_id, true);
+ /* POSITION */
+ table->field[1]->store(pos++, true);
+ /* PRIORITY */
+ table->field[2]->store(prio, true);
+ /* CONNECTION_ID */
+ table->field[3]->store(c->thd->thread_id, true);
+ /* QUEUEING_TIME */
+ table->field[4]->store(now - c->enqueue_time, true);
+
+ err = schema_table_store_record(thd, table);
+ if (err)
+ break;
+ }
+ }
+ mysql_mutex_unlock(&group->mutex);
+ if (err)
+ return 1;
+ }
+ return 0;
+}
+
+static int queues_init(void* p)
+{
+ ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
+ schema->fields_info = Show::queues_field_info;
+ schema->fill_table = queues_fill_table;
+ return 0;
+}
+
+namespace Show {
+
+static ST_FIELD_INFO stats_fields_info[] =
+{
+ Column("GROUP_ID", SLong(6), NOT_NULL),
+ Column("THREAD_CREATIONS", SLonglong(19), NOT_NULL),
+ Column("THREAD_CREATIONS_DUE_TO_STALL", SLonglong(19), NOT_NULL),
+ Column("WAKES", SLonglong(19), NOT_NULL),
+ Column("WAKES_DUE_TO_STALL", SLonglong(19), NOT_NULL),
+ Column("THROTTLES", SLonglong(19), NOT_NULL),
+ Column("STALLS", SLonglong(19), NOT_NULL),
+ Column("POLLS_BY_LISTENER", SLonglong(19), NOT_NULL),
+ Column("POLLS_BY_WORKER", SLonglong(19), NOT_NULL),
+ Column("DEQUEUES_BY_LISTENER", SLonglong(19), NOT_NULL),
+ Column("DEQUEUES_BY_WORKER", SLonglong(19), NOT_NULL),
+ CEnd()
+};
+
+} // namespace Show
+
+
+static int stats_fill_table(THD* thd, TABLE_LIST* tables, COND*)
+{
+ if (!all_groups)
+ return 0;
+
+ TABLE* table = tables->table;
+ for (uint i = 0; i < threadpool_max_size && all_groups[i].pollfd != INVALID_HANDLE_VALUE; i++)
+ {
+ table->field[0]->store(i, true);
+ thread_group_t* group = &all_groups[i];
+
+ mysql_mutex_lock(&group->mutex);
+ thread_group_counters_t* counters = &group->counters;
+ table->field[1]->store(counters->thread_creations, true);
+ table->field[2]->store(counters->thread_creations_due_to_stall, true);
+ table->field[3]->store(counters->wakes, true);
+ table->field[4]->store(counters->wakes_due_to_stall, true);
+ table->field[5]->store(counters->throttles, true);
+ table->field[6]->store(counters->stalls, true);
+ table->field[7]->store(counters->polls[(int)operation_origin::LISTENER], true);
+ table->field[8]->store(counters->polls[(int)operation_origin::WORKER], true);
+ table->field[9]->store(counters->dequeues[(int)operation_origin::LISTENER], true);
+ table->field[10]->store(counters->dequeues[(int)operation_origin::WORKER], true);
+ mysql_mutex_unlock(&group->mutex);
+ if (schema_table_store_record(thd, table))
+ return 1;
+ }
+ return 0;
+}
+
+static int stats_reset_table()
+{
+ if (!all_groups)
+ return 0;
+
+ for (uint i = 0; i < threadpool_max_size && all_groups[i].pollfd != INVALID_HANDLE_VALUE; i++)
+ {
+ thread_group_t* group = &all_groups[i];
+ mysql_mutex_lock(&group->mutex);
+ memset(&group->counters, 0, sizeof(group->counters));
+ mysql_mutex_unlock(&group->mutex);
+ }
+ return 0;
+}
+
+static int stats_init(void* p)
+{
+ ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
+ schema->fields_info = Show::stats_fields_info;
+ schema->fill_table = stats_fill_table;
+ schema->reset_table = stats_reset_table;
+ return 0;
+}
+
+
+namespace Show {
+
+static ST_FIELD_INFO waits_fields_info[] =
+{
+ Column("REASON", Varchar(16), NOT_NULL),
+ Column("COUNT", SLonglong(19), NOT_NULL),
+ CEnd()
+};
+
+} // namespace Show
+
+/* See thd_wait_type enum for explanation*/
+static const LEX_CSTRING wait_reasons[THD_WAIT_LAST] =
+{
+ {STRING_WITH_LEN("UNKNOWN")},
+ {STRING_WITH_LEN("SLEEP")},
+ {STRING_WITH_LEN("DISKIO")},
+ {STRING_WITH_LEN("ROW_LOCK")},
+ {STRING_WITH_LEN("GLOBAL_LOCK")},
+ {STRING_WITH_LEN("META_DATA_LOCK")},
+ {STRING_WITH_LEN("TABLE_LOCK")},
+ {STRING_WITH_LEN("USER_LOCK")},
+ {STRING_WITH_LEN("BINLOG")},
+ {STRING_WITH_LEN("GROUP_COMMIT")},
+ {STRING_WITH_LEN("SYNC")},
+ {STRING_WITH_LEN("NET")}
+};
+
+extern Atomic_counter<unsigned long long> tp_waits[THD_WAIT_LAST];
+
+static int waits_fill_table(THD* thd, TABLE_LIST* tables, COND*)
+{
+ if (!all_groups)
+ return 0;
+
+ TABLE* table = tables->table;
+ for (auto i = 0; i < THD_WAIT_LAST; i++)
+ {
+ table->field[0]->store(wait_reasons[i].str, wait_reasons[i].length, system_charset_info);
+ table->field[1]->store(tp_waits[i], true);
+ if (schema_table_store_record(thd, table))
+ return 1;
+ }
+ return 0;
+}
+
+static int waits_reset_table()
+{
+ for (auto i = 0; i < THD_WAIT_LAST; i++)
+ tp_waits[i] = 0;
+
+ return 0;
+}
+
+static int waits_init(void* p)
+{
+ ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*)p;
+ schema->fields_info = Show::waits_fields_info;
+ schema->fill_table = waits_fill_table;
+ schema->reset_table = waits_reset_table;
+ return 0;
+}
+
+static struct st_mysql_information_schema plugin_descriptor =
+{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };
+
+maria_declare_plugin(thread_pool_info)
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &plugin_descriptor,
+ "THREAD_POOL_GROUPS",
+ "Vladislav Vaintroub",
+ "Provides information about threadpool groups.",
+ PLUGIN_LICENSE_GPL,
+ groups_init,
+ 0,
+ 0x0100,
+ NULL,
+ NULL,
+ "1.0",
+ MariaDB_PLUGIN_MATURITY_STABLE
+},
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &plugin_descriptor,
+ "THREAD_POOL_QUEUES",
+ "Vladislav Vaintroub",
+ "Provides information about threadpool queues.",
+ PLUGIN_LICENSE_GPL,
+ queues_init,
+ 0,
+ 0x0100,
+ NULL,
+ NULL,
+ "1.0",
+ MariaDB_PLUGIN_MATURITY_STABLE
+},
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &plugin_descriptor,
+ "THREAD_POOL_STATS",
+ "Vladislav Vaintroub",
+ "Provides performance counter information for threadpool.",
+ PLUGIN_LICENSE_GPL,
+ stats_init,
+ 0,
+ 0x0100,
+ NULL,
+ NULL,
+ "1.0",
+ MariaDB_PLUGIN_MATURITY_STABLE
+},
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &plugin_descriptor,
+ "THREAD_POOL_WAITS",
+ "Vladislav Vaintroub",
+ "Provides wait counters for threadpool.",
+ PLUGIN_LICENSE_GPL,
+ waits_init,
+ 0,
+ 0x0100,
+ NULL,
+ NULL,
+ "1.0",
+ MariaDB_PLUGIN_MATURITY_STABLE
+}
+maria_declare_plugin_end;
diff --git a/sql/threadpool.h b/sql/threadpool.h
index 817681e174f..27da872c5cc 100644
--- a/sql/threadpool.h
+++ b/sql/threadpool.h
@@ -1,8 +1,4 @@
-#ifndef THREADPOOL_H_INCLUDED
-#define THREADPOOL_H_INCLUDED
-
-#ifdef HAVE_POOL_OF_THREADS
-/* Copyright (C) 2012 Monty Program Ab
+/* Copyright (C) 2012, 2020, 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
@@ -17,6 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#pragma once
+#ifdef HAVE_POOL_OF_THREADS
#define MAX_THREAD_GROUPS 100000
/* Threadpool parameters */
@@ -24,16 +22,19 @@ extern uint threadpool_min_threads; /* Minimum threads in pool */
extern uint threadpool_idle_timeout; /* Shutdown idle worker threads after this timeout */
extern uint threadpool_size; /* Number of parallel executing threads */
extern uint threadpool_max_size;
-extern uint threadpool_stall_limit; /* time interval in 10 ms units for stall checks*/
+extern uint threadpool_stall_limit; /* time interval in milliseconds for stall checks*/
extern uint threadpool_max_threads; /* Maximum threads in pool */
extern uint threadpool_oversubscribe; /* Maximum active threads in group */
extern uint threadpool_prio_kickup_timer; /* Time before low prio item gets prio boost */
+extern my_bool threadpool_exact_stats; /* Better queueing time stats for information_schema, at small performance cost */
+extern my_bool threadpool_dedicated_listener; /* Listener thread does not pick up work items. */
#ifdef _WIN32
extern uint threadpool_mode; /* Thread pool implementation , windows or generic */
#define TP_MODE_WINDOWS 0
#define TP_MODE_GENERIC 1
#endif
+#define DEFAULT_THREADPOOL_STALL_LIMIT 500U
struct TP_connection;
extern void tp_callback(TP_connection *c);
@@ -61,11 +62,6 @@ extern void tp_set_threadpool_stall_limit(uint val);
extern int tp_get_idle_thread_count();
extern int tp_get_thread_count();
-/* Activate threadpool scheduler */
-extern void tp_scheduler(void);
-
-extern int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff,
- enum enum_var_type scope);
enum TP_PRIORITY {
TP_PRIORITY_HIGH,
@@ -89,6 +85,8 @@ enum TP_STATE
inside threadpool_win.cc and threadpool_unix.cc
*/
+class CONNECT;
+
struct TP_connection
{
THD* thd;
@@ -138,7 +136,7 @@ struct TP_pool
#ifdef _WIN32
struct TP_pool_win:TP_pool
{
- TP_pool_win();
+ TP_pool_win();
virtual int init();
virtual ~TP_pool_win();
virtual TP_connection *new_connection(CONNECT *c);
@@ -161,4 +159,3 @@ struct TP_pool_generic :TP_pool
};
#endif /* HAVE_POOL_OF_THREADS */
-#endif /* THREADPOOL_H_INCLUDED */
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index f40a958e62a..e8eb0dcc29d 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -23,6 +23,7 @@
#include <sql_audit.h>
#include <debug_sync.h>
#include <threadpool.h>
+
#ifdef WITH_WSREP
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
@@ -38,6 +39,8 @@ uint threadpool_max_threads;
uint threadpool_oversubscribe;
uint threadpool_mode;
uint threadpool_prio_kickup_timer;
+my_bool threadpool_exact_stats;
+my_bool threadpool_dedicated_listener;
/* Stats */
TP_STATISTICS tp_stats;
@@ -57,24 +60,24 @@ static inline TP_connection *get_TP_connection(THD *thd)
/*
Worker threads contexts, and THD contexts.
=========================================
-
- Both worker threads and connections have their sets of thread local variables
- At the moment it is mysys_var (this has specific data for dbug, my_error and
+
+ Both worker threads and connections have their sets of thread local variables
+ At the moment it is mysys_var (this has specific data for dbug, my_error and
similar goodies), and PSI per-client structure.
Whenever query is executed following needs to be done:
1. Save worker thread context.
2. Change TLS variables to connection specific ones using thread_attach(THD*).
- This function does some additional work , e.g setting up
+ This function does some additional work , e.g setting up
thread_stack/thread_ends_here pointers.
3. Process query
4. Restore worker thread context.
- Connection login and termination follows similar schema w.r.t saving and
- restoring contexts.
+ Connection login and termination follows similar schema w.r.t saving and
+ restoring contexts.
- For both worker thread, and for the connection, mysys variables are created
+ For both worker thread, and for the connection, mysys variables are created
using my_thread_init() and freed with my_thread_end().
*/
@@ -93,7 +96,7 @@ struct Worker_thread_context
{
PSI_CALL_set_thread(psi_thread);
set_mysys_var(mysys_var);
- pthread_setspecific(THR_THD, 0);
+ set_current_thd(nullptr);
}
};
@@ -146,12 +149,12 @@ static void thread_attach(THD* thd)
set_mysys_var(thd->mysys_var);
thd->thread_stack=(char*)&thd;
thd->store_globals();
- PSI_CALL_set_thread(thd->event_scheduler.m_psi);
+ PSI_CALL_set_thread(thd->get_psi());
mysql_socket_set_thread_owner(thd->net.vio->mysql_socket);
}
/*
- Determine connection priority , using current
+ Determine connection priority , using current
transaction state and 'threadpool_priority' variable value.
*/
static TP_PRIORITY get_priority(TP_connection *c)
@@ -159,9 +162,8 @@ static TP_PRIORITY get_priority(TP_connection *c)
DBUG_ASSERT(c->thd == current_thd);
TP_PRIORITY prio= (TP_PRIORITY)c->thd->variables.threadpool_priority;
if (prio == TP_PRIORITY_AUTO)
- {
- return c->thd->transaction.is_active() ? TP_PRIORITY_HIGH : TP_PRIORITY_LOW;
- }
+ prio= c->thd->transaction->is_active() ? TP_PRIORITY_HIGH : TP_PRIORITY_LOW;
+
return prio;
}
@@ -209,12 +211,11 @@ void tp_callback(TP_connection *c)
error:
c->thd= 0;
- delete c;
-
if (thd)
{
threadpool_remove_connection(thd);
}
+ delete c;
worker_context.restore();
}
@@ -231,31 +232,20 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
set_mysys_var(NULL);
my_thread_init();
st_my_thread_var* mysys_var= my_thread_var;
+ PSI_CALL_set_thread(PSI_CALL_new_thread(key_thread_one_connection, connect, 0));
if (!mysys_var ||!(thd= connect->create_thd(NULL)))
{
/* Out of memory? */
connect->close_and_delete();
if (mysys_var)
- {
-#ifdef HAVE_PSI_INTERFACE
- /*
- current PSI is still from worker thread.
- Set to 0, to avoid premature cleanup by my_thread_end
- */
- if (PSI_server) PSI_server->set_thread(0);
-#endif
my_thread_end();
- }
return NULL;
}
delete connect;
+
+ thd->event_scheduler.data = scheduler_data;
server_threads.insert(thd);
thd->set_mysys_var(mysys_var);
- thd->event_scheduler.data= scheduler_data;
-
- /* Create new PSI thread for use with the THD. */
- thd->event_scheduler.m_psi=
- PSI_CALL_new_thread(key_thread_one_connection, thd, thd->thread_id);
/* Login. */
@@ -266,8 +256,7 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
thd->start_utime= now;
thd->thr_create_utime= now;
- if (setup_connection_thread_globals(thd))
- goto end;
+ setup_connection_thread_globals(thd);
if (thd_prepare_connection(thd))
goto end;
@@ -292,15 +281,15 @@ end:
static void threadpool_remove_connection(THD *thd)
{
thread_attach(thd);
- thd->event_scheduler.data= 0;
thd->net.reading_or_writing = 0;
end_connection(thd);
close_connection(thd, 0);
unlink_thd(thd);
+ PSI_CALL_delete_current_thread(); // before THD is destroyed
delete thd;
/*
- Free resources associated with this connection:
+ Free resources associated with this connection:
mysys thread_var and PSI thread.
*/
my_thread_end();
@@ -344,8 +333,8 @@ static int threadpool_process_request(THD *thd)
if (thd->killed >= KILL_CONNECTION)
{
- /*
- killed flag was set by timeout handler
+ /*
+ killed flag was set by timeout handler
or KILL command. Return error.
*/
retval= 1;
@@ -395,19 +384,6 @@ end:
}
-
-/* Dummy functions, do nothing */
-
-static bool tp_init_new_connection_thread()
-{
- return 0;
-}
-
-static bool tp_end_thread(THD *, bool)
-{
- return 0;
-}
-
static TP_pool *pool;
static bool tp_init()
@@ -505,12 +481,17 @@ void tp_timeout_handler(TP_connection *c)
mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
+MY_ALIGNED(CPU_LEVEL1_DCACHE_LINESIZE) Atomic_counter<unsigned long long> tp_waits[THD_WAIT_LAST];
static void tp_wait_begin(THD *thd, int type)
{
TP_connection *c = get_TP_connection(thd);
if (c)
+ {
+ DBUG_ASSERT(type > 0 && type < THD_WAIT_LAST);
+ tp_waits[type]++;
c->wait_begin(type);
+ }
}
@@ -541,18 +522,16 @@ static scheduler_functions tp_scheduler_functions=
NULL,
NULL,
tp_init, // init
- tp_init_new_connection_thread, // init_new_connection_thread
tp_add_connection, // add_connection
tp_wait_begin, // thd_wait_begin
tp_wait_end, // thd_wait_end
tp_post_kill_notification, // post kill notification
- tp_end_thread, // Dummy function
tp_end // end
};
void pool_of_threads_scheduler(struct scheduler_functions *func,
ulong *arg_max_connections,
- uint *arg_connection_count)
+ Atomic_counter<uint> *arg_connection_count)
{
*func = tp_scheduler_functions;
func->max_threads= threadpool_max_threads;
diff --git a/sql/threadpool_generic.cc b/sql/threadpool_generic.cc
index 3f4997b8238..b6bb47e8f29 100644
--- a/sql/threadpool_generic.cc
+++ b/sql/threadpool_generic.cc
@@ -13,56 +13,28 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+#if (defined HAVE_POOL_OF_THREADS) && !defined(EMBEDDED_LIBRARY)
+
+#include "threadpool_generic.h"
#include "mariadb.h"
#include <violite.h>
#include <sql_priv.h>
#include <sql_class.h>
#include <my_pthread.h>
#include <scheduler.h>
-
-#ifdef HAVE_POOL_OF_THREADS
-
-#ifdef _WIN32
-/* AIX may define this, too ?*/
-#define HAVE_IOCP
-#endif
-
-#ifdef HAVE_IOCP
-#define OPTIONAL_IO_POLL_READ_PARAM this
-#else
-#define OPTIONAL_IO_POLL_READ_PARAM 0
-#endif
-
-#ifdef _WIN32
-typedef HANDLE TP_file_handle;
-#else
-typedef int TP_file_handle;
-#define INVALID_HANDLE_VALUE -1
-#endif
-
-
#include <sql_connect.h>
#include <mysqld.h>
#include <debug_sync.h>
#include <time.h>
#include <sql_plist.h>
#include <threadpool.h>
-#include <time.h>
-#ifdef __linux__
-#include <sys/epoll.h>
-typedef struct epoll_event native_event;
-#elif defined(HAVE_KQUEUE)
-#include <sys/event.h>
-typedef struct kevent native_event;
-#elif defined (__sun)
-#include <port.h>
-typedef port_event_t native_event;
-#elif defined (HAVE_IOCP)
-typedef OVERLAPPED_ENTRY native_event;
-#else
-#error threadpool is not available on this platform
-#endif
+#include <algorithm>
+#ifdef HAVE_IOCP
+#define OPTIONAL_IO_POLL_READ_PARAM this
+#else
+#define OPTIONAL_IO_POLL_READ_PARAM 0
+#endif
static void io_poll_close(TP_file_handle fd)
{
@@ -73,7 +45,6 @@ static void io_poll_close(TP_file_handle fd)
#endif
}
-
/** Maximum number of native events a listener can read in one go */
#define MAX_EVENTS 1024
@@ -112,93 +83,14 @@ static PSI_thread_info thread_list[] =
{&key_timer_thread, "timer_thread", PSI_FLAG_GLOBAL}
};
-/* Macro to simplify performance schema registration */
+/* Macro to simplify performance schema registration */
#define PSI_register(X) \
if(PSI_server) PSI_server->register_ ## X("threadpool", X ## _list, array_elements(X ## _list))
#else
#define PSI_register(X) /* no-op */
#endif
-
-struct thread_group_t;
-
-/* Per-thread structure for workers */
-struct worker_thread_t
-{
- ulonglong event_count; /* number of request handled by this thread */
- thread_group_t* thread_group;
- worker_thread_t *next_in_list;
- worker_thread_t **prev_in_list;
-
- mysql_cond_t cond;
- bool woken;
-};
-
-typedef I_P_List<worker_thread_t, I_P_List_adapter<worker_thread_t,
- &worker_thread_t::next_in_list,
- &worker_thread_t::prev_in_list>
- >
-worker_list_t;
-
-struct TP_connection_generic:public TP_connection
-{
- TP_connection_generic(CONNECT *c);
- ~TP_connection_generic();
-
- virtual int init(){ return 0; };
- virtual void set_io_timeout(int sec);
- virtual int start_io();
- virtual void wait_begin(int type);
- virtual void wait_end();
-
- thread_group_t *thread_group;
- TP_connection_generic *next_in_queue;
- TP_connection_generic **prev_in_queue;
- ulonglong abs_wait_timeout;
- ulonglong dequeue_time;
- TP_file_handle fd;
- bool bound_to_poll_descriptor;
- int waiting;
-#ifdef HAVE_IOCP
- OVERLAPPED overlapped;
-#endif
-#ifdef _WIN32
- enum_vio_type vio_type;
-#endif
-};
-
-
-typedef I_P_List<TP_connection_generic,
- I_P_List_adapter<TP_connection_generic,
- &TP_connection_generic::next_in_queue,
- &TP_connection_generic::prev_in_queue>,
- I_P_List_null_counter,
- I_P_List_fast_push_back<TP_connection_generic> >
-connection_queue_t;
-
-const int NQUEUES=2; /* We have high and low priority queues*/
-
-struct MY_ALIGNED(CPU_LEVEL1_DCACHE_LINESIZE) thread_group_t
-{
- mysql_mutex_t mutex;
- connection_queue_t queues[NQUEUES];
- worker_list_t waiting_threads;
- worker_thread_t *listener;
- pthread_attr_t *pthread_attr;
- TP_file_handle pollfd;
- int thread_count;
- int active_thread_count;
- int connection_count;
- /* Stats for the deadlock detection timer routine.*/
- int io_event_count;
- int queue_event_count;
- ulonglong last_thread_creation_time;
- int shutdown_pipe[2];
- bool shutdown;
- bool stalled;
-};
-
-static thread_group_t *all_groups;
+thread_group_t *all_groups;
static uint group_count;
static Atomic_counter<uint32_t> shutdown_group_count;
@@ -224,9 +116,9 @@ static pool_timer_t pool_timer;
static void queue_put(thread_group_t *thread_group, TP_connection_generic *connection);
static void queue_put(thread_group_t *thread_group, native_event *ev, int cnt);
-static int wake_thread(thread_group_t *thread_group);
-static int wake_or_create_thread(thread_group_t *thread_group);
-static int create_worker(thread_group_t *thread_group);
+static int wake_thread(thread_group_t *thread_group,bool due_to_stall);
+static int wake_or_create_thread(thread_group_t *thread_group, bool due_to_stall=false);
+static int create_worker(thread_group_t *thread_group, bool due_to_stall);
static void *worker_main(void *param);
static void check_stall(thread_group_t *thread_group);
static void set_next_timeout_check(ulonglong abstime);
@@ -234,48 +126,47 @@ static void print_pool_blocked_message(bool);
/**
Asynchronous network IO.
-
- We use native edge-triggered network IO multiplexing facility.
+
+ We use native edge-triggered network IO multiplexing facility.
This maps to different APIs on different Unixes.
-
+
Supported are currently Linux with epoll, Solaris with event ports,
OSX and BSD with kevent, Windows with IOCP. All those API's are used with one-shot flags
- (the event is signalled once client has written something into the socket,
+ (the event is signalled once client has written something into the socket,
then socket is removed from the "poll-set" until the command is finished,
and we need to re-arm/re-register socket)
-
+
No implementation for poll/select is currently provided.
-
- The API closely resembles all of the above mentioned platform APIs
- and consists of following functions.
-
+
+ The API closely resembles all of the above mentioned platform APIs
+ and consists of following functions.
+
- io_poll_create()
- Creates an io_poll descriptor
+ Creates an io_poll descriptor
On Linux: epoll_create()
-
+
- io_poll_associate_fd(int poll_fd, TP_file_handle fd, void *data, void *opt)
- Associate file descriptor with io poll descriptor
+ Associate file descriptor with io poll descriptor
On Linux : epoll_ctl(..EPOLL_CTL_ADD))
-
+
- io_poll_disassociate_fd(TP_file_handle pollfd, TP_file_handle fd)
- Associate file descriptor with io poll descriptor
+ Associate file descriptor with io poll descriptor
On Linux: epoll_ctl(..EPOLL_CTL_DEL)
-
-
+
+
- io_poll_start_read(int poll_fd,int fd, void *data, void *opt)
- The same as io_poll_associate_fd(), but cannot be used before
+ The same as io_poll_associate_fd(), but cannot be used before
io_poll_associate_fd() was called.
On Linux : epoll_ctl(..EPOLL_CTL_MOD)
-
- - io_poll_wait (TP_file_handle pollfd, native_event *native_events, int maxevents,
+
+ - io_poll_wait (TP_file_handle pollfd, native_event *native_events, int maxevents,
int timeout_ms)
-
- wait until one or more descriptors added with io_poll_associate_fd()
- or io_poll_start_read() becomes readable. Data associated with
- descriptors can be retrieved from native_events array, using
+
+ wait until one or more descriptors added with io_poll_associate_fd()
+ or io_poll_start_read() becomes readable. Data associated with
+ descriptors can be retrieved from native_events array, using
native_event_get_userdata() function.
-
On Linux: epoll_wait()
*/
@@ -307,7 +198,7 @@ int io_poll_start_read(TP_file_handle pollfd, TP_file_handle fd, void *data, voi
ev.data.u64= 0; /* Keep valgrind happy */
ev.data.ptr= data;
ev.events= EPOLLIN|EPOLLET|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT;
- return epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev);
+ return epoll_ctl(pollfd, EPOLL_CTL_MOD, fd, &ev);
}
int io_poll_disassociate_fd(TP_file_handle pollfd, TP_file_handle fd)
@@ -322,11 +213,11 @@ int io_poll_disassociate_fd(TP_file_handle pollfd, TP_file_handle fd)
NOTE - in case of EINTR, it restarts with original timeout. Since we use
either infinite or 0 timeouts, this is not critical
*/
-int io_poll_wait(TP_file_handle pollfd, native_event *native_events, int maxevents,
+int io_poll_wait(TP_file_handle pollfd, native_event *native_events, int maxevents,
int timeout_ms)
{
int ret;
- do
+ do
{
ret = epoll_wait(pollfd, native_events, maxevents, timeout_ms);
}
@@ -342,9 +233,9 @@ static void *native_event_get_userdata(native_event *event)
#elif defined(HAVE_KQUEUE)
-/*
+/*
NetBSD is incompatible with other BSDs , last parameter in EV_SET macro
- (udata, user data) needs to be intptr_t, whereas it needs to be void*
+ (udata, user data) needs to be intptr_t, whereas it needs to be void*
everywhere else.
*/
@@ -363,18 +254,18 @@ TP_file_handle io_poll_create()
int io_poll_start_read(TP_file_handle pollfd, TP_file_handle fd, void *data,void *)
{
struct kevent ke;
- MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
+ MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
0, 0, data);
- return kevent(pollfd, &ke, 1, 0, 0, 0);
+ return kevent(pollfd, &ke, 1, 0, 0, 0);
}
int io_poll_associate_fd(TP_file_handle pollfd, TP_file_handle fd, void *data,void *)
{
struct kevent ke;
- MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
+ MY_EV_SET(&ke, fd, EVFILT_READ, EV_ADD|EV_ONESHOT,
0, 0, data);
- return io_poll_start_read(pollfd,fd, data, 0);
+ return io_poll_start_read(pollfd,fd, data, 0);
}
@@ -397,7 +288,7 @@ int io_poll_wait(TP_file_handle pollfd, struct kevent *events, int maxevents, in
}
do
{
- ret= kevent(pollfd, 0, 0, events, maxevents,
+ ret= kevent(pollfd, 0, 0, events, maxevents,
(timeout_ms >= 0)?&ts:NULL);
}
while (ret == -1 && errno == EINTR);
@@ -496,15 +387,44 @@ int io_poll_start_read(TP_file_handle pollfd, TP_file_handle fd, void *, void *o
static int io_poll_associate_fd(TP_file_handle pollfd, TP_file_handle fd, void *data, void *opt)
{
HANDLE h= CreateIoCompletionPort(fd, pollfd, (ULONG_PTR)data, 0);
- if (!h)
+ if (!h)
return -1;
- return io_poll_start_read(pollfd,fd, 0, opt);
+ return io_poll_start_read(pollfd,fd, 0, opt);
}
+typedef LONG NTSTATUS;
+
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;
+
+struct FILE_COMPLETION_INFORMATION {
+ HANDLE Port;
+ PVOID Key;
+};
+
+enum FILE_INFORMATION_CLASS {
+ FileReplaceCompletionInformation = 0x3D
+};
+
+
+typedef NTSTATUS(WINAPI* pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
+
int io_poll_disassociate_fd(TP_file_handle pollfd, TP_file_handle fd)
{
- /* Not possible to unbind/rebind file descriptor in IOCP. */
+ static pNtSetInformationFile my_NtSetInformationFile = (pNtSetInformationFile)
+ GetProcAddress(GetModuleHandle("ntdll"), "NtSetInformationFile");
+ if (!my_NtSetInformationFile)
+ return -1; /* unexpected, we only support Windows 8.1+*/
+ IO_STATUS_BLOCK iosb{};
+ FILE_COMPLETION_INFORMATION fci{};
+ if (my_NtSetInformationFile(fd,&iosb,&fci,sizeof(fci),FileReplaceCompletionInformation))
+ return -1;
return 0;
}
@@ -512,9 +432,9 @@ int io_poll_disassociate_fd(TP_file_handle pollfd, TP_file_handle fd)
int io_poll_wait(TP_file_handle pollfd, native_event *events, int maxevents, int timeout_ms)
{
ULONG n;
- BOOL ok = GetQueuedCompletionStatusEx(pollfd, events,
+ BOOL ok = GetQueuedCompletionStatusEx(pollfd, events,
maxevents, &n, timeout_ms, FALSE);
-
+
return ok ? (int)n : -1;
}
@@ -539,7 +459,17 @@ static TP_connection_generic *queue_get(thread_group_t *thread_group)
if (c)
DBUG_RETURN(c);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(0);
+}
+
+static TP_connection_generic* queue_get(thread_group_t* group, operation_origin origin)
+{
+ auto ret = queue_get(group);
+ if (ret)
+ {
+ TP_INCREMENT_GROUP_COUNTER(group, dequeues[(int)origin]);
+ }
+ return ret;
}
static bool is_queue_empty(thread_group_t *thread_group)
@@ -563,17 +493,17 @@ static void queue_init(thread_group_t *thread_group)
static void queue_put(thread_group_t *thread_group, native_event *ev, int cnt)
{
- ulonglong now= pool_timer.current_microtime;
+ ulonglong now= threadpool_exact_stats?microsecond_interval_timer():pool_timer.current_microtime;
for(int i=0; i < cnt; i++)
{
TP_connection_generic *c = (TP_connection_generic *)native_event_get_userdata(&ev[i]);
- c->dequeue_time= now;
+ c->enqueue_time= now;
thread_group->queues[c->priority].push_back(c);
}
}
-/*
- Handle wait timeout :
+/*
+ Handle wait timeout :
Find connections that have been idle for too long and kill them.
Also, recalculate time when next timeout check should run.
*/
@@ -609,16 +539,16 @@ static my_bool timeout_check(THD *thd, pool_timer_t *timer)
}
-/*
- Timer thread.
-
+/*
+ Timer thread.
+
Periodically, check if one of the thread groups is stalled. Stalls happen if
- events are not being dequeued from the queue, or from the network, Primary
- reason for stall can be a lengthy executing non-blocking request. It could
- also happen that thread is waiting but wait_begin/wait_end is forgotten by
- storage engine. Timer thread will create a new thread in group in case of
+ events are not being dequeued from the queue, or from the network, Primary
+ reason for stall can be a lengthy executing non-blocking request. It could
+ also happen that thread is waiting but wait_begin/wait_end is forgotten by
+ storage engine. Timer thread will create a new thread in group in case of
a stall.
-
+
Besides checking for stalls, timer thread is also responsible for terminating
clients that have been idle for longer than wait_timeout seconds.
@@ -653,14 +583,14 @@ static void* timer_thread(void *param)
if (err == ETIMEDOUT)
{
timer->current_microtime= microsecond_interval_timer();
-
+
/* Check stalls in thread groups */
for (i= 0; i < threadpool_max_size; i++)
{
if(all_groups[i].connection_count)
check_stall(&all_groups[i]);
}
-
+
/* Check if any client exceeded wait_timeout */
if (timer->next_timeout_check.load(std::memory_order_relaxed) <=
timer->current_microtime)
@@ -693,7 +623,7 @@ void check_stall(thread_group_t *thread_group)
for (;;)
{
c= thread_group->queues[TP_PRIORITY_LOW].front();
- if (c && pool_timer.current_microtime - c->dequeue_time > 1000ULL * threadpool_prio_kickup_timer)
+ if (c && pool_timer.current_microtime - c->enqueue_time > 1000ULL * threadpool_prio_kickup_timer)
{
thread_group->queues[TP_PRIORITY_LOW].remove(c);
thread_group->queues[TP_PRIORITY_HIGH].push_back(c);
@@ -703,27 +633,27 @@ void check_stall(thread_group_t *thread_group)
}
/*
- Check if listener is present. If not, check whether any IO
- events were dequeued since last time. If not, this means
- listener is either in tight loop or thd_wait_begin()
+ Check if listener is present. If not, check whether any IO
+ events were dequeued since last time. If not, this means
+ listener is either in tight loop or thd_wait_begin()
was forgotten. Create a new worker(it will make itself listener).
*/
if (!thread_group->listener && !thread_group->io_event_count)
{
- wake_or_create_thread(thread_group);
+ wake_or_create_thread(thread_group, true);
mysql_mutex_unlock(&thread_group->mutex);
return;
}
-
+
/* Reset io event count */
thread_group->io_event_count= 0;
- /*
+ /*
Check whether requests from the workqueue are being dequeued.
The stall detection and resolution works as follows:
- 1. There is a counter thread_group->queue_event_count for the number of
+ 1. There is a counter thread_group->queue_event_count for the number of
events removed from the queue. Timer resets the counter to 0 on each run.
2. Timer determines stall if this counter remains 0 since last check
and the queue is not empty.
@@ -734,25 +664,26 @@ void check_stall(thread_group_t *thread_group)
Q : Will this handling lead to an unbound growth of threads, if queue
stalls permanently?
A : No. If queue stalls permanently, it is an indication for many very long
- simultaneous queries. The maximum number of simultanoues queries is
+ simultaneous queries. The maximum number of simultanoues queries is
max_connections, further we have threadpool_max_threads limit, upon which no
- worker threads are created. So in case there is a flood of very long
+ worker threads are created. So in case there is a flood of very long
queries, threadpool would slowly approach thread-per-connection behavior.
NOTE:
If long queries never wait, creation of the new threads is done by timer,
- so it is slower than in real thread-per-connection. However if long queries
+ so it is slower than in real thread-per-connection. However if long queries
do wait and indicate that via thd_wait_begin/end callbacks, thread creation
will be faster.
*/
if (!is_queue_empty(thread_group) && !thread_group->queue_event_count)
{
thread_group->stalled= true;
- wake_or_create_thread(thread_group);
+ TP_INCREMENT_GROUP_COUNTER(thread_group,stalls);
+ wake_or_create_thread(thread_group,true);
}
-
+
/* Reset queue event count */
thread_group->queue_event_count= 0;
-
+
mysql_mutex_unlock(&thread_group->mutex);
}
@@ -784,10 +715,10 @@ static void stop_timer(pool_timer_t *timer)
/**
Poll for socket events and distribute them to worker threads
In many case current thread will handle single event itself.
-
+
@return a ready connection, or NULL on shutdown
*/
-static TP_connection_generic * listener(worker_thread_t *current_thread,
+static TP_connection_generic * listener(worker_thread_t *current_thread,
thread_group_t *thread_group)
{
DBUG_ENTER("listener");
@@ -797,12 +728,12 @@ static TP_connection_generic * listener(worker_thread_t *current_thread,
{
native_event ev[MAX_EVENTS];
int cnt;
-
+
if (thread_group->shutdown)
break;
-
+
cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, -1);
-
+ TP_INCREMENT_GROUP_COUNTER(thread_group, polls[(int)operation_origin::LISTENER]);
if (cnt <=0)
{
DBUG_ASSERT(thread_group->shutdown);
@@ -816,59 +747,58 @@ static TP_connection_generic * listener(worker_thread_t *current_thread,
mysql_mutex_unlock(&thread_group->mutex);
break;
}
-
- thread_group->io_event_count += cnt;
-
- /*
+
+ thread_group->io_event_count += cnt;
+
+ /*
We got some network events and need to make decisions : whether
listener hould handle events and whether or not any wake worker
threads so they can handle events.
-
- Q1 : Should listener handle an event itself, or put all events into
+
+ Q1 : Should listener handle an event itself, or put all events into
queue and let workers handle the events?
-
+
Solution :
- Generally, listener that handles events itself is preferable. We do not
- want listener thread to change its state from waiting to running too
+ Generally, listener that handles events itself is preferable. We do not
+ want listener thread to change its state from waiting to running too
often, Since listener has just woken from poll, it better uses its time
slice and does some work. Besides, not handling events means they go to
the queue, and often to wake another worker must wake up to handle the
event. This is not good, as we want to avoid wakeups.
-
+
The downside of listener that also handles queries is that we can
- potentially leave thread group for long time not picking the new
+ potentially leave thread group for long time not picking the new
network events. It is not a major problem, because this stall will be
detected sooner or later by the timer thread. Still, relying on timer
is not always good, because it may "tick" too slow (large timer_interval)
-
+
We use following strategy to solve this problem - if queue was not empty
- we suspect flood of network events and listener stays, Otherwise, it
+ we suspect flood of network events and listener stays, Otherwise, it
handles a query.
-
-
+
Q2: If queue is not empty, how many workers to wake?
-
+
Solution:
- We generally try to keep one thread per group active (threads handling
- queries are considered active, unless they stuck in inside some "wait")
- Thus, we will wake only one worker, and only if there is not active
- threads currently,and listener is not going to handle a query. When we
- don't wake, we hope that currently active threads will finish fast and
+ We generally try to keep one thread per group active (threads handling
+ queries are considered active, unless they stuck in inside some "wait")
+ Thus, we will wake only one worker, and only if there is not active
+ threads currently,and listener is not going to handle a query. When we
+ don't wake, we hope that currently active threads will finish fast and
handle the queue. If this does not happen, timer thread will detect stall
and wake a worker.
-
- NOTE: Currently nothing is done to detect or prevent long queuing times.
- A solution for the future would be to give up "one active thread per
- group" principle, if events stay in the queue for too long, and just wake
+
+ NOTE: Currently nothing is done to detect or prevent long queuing times.
+ A solution for the future would be to give up "one active thread per
+ group" principle, if events stay in the queue for too long, and just wake
more workers.
*/
-
- bool listener_picks_event=is_queue_empty(thread_group);
+
+ bool listener_picks_event=is_queue_empty(thread_group) && !threadpool_dedicated_listener;
queue_put(thread_group, ev, cnt);
if (listener_picks_event)
{
/* Handle the first event. */
- retval= queue_get(thread_group);
+ retval= queue_get(thread_group, operation_origin::LISTENER);
mysql_mutex_unlock(&thread_group->mutex);
break;
}
@@ -876,25 +806,25 @@ static TP_connection_generic * listener(worker_thread_t *current_thread,
if(thread_group->active_thread_count==0)
{
/* We added some work items to queue, now wake a worker. */
- if(wake_thread(thread_group))
+ if(wake_thread(thread_group, false))
{
- /*
+ /*
Wake failed, hence groups has no idle threads. Now check if there are
any threads in the group except listener.
- */
+ */
if(thread_group->thread_count == 1)
{
/*
Currently there is no worker thread in the group, as indicated by
- thread_count == 1 (this means listener is the only one thread in
+ thread_count == 1 (this means listener is the only one thread in
the group).
The queue is not empty, and listener is not going to handle
events. In order to drain the queue, we create a worker here.
- Alternatively, we could just rely on timer to detect stall, and
+ Alternatively, we could just rely on timer to detect stall, and
create thread, but waiting for timer would be an inefficient and
pointless delay.
*/
- create_worker(thread_group);
+ create_worker(thread_group, false);
}
}
}
@@ -905,7 +835,7 @@ static TP_connection_generic * listener(worker_thread_t *current_thread,
}
/**
- Adjust thread counters in group or global
+ Adjust thread counters in group or global
whenever thread is created or is about to exit
@param thread_group
@@ -923,20 +853,20 @@ static void add_thread_count(thread_group_t *thread_group, int32 count)
/**
- Creates a new worker thread.
- thread_mutex must be held when calling this function
+ Creates a new worker thread.
+ thread_mutex must be held when calling this function
NOTE: in rare cases, the number of threads can exceed
threadpool_max_threads, because we need at least 2 threads
per group to prevent deadlocks (one listener + one worker)
*/
-static int create_worker(thread_group_t *thread_group)
+static int create_worker(thread_group_t *thread_group, bool due_to_stall)
{
pthread_t thread_id;
bool max_threads_reached= false;
int err;
-
+
DBUG_ENTER("create_worker");
if (tp_stats.num_worker_threads >= threadpool_max_threads
&& thread_group->thread_count >= 2)
@@ -946,14 +876,18 @@ static int create_worker(thread_group_t *thread_group)
goto end;
}
-
- err= mysql_thread_create(key_worker_thread, &thread_id,
+ err= mysql_thread_create(key_worker_thread, &thread_id,
thread_group->pthread_attr, worker_main, thread_group);
if (!err)
{
thread_group->last_thread_creation_time=microsecond_interval_timer();
statistic_increment(thread_created,&LOCK_status);
add_thread_count(thread_group, 1);
+ TP_INCREMENT_GROUP_COUNTER(thread_group,thread_creations);
+ if(due_to_stall)
+ {
+ TP_INCREMENT_GROUP_COUNTER(thread_group, thread_creations_due_to_stall);
+ }
}
else
{
@@ -965,82 +899,86 @@ end:
print_pool_blocked_message(max_threads_reached);
else
pool_block_start= 0; /* Reset pool blocked timer, if it was set */
-
+
DBUG_RETURN(err);
}
/**
Calculate microseconds throttling delay for thread creation.
-
+
The value depends on how many threads are already in the group:
small number of threads means no delay, the more threads the larger
the delay.
-
+
The actual values were not calculated using any scientific methods.
They just look right, and behave well in practice.
-
- TODO: Should throttling depend on thread_pool_stall_limit?
*/
+
+#define THROTTLING_FACTOR (threadpool_stall_limit/std::max(DEFAULT_THREADPOOL_STALL_LIMIT,threadpool_stall_limit))
+
static ulonglong microsecond_throttling_interval(thread_group_t *thread_group)
{
int count= thread_group->thread_count;
-
- if (count < 4)
+
+ if (count < 1+ (int)threadpool_oversubscribe)
return 0;
-
+
if (count < 8)
- return 50*1000;
-
+ return 50*1000*THROTTLING_FACTOR;
+
if(count < 16)
- return 100*1000;
-
- return 200*1000;
+ return 100*1000*THROTTLING_FACTOR;
+
+ return 200*100*THROTTLING_FACTOR;
}
/**
- Wakes a worker thread, or creates a new one.
-
+ Wakes a worker thread, or creates a new one.
+
Worker creation is throttled, so we avoid too many threads
to be created during the short time.
*/
-static int wake_or_create_thread(thread_group_t *thread_group)
+static int wake_or_create_thread(thread_group_t *thread_group, bool due_to_stall)
{
DBUG_ENTER("wake_or_create_thread");
-
+
if (thread_group->shutdown)
DBUG_RETURN(0);
- if (wake_thread(thread_group) == 0)
+ if (wake_thread(thread_group, due_to_stall) == 0)
+ {
DBUG_RETURN(0);
+ }
if (thread_group->thread_count > thread_group->connection_count)
DBUG_RETURN(-1);
-
+
if (thread_group->active_thread_count == 0)
{
/*
- We're better off creating a new thread here with no delay, either there
- are no workers at all, or they all are all blocking and there was no
- idle thread to wakeup. Smells like a potential deadlock or very slowly
+ We're better off creating a new thread here with no delay, either there
+ are no workers at all, or they all are all blocking and there was no
+ idle thread to wakeup. Smells like a potential deadlock or very slowly
executing requests, e.g sleeps or user locks.
*/
- DBUG_RETURN(create_worker(thread_group));
+ DBUG_RETURN(create_worker(thread_group, due_to_stall));
}
ulonglong now = microsecond_interval_timer();
ulonglong time_since_last_thread_created =
(now - thread_group->last_thread_creation_time);
-
- /* Throttle thread creation. */
+
+ /* Throttle thread creation. */
if (time_since_last_thread_created >
microsecond_throttling_interval(thread_group))
{
- DBUG_RETURN(create_worker(thread_group));
+ DBUG_RETURN(create_worker(thread_group, due_to_stall));
}
-
+
+ TP_INCREMENT_GROUP_COUNTER(thread_group,throttles);
DBUG_RETURN(-1);
}
@@ -1089,7 +1027,7 @@ void thread_group_destroy(thread_group_t *thread_group)
Wake sleeping thread from waiting list
*/
-static int wake_thread(thread_group_t *thread_group)
+static int wake_thread(thread_group_t *thread_group,bool due_to_stall)
{
DBUG_ENTER("wake_thread");
worker_thread_t *thread = thread_group->waiting_threads.front();
@@ -1098,12 +1036,17 @@ static int wake_thread(thread_group_t *thread_group)
thread->woken= true;
thread_group->waiting_threads.remove(thread);
mysql_cond_signal(&thread->cond);
+ TP_INCREMENT_GROUP_COUNTER(thread_group, wakes);
+ if (due_to_stall)
+ {
+ TP_INCREMENT_GROUP_COUNTER(thread_group, wakes_due_to_stall);
+ }
DBUG_RETURN(0);
}
DBUG_RETURN(1); /* no thread in waiter list => missed wakeup */
}
-/*
+/*
Wake listener thread (during shutdown)
Self-pipe trick is used in most cases,except IOCP.
*/
@@ -1132,8 +1075,8 @@ static int wake_listener(thread_group_t *thread_group)
/**
Initiate shutdown for thread group.
- The shutdown is asynchronous, we only care to wake all threads in here, so
- they can finish. We do not wait here until threads terminate. Final cleanup
+ The shutdown is asynchronous, we only care to wake all threads in here, so
+ they can finish. We do not wait here until threads terminate. Final cleanup
of the group (thread_group_destroy) will be done by the last exiting threads.
*/
@@ -1142,32 +1085,32 @@ static void thread_group_close(thread_group_t *thread_group)
DBUG_ENTER("thread_group_close");
mysql_mutex_lock(&thread_group->mutex);
- if (thread_group->thread_count == 0)
+ if (thread_group->thread_count == 0)
{
mysql_mutex_unlock(&thread_group->mutex);
thread_group_destroy(thread_group);
DBUG_VOID_RETURN;
}
- thread_group->shutdown= true;
+ thread_group->shutdown= true;
thread_group->listener= NULL;
wake_listener(thread_group);
/* Wake all workers. */
- while(wake_thread(thread_group) == 0)
- {
+ while(wake_thread(thread_group, false) == 0)
+ {
}
-
+
mysql_mutex_unlock(&thread_group->mutex);
DBUG_VOID_RETURN;
}
-/*
+/*
Add work to the queue. Maybe wake a worker if they all sleep.
-
+
Currently, this function is only used when new connections need to
perform login (this is done in worker threads).
@@ -1177,7 +1120,7 @@ static void queue_put(thread_group_t *thread_group, TP_connection_generic *conne
{
DBUG_ENTER("queue_put");
- connection->dequeue_time= pool_timer.current_microtime;
+ connection->enqueue_time= threadpool_exact_stats?microsecond_interval_timer():pool_timer.current_microtime;
thread_group->queues[connection->priority].push_back(connection);
if (thread_group->active_thread_count == 0)
@@ -1187,39 +1130,39 @@ static void queue_put(thread_group_t *thread_group, TP_connection_generic *conne
}
-/*
- Prevent too many threads executing at the same time,if the workload is
+/*
+ Prevent too many threads executing at the same time,if the workload is
not CPU bound.
*/
static bool too_many_threads(thread_group_t *thread_group)
{
- return (thread_group->active_thread_count >= 1+(int)threadpool_oversubscribe
+ return (thread_group->active_thread_count >= 1+(int)threadpool_oversubscribe
&& !thread_group->stalled);
}
/**
Retrieve a connection with pending event.
-
- Pending event in our case means that there is either a pending login request
+
+ Pending event in our case means that there is either a pending login request
(if connection is not yet logged in), or there are unread bytes on the socket.
- If there are no pending events currently, thread will wait.
+ If there are no pending events currently, thread will wait.
If timeout specified in abstime parameter passes, the function returns NULL.
-
+
@param current_thread - current worker thread
@param thread_group - current thread group
@param abstime - absolute wait timeout
-
+
@return
- connection with pending event.
+ connection with pending event.
NULL is returned if timeout has expired,or on shutdown.
*/
-TP_connection_generic *get_event(worker_thread_t *current_thread,
+TP_connection_generic *get_event(worker_thread_t *current_thread,
thread_group_t *thread_group, struct timespec *abstime)
-{
+{
DBUG_ENTER("get_event");
TP_connection_generic *connection = NULL;
@@ -1227,19 +1170,21 @@ TP_connection_generic *get_event(worker_thread_t *current_thread,
mysql_mutex_lock(&thread_group->mutex);
DBUG_ASSERT(thread_group->active_thread_count >= 0);
- for(;;)
+ for(;;)
{
int err=0;
- bool oversubscribed = too_many_threads(thread_group);
+ bool oversubscribed = too_many_threads(thread_group);
if (thread_group->shutdown)
break;
/* Check if queue is not empty */
if (!oversubscribed)
{
- connection = queue_get(thread_group);
+ connection = queue_get(thread_group, operation_origin::WORKER);
if(connection)
+ {
break;
+ }
}
/* If there is currently no listener in the group, become one. */
@@ -1257,41 +1202,41 @@ TP_connection_generic *get_event(worker_thread_t *current_thread,
thread_group->listener= NULL;
break;
}
-
- /*
- Last thing we try before going to sleep is to
+
+ /*
+ Last thing we try before going to sleep is to
non-blocking event poll, i.e with timeout = 0.
If this returns events, pick one
*/
if (!oversubscribed)
{
-
native_event ev[MAX_EVENTS];
int cnt = io_poll_wait(thread_group->pollfd, ev, MAX_EVENTS, 0);
+ TP_INCREMENT_GROUP_COUNTER(thread_group, polls[(int)operation_origin::WORKER]);
if (cnt > 0)
{
queue_put(thread_group, ev, cnt);
- connection= queue_get(thread_group);
+ connection= queue_get(thread_group,operation_origin::WORKER);
break;
}
}
- /* And now, finally sleep */
+ /* And now, finally sleep */
current_thread->woken = false; /* wake() sets this to true */
- /*
+ /*
Add current thread to the head of the waiting list and wait.
It is important to add thread to the head rather than tail
as it ensures LIFO wakeup order (hot caches, working inactivity timeout)
*/
thread_group->waiting_threads.push_front(current_thread);
-
+
thread_group->active_thread_count--;
if (abstime)
{
- err = mysql_cond_timedwait(&current_thread->cond, &thread_group->mutex,
+ err = mysql_cond_timedwait(&current_thread->cond, &thread_group->mutex,
abstime);
}
else
@@ -1299,7 +1244,7 @@ TP_connection_generic *get_event(worker_thread_t *current_thread,
err = mysql_cond_wait(&current_thread->cond, &thread_group->mutex);
}
thread_group->active_thread_count++;
-
+
if (!current_thread->woken)
{
/*
@@ -1315,15 +1260,16 @@ TP_connection_generic *get_event(worker_thread_t *current_thread,
}
thread_group->stalled= false;
+
mysql_mutex_unlock(&thread_group->mutex);
-
+
DBUG_RETURN(connection);
}
/**
- Tells the pool that worker starts waiting on IO, lock, condition,
+ Tells the pool that worker starts waiting on IO, lock, condition,
sleep() or similar.
*/
@@ -1332,20 +1278,20 @@ void wait_begin(thread_group_t *thread_group)
DBUG_ENTER("wait_begin");
mysql_mutex_lock(&thread_group->mutex);
thread_group->active_thread_count--;
-
+
DBUG_ASSERT(thread_group->active_thread_count >=0);
DBUG_ASSERT(thread_group->connection_count > 0);
- if ((thread_group->active_thread_count == 0) &&
+ if ((thread_group->active_thread_count == 0) &&
(!is_queue_empty(thread_group) || !thread_group->listener))
{
- /*
- Group might stall while this thread waits, thus wake
+ /*
+ Group might stall while this thread waits, thus wake
or create a worker to prevent stall.
*/
wake_or_create_thread(thread_group);
}
-
+
mysql_mutex_unlock(&thread_group->mutex);
DBUG_VOID_RETURN;
}
@@ -1364,15 +1310,13 @@ void wait_end(thread_group_t *thread_group)
}
-
-
TP_connection * TP_pool_generic::new_connection(CONNECT *c)
{
return new (std::nothrow) TP_connection_generic(c);
}
/**
- Add a new connection to thread pool..
+ Add a new connection to thread pool
*/
void TP_pool_generic::add(TP_connection *c)
@@ -1382,7 +1326,7 @@ void TP_pool_generic::add(TP_connection *c)
TP_connection_generic *connection=(TP_connection_generic *)c;
thread_group_t *thread_group= connection->thread_group;
/*
- Add connection to the work queue.Actual logon
+ Add connection to the work queue.Actual logon
will be done by a worker thread.
*/
mysql_mutex_lock(&thread_group->mutex);
@@ -1413,8 +1357,8 @@ void TP_connection_generic::wait_begin(int type)
MySQL scheduler callback: wait end
*/
-void TP_connection_generic::wait_end()
-{
+void TP_connection_generic::wait_end()
+{
DBUG_ENTER("wait_end");
DBUG_ASSERT(waiting);
waiting--;
@@ -1439,6 +1383,12 @@ static void set_next_timeout_check(ulonglong abstime)
DBUG_VOID_RETURN;
}
+static size_t get_group_id(my_thread_id tid)
+{
+ return size_t(tid % group_count);
+}
+
+
TP_connection_generic::TP_connection_generic(CONNECT *c):
TP_connection(c),
thread_group(0),
@@ -1446,25 +1396,27 @@ TP_connection_generic::TP_connection_generic(CONNECT *c):
prev_in_queue(0),
abs_wait_timeout(ULONGLONG_MAX),
bound_to_poll_descriptor(false),
- waiting(false)
+ waiting(false),
+ fix_group(false)
#ifdef HAVE_IOCP
, overlapped()
#endif
+#ifdef _WIN32
+, vio_type(c->vio_type)
+#endif
{
- DBUG_ASSERT(c->vio);
+ DBUG_ASSERT(c->vio_type != VIO_CLOSED);
#ifdef _WIN32
- vio_type= c->vio->type;
- fd= (vio_type == VIO_TYPE_NAMEDPIPE) ?
- c->vio->hPipe: (TP_file_handle)mysql_socket_getfd(c->vio->mysql_socket);
+ fd= (c->vio_type == VIO_TYPE_NAMEDPIPE) ?
+ c->pipe: (TP_file_handle) mysql_socket_getfd(c->sock);
#else
- fd= mysql_socket_getfd(c->vio->mysql_socket);
+ fd= mysql_socket_getfd(c->sock);
#endif
/* Assign connection to a group. */
thread_group_t *group=
- &all_groups[c->thread_id%group_count];
-
+ &all_groups[get_group_id(c->thread_id)];
thread_group=group;
mysql_mutex_lock(&group->mutex);
@@ -1480,17 +1432,17 @@ TP_connection_generic::~TP_connection_generic()
}
/**
- Set wait timeout for connection.
+ Set wait timeout for connection.
*/
void TP_connection_generic::set_io_timeout(int timeout_sec)
{
DBUG_ENTER("set_wait_timeout");
- /*
+ /*
Calculate wait deadline for this connection.
- Instead of using microsecond_interval_timer() which has a syscall
- overhead, use pool_timer.current_microtime and take
- into account that its value could be off by at most
+ Instead of using microsecond_interval_timer() which has a syscall
+ overhead, use pool_timer.current_microtime and take
+ into account that its value could be off by at most
one tick interval.
*/
@@ -1503,17 +1455,16 @@ void TP_connection_generic::set_io_timeout(int timeout_sec)
}
-#ifndef HAVE_IOCP
/**
- Handle a (rare) special case,where connection needs to
+ Handle a (rare) special case,where connection needs to
migrate to a different group because group_count has changed
- after thread_pool_size setting.
+ after thread_pool_size setting.
*/
-static int change_group(TP_connection_generic *c,
+static int change_group(TP_connection_generic *c,
thread_group_t *old_group,
thread_group_t *new_group)
-{
+{
int ret= 0;
DBUG_ASSERT(c->thread_group == old_group);
@@ -1527,22 +1478,21 @@ static int change_group(TP_connection_generic *c,
}
c->thread_group->connection_count--;
mysql_mutex_unlock(&old_group->mutex);
-
+
/* Add connection to the new group. */
mysql_mutex_lock(&new_group->mutex);
c->thread_group= new_group;
new_group->connection_count++;
/* Ensure that there is a listener in the new group. */
if (!new_group->thread_count)
- ret= create_worker(new_group);
+ ret= create_worker(new_group, false);
mysql_mutex_unlock(&new_group->mutex);
return ret;
}
-#endif
+
int TP_connection_generic::start_io()
{
-#ifndef HAVE_IOCP
/*
Usually, connection will stay in the same group for the entire
connection's life. However, we do allow group_count to
@@ -1552,26 +1502,28 @@ int TP_connection_generic::start_io()
So we recalculate in which group the connection should be, based
on thread_id and current group count, and migrate if necessary.
- */
- thread_group_t *group =
- &all_groups[thd->thread_id%group_count];
-
- if (group != thread_group)
+ */
+ if (fix_group)
{
- if (change_group(this, thread_group, group))
- return -1;
+ fix_group = false;
+ thread_group_t *new_group= &all_groups[get_group_id(thd->thread_id)];
+
+ if (new_group != thread_group)
+ {
+ if (change_group(this, thread_group, new_group))
+ return -1;
+ }
}
-#endif
- /*
- Bind to poll descriptor if not yet done.
- */
+ /*
+ Bind to poll descriptor if not yet done.
+ */
if (!bound_to_poll_descriptor)
{
bound_to_poll_descriptor= true;
return io_poll_associate_fd(thread_group->pollfd, fd, this, OPTIONAL_IO_POLL_READ_PARAM);
}
-
+
return io_poll_start_read(thread_group->pollfd, fd, this, OPTIONAL_IO_POLL_READ_PARAM);
}
@@ -1583,13 +1535,13 @@ int TP_connection_generic::start_io()
static void *worker_main(void *param)
{
-
+
worker_thread_t this_thread;
pthread_detach_this_thread();
my_thread_init();
-
+
DBUG_ENTER("worker_main");
-
+
thread_group_t *thread_group = (thread_group_t *)param;
/* Init per-thread structure */
@@ -1636,7 +1588,8 @@ int TP_pool_generic::init()
DBUG_ENTER("TP_pool_generic::TP_pool_generic");
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));
+ my_malloc(PSI_INSTRUMENT_ME,
+ sizeof(thread_group_t) * threadpool_max_size, MYF(MY_WME|MY_ZEROFILL));
if (!all_groups)
{
threadpool_max_size= 0;
@@ -1647,7 +1600,7 @@ int TP_pool_generic::init()
threadpool_started= true;
for (uint i= 0; i < threadpool_max_size; i++)
{
- thread_group_init(&all_groups[i], get_connection_attrib());
+ thread_group_init(&all_groups[i], get_connection_attrib());
}
set_pool_size(threadpool_size);
if(group_count == 0)
@@ -1659,7 +1612,7 @@ int TP_pool_generic::init()
PSI_register(mutex);
PSI_register(cond);
PSI_register(thread);
-
+
pool_timer.tick_interval= threadpool_stall_limit;
start_timer(&pool_timer);
DBUG_RETURN(0);
@@ -1668,7 +1621,7 @@ int TP_pool_generic::init()
TP_pool_generic::~TP_pool_generic()
{
DBUG_ENTER("tp_end");
-
+
if (!threadpool_started)
DBUG_VOID_RETURN;
@@ -1691,11 +1644,19 @@ TP_pool_generic::~TP_pool_generic()
}
+static my_bool thd_reset_group(THD* thd, void*)
+{
+ auto c= (TP_connection_generic*)thd->event_scheduler.data;
+ if(c)
+ c->fix_group= true;
+ return FALSE;
+}
+
/** Ensure that poll descriptors are created when threadpool_size changes */
int TP_pool_generic::set_pool_size(uint size)
{
bool success= true;
-
+
for(uint i=0; i< size; i++)
{
thread_group_t *group= &all_groups[i];
@@ -1708,7 +1669,7 @@ int TP_pool_generic::set_pool_size(uint size)
{
sql_print_error("io_poll_create() failed, errno=%d", errno);
}
- }
+ }
mysql_mutex_unlock(&group->mutex);
if (!success)
{
@@ -1717,6 +1678,7 @@ int TP_pool_generic::set_pool_size(uint size)
}
}
group_count= size;
+ server_threads.iterate(thd_reset_group);
return 0;
}
@@ -1732,8 +1694,8 @@ int TP_pool_generic::set_stall_limit(uint limit)
/**
Calculate number of idle/waiting threads in the pool.
-
- Sum idle threads over all groups.
+
+ Sum idle threads over all groups.
Don't do any locking, it is not required for stats.
*/
@@ -1750,7 +1712,7 @@ int TP_pool_generic::get_idle_thread_count()
/* Report threadpool problems */
-/**
+/**
Delay in microseconds, after which "pool blocked" message is printed.
(30 sec == 30 Mio usec)
*/
@@ -1779,7 +1741,7 @@ static void print_pool_blocked_message(bool max_threads_reached)
{
ulonglong now;
static bool msg_written;
-
+
now= microsecond_interval_timer();
if (pool_block_start == 0)
{
@@ -1787,14 +1749,14 @@ static void print_pool_blocked_message(bool max_threads_reached)
msg_written = false;
return;
}
-
+
if (now > pool_block_start + BLOCK_MSG_DELAY && !msg_written)
{
if (max_threads_reached)
sql_print_error(MAX_THREADS_REACHED_MSG);
else
sql_print_error(CREATE_THREAD_ERROR_MSG, my_errno);
-
+
sql_print_information("Threadpool has been blocked for %u seconds\n",
(uint)((now- pool_block_start)/1000000));
/* avoid reperated messages for the same blocking situation */
diff --git a/sql/threadpool_generic.h b/sql/threadpool_generic.h
new file mode 100644
index 00000000000..acf5ec6978b
--- /dev/null
+++ b/sql/threadpool_generic.h
@@ -0,0 +1,156 @@
+/* Copyright(C) 2019, 2020, MariaDB
+ *
+ * This program is free software; you can redistribute itand /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 02111 - 1301 USA*/
+
+#if defined (HAVE_POOL_OF_THREADS)
+#include <my_global.h>
+#include <sql_plist.h>
+#include <my_pthread.h>
+#include <mysqld.h>
+#include <threadpool.h>
+#include <violite.h>
+
+#ifdef _WIN32
+#include <windows.h>
+/* AIX may define this, too ?*/
+#define HAVE_IOCP
+#endif
+
+
+#ifdef _WIN32
+typedef HANDLE TP_file_handle;
+#else
+typedef int TP_file_handle;
+#define INVALID_HANDLE_VALUE -1
+#endif
+
+#ifdef __linux__
+#include <sys/epoll.h>
+typedef struct epoll_event native_event;
+#elif defined(HAVE_KQUEUE)
+#include <sys/event.h>
+typedef struct kevent native_event;
+#elif defined (__sun)
+#include <port.h>
+typedef port_event_t native_event;
+#elif defined (HAVE_IOCP)
+typedef OVERLAPPED_ENTRY native_event;
+#else
+#error threadpool is not available on this platform
+#endif
+
+struct thread_group_t;
+
+/* Per-thread structure for workers */
+struct worker_thread_t
+{
+ ulonglong event_count; /* number of request handled by this thread */
+ thread_group_t* thread_group;
+ worker_thread_t* next_in_list;
+ worker_thread_t** prev_in_list;
+ mysql_cond_t cond;
+ bool woken;
+};
+
+typedef I_P_List<worker_thread_t, I_P_List_adapter<worker_thread_t,
+ & worker_thread_t::next_in_list,
+ & worker_thread_t::prev_in_list>,
+ I_P_List_counter
+>
+worker_list_t;
+
+struct TP_connection_generic :public TP_connection
+{
+ TP_connection_generic(CONNECT* c);
+ ~TP_connection_generic();
+
+ virtual int init() { return 0; };
+ virtual void set_io_timeout(int sec);
+ virtual int start_io();
+ virtual void wait_begin(int type);
+ virtual void wait_end();
+
+ thread_group_t* thread_group;
+ TP_connection_generic* next_in_queue;
+ TP_connection_generic** prev_in_queue;
+ ulonglong abs_wait_timeout;
+ ulonglong enqueue_time;
+ TP_file_handle fd;
+ bool bound_to_poll_descriptor;
+ int waiting;
+ bool fix_group;
+#ifdef HAVE_IOCP
+ OVERLAPPED overlapped;
+#endif
+#ifdef _WIN32
+ enum_vio_type vio_type;
+#endif
+};
+
+
+typedef I_P_List<TP_connection_generic,
+ I_P_List_adapter<TP_connection_generic,
+ & TP_connection_generic::next_in_queue,
+ & TP_connection_generic::prev_in_queue>,
+ I_P_List_counter,
+ I_P_List_fast_push_back<TP_connection_generic> >
+ connection_queue_t;
+
+const int NQUEUES = 2; /* We have high and low priority queues*/
+
+enum class operation_origin
+{
+ WORKER,
+ LISTENER
+};
+
+struct thread_group_counters_t
+{
+ ulonglong thread_creations;
+ ulonglong thread_creations_due_to_stall;
+ ulonglong wakes;
+ ulonglong wakes_due_to_stall;
+ ulonglong throttles;
+ ulonglong stalls;
+ ulonglong dequeues[2];
+ ulonglong polls[2];
+};
+
+struct thread_group_t
+{
+ mysql_mutex_t mutex;
+ connection_queue_t queues[NQUEUES];
+ worker_list_t waiting_threads;
+ worker_thread_t* listener;
+ pthread_attr_t* pthread_attr;
+ TP_file_handle pollfd;
+ int thread_count;
+ int active_thread_count;
+ int connection_count;
+ /* Stats for the deadlock detection timer routine.*/
+ int io_event_count;
+ int queue_event_count;
+ ulonglong last_thread_creation_time;
+ int shutdown_pipe[2];
+ bool shutdown;
+ bool stalled;
+ thread_group_counters_t counters;
+ char pad[CPU_LEVEL1_DCACHE_LINESIZE];
+};
+
+#define TP_INCREMENT_GROUP_COUNTER(group,var) do {group->counters.var++;}while(0)
+
+extern thread_group_t* all_groups;
+#endif
+
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
index 7d721c88f96..6003b06bc7b 100644
--- a/sql/threadpool_win.cc
+++ b/sql/threadpool_win.cc
@@ -31,31 +31,6 @@
#include <threadpool.h>
#include <windows.h>
-
-
-/*
- WEAK_SYMBOL(return_type, function_name, argument_type1,..,argument_typeN)
-
- Declare and load function pointer from kernel32. The name of the static
- variable that holds the function pointer is my_<original function name>
- This should be combined with
- #define <original function name> my_<original function name>
- so that one could use Widows APIs transparently, without worrying whether
- they are present in a particular version or not.
-
- Of course, prior to use of any function there should be a check for correct
- Windows version, or check whether function pointer is not NULL.
-*/
-#define WEAK_SYMBOL(return_type, function, ...) \
- typedef return_type (WINAPI *pFN_##function)(__VA_ARGS__); \
- static pFN_##function my_##function = (pFN_##function) \
- (GetProcAddress(GetModuleHandle("kernel32"),#function))
-
-
-WEAK_SYMBOL(BOOL, SetThreadpoolStackInformation, PTP_POOL,
- PTP_POOL_STACK_INFORMATION);
-#define SetThreadpoolStackInformation my_SetThreadpoolStackInformation
-
/* Log a warning */
static void tp_log_warning(const char *msg, const char *fct)
{
@@ -84,10 +59,10 @@ PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ()
*/
-static void CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance,
+static void CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance,
PVOID context, PTP_TIMER timer);
-static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
+static void CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io);
@@ -153,7 +128,7 @@ void TP_pool_win::add(TP_connection *c)
TP_connection_win::TP_connection_win(CONNECT *c) :
TP_connection(c),
- timeout(ULONGLONG_MAX),
+ timeout(ULONGLONG_MAX),
callback_instance(0),
io(0),
timer(0),
@@ -167,15 +142,14 @@ int TP_connection_win::init()
{
memset(&overlapped, 0, sizeof(OVERLAPPED));
- Vio *vio = connect->vio;
- switch ((vio_type = vio->type))
+ switch ((vio_type = connect->vio_type))
{
case VIO_TYPE_SSL:
case VIO_TYPE_TCPIP:
- handle= (HANDLE)mysql_socket_getfd(vio->mysql_socket);
+ handle= (HANDLE) mysql_socket_getfd(connect->sock);
break;
case VIO_TYPE_NAMEDPIPE:
- handle= (HANDLE)vio->hPipe;
+ handle= connect->pipe;
break;
default:
abort();
@@ -244,14 +218,14 @@ int TP_connection_win::start_io()
if (retval == 0 || last_error == ERROR_MORE_DATA)
{
/*
- IO successfully finished (synchronously).
- If skip_completion_port_on_success is set, we need to handle it right
+ IO successfully finished (synchronously).
+ If skip_completion_port_on_success is set, we need to handle it right
here, because completion callback would not be executed by the pool.
*/
if (skip_completion_port_on_success)
{
CancelThreadpoolIo(io);
- io_completion_callback(callback_instance, this, &overlapped, last_error,
+ io_completion_callback(callback_instance, this, &overlapped, last_error,
num_bytes, io);
}
return 0;
@@ -348,7 +322,7 @@ static void pre_callback(PVOID context, PTP_CALLBACK_INSTANCE instance)
/*
- Decrement number of threads when a thread exits .
+ Decrement number of threads when a thread exits.
On Windows, FlsAlloc() provides the thread destruction callbacks.
*/
static VOID WINAPI thread_destructor(void *data)
@@ -372,7 +346,7 @@ static inline void tp_callback(PTP_CALLBACK_INSTANCE instance, PVOID context)
/*
Handle read completion/notification.
*/
-static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
+static VOID CALLBACK io_completion_callback(PTP_CALLBACK_INSTANCE instance,
PVOID context, PVOID overlapped, ULONG io_result, ULONG_PTR nbytes, PTP_IO io)
{
TP_connection_win *c= (TP_connection_win *)context;
@@ -452,19 +426,13 @@ int TP_pool_win::init()
}
}
- /*
- Control stack size (OS must be Win7 or later)
- */
- if (SetThreadpoolStackInformation)
+ TP_POOL_STACK_INFORMATION stackinfo;
+ stackinfo.StackCommit = 0;
+ stackinfo.StackReserve = (SIZE_T)my_thread_stack_size;
+ if (!SetThreadpoolStackInformation(pool, &stackinfo))
{
- TP_POOL_STACK_INFORMATION stackinfo;
- stackinfo.StackCommit = 0;
- stackinfo.StackReserve = (SIZE_T)my_thread_stack_size;
- if (!SetThreadpoolStackInformation(pool, &stackinfo))
- {
- tp_log_warning("Can't set threadpool stack size",
- "SetThreadpoolStackInformation");
- }
+ tp_log_warning("Can't set threadpool stack size",
+ "SetThreadpoolStackInformation");
}
return 0;
}
diff --git a/sql/transaction.cc b/sql/transaction.cc
index aecee04c364..421aa1cfc51 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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,6 +25,8 @@
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_acl.h"
#include "semisync_master.h"
+#include <pfs_transaction_provider.h>
+#include <mysql/psi/mysql_transaction.h>
#ifdef WITH_WSREP
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
@@ -57,7 +60,14 @@ void trans_reset_one_shot_chistics(THD *thd)
thd->tx_read_only= thd->variables.tx_read_only;
}
-/* Conditions under which the transaction state must not change. */
+
+/*
+ Conditions under which the transaction state must not change
+
+ @result TRUE Transaction can not commit
+ @result FALSE Transaction can commit
+*/
+
static bool trans_check(THD *thd)
{
DBUG_ENTER("trans_check");
@@ -66,15 +76,17 @@ static bool trans_check(THD *thd)
Always commit statement transaction before manipulating with
the normal one.
*/
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ DBUG_ASSERT(thd->transaction->stmt.is_empty());
if (unlikely(thd->in_sub_stmt))
+ {
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
- if (thd->transaction.xid_state.is_explicit_XA())
- thd->transaction.xid_state.er_xaer_rmfail();
- else
+ DBUG_RETURN(TRUE);
+ }
+ if (likely(!thd->transaction->xid_state.is_explicit_XA()))
DBUG_RETURN(FALSE);
+ thd->transaction->xid_state.er_xaer_rmfail();
DBUG_RETURN(TRUE);
}
@@ -100,7 +112,8 @@ bool trans_begin(THD *thd, uint flags)
if (trans_check(thd))
DBUG_RETURN(TRUE);
- thd->locked_tables_list.unlock_locked_tables(thd);
+ if (thd->locked_tables_list.unlock_locked_tables(thd))
+ DBUG_RETURN(true);
DBUG_ASSERT(!thd->locked_tables_mode);
@@ -126,10 +139,10 @@ bool trans_begin(THD *thd, uint flags)
The following set should not be needed as transaction state should
already be reset. We should at some point change this to an assert.
*/
- thd->transaction.all.reset();
+ thd->transaction->all.reset();
thd->has_waiter= false;
thd->waiting_on_group_commit= false;
- thd->transaction.start_time.reset(thd);
+ thd->transaction->start_time.reset(thd);
if (res)
DBUG_RETURN(TRUE);
@@ -160,7 +173,7 @@ bool trans_begin(THD *thd, uint flags)
compatibility.
*/
const bool user_is_super=
- MY_TEST(thd->security_ctx->master_access & SUPER_ACL);
+ MY_TEST(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY);
if (opt_readonly && !user_is_super)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
@@ -208,6 +221,23 @@ bool trans_begin(THD *thd, uint flags)
#endif //EMBEDDED_LIBRARY
res= ha_start_consistent_snapshot(thd);
}
+ /*
+ Register transaction start in performance schema if not done already.
+ We handle explicitly started transactions here, implicitly started
+ transactions (and single-statement transactions in autocommit=1 mode)
+ are handled in trans_register_ha().
+ We can't handle explicit transactions in the same way as implicit
+ because we want to correctly attribute statements which follow
+ BEGIN but do not touch any transactional tables.
+ */
+ if (thd->m_transaction_psi == NULL)
+ {
+ thd->m_transaction_psi= MYSQL_START_TRANSACTION(&thd->m_transaction_state,
+ NULL, 0, thd->tx_isolation,
+ thd->tx_read_only, false);
+ DEBUG_SYNC(thd, "after_set_transaction_psi_before_set_transaction_gtid");
+ //gtid_set_performance_schema_values(thd);
+ }
DBUG_RETURN(MY_TEST(res));
}
@@ -240,26 +270,22 @@ bool trans_commit(THD *thd)
mysql_mutex_assert_not_owner(&LOCK_after_binlog_sync);
mysql_mutex_assert_not_owner(&LOCK_commit_ordered);
- /*
- if res is non-zero, then ha_commit_trans has rolled back the
- transaction, so the hooks for rollback will be called.
- */
- if (res)
- {
+ /*
+ if res is non-zero, then ha_commit_trans has rolled back the
+ transaction, so the hooks for rollback will be called.
+ */
#ifdef HAVE_REPLICATION
+ if (res)
repl_semisync_master.wait_after_rollback(thd, FALSE);
-#endif
- }
else
- {
-#ifdef HAVE_REPLICATION
repl_semisync_master.wait_after_commit(thd, FALSE);
#endif
- }
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.reset();
+ thd->transaction->all.reset();
thd->lex->start_transaction_opt= 0;
+ /* The transaction should be marked as complete in P_S. */
+ DBUG_ASSERT(thd->m_transaction_psi == NULL);
trans_track_end_trx(thd);
DBUG_RETURN(MY_TEST(res));
@@ -286,8 +312,10 @@ bool trans_commit_implicit(THD *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))
@@ -302,7 +330,10 @@ bool trans_commit_implicit(THD *thd)
}
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.reset();
+ thd->transaction->all.reset();
+
+ /* The transaction should be marked as complete in P_S. */
+ DBUG_ASSERT(thd->m_transaction_psi == NULL);
/*
Upon implicit commit, reset the current transaction
@@ -342,12 +373,15 @@ bool trans_rollback(THD *thd)
#ifdef HAVE_REPLICATION
repl_semisync_master.wait_after_rollback(thd, FALSE);
#endif
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
/* Reset the binlog transaction marker */
- thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
- thd->transaction.all.reset();
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG |
+ OPTION_GTID_BEGIN);
+ thd->transaction->all.reset();
thd->lex->start_transaction_opt= 0;
+ /* The transaction should be marked as complete in P_S. */
+ DBUG_ASSERT(thd->m_transaction_psi == NULL);
+
trans_track_end_trx(thd);
DBUG_RETURN(MY_TEST(res));
@@ -380,7 +414,7 @@ bool trans_rollback_implicit(THD *thd)
Don't perform rollback in the middle of sub-statement, wait till
its end.
*/
- DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt);
+ DBUG_ASSERT(thd->transaction->stmt.is_empty() && !thd->in_sub_stmt);
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
@@ -391,10 +425,12 @@ bool trans_rollback_implicit(THD *thd)
preserve backward compatibility.
*/
thd->variables.option_bits&= ~(OPTION_KEEP_LOG);
- thd->transaction.all.reset();
+ thd->transaction->all.reset();
/* Rollback should clear transaction_rollback_request flag. */
- DBUG_ASSERT(! thd->transaction_rollback_request);
+ DBUG_ASSERT(!thd->transaction_rollback_request);
+ /* The transaction should be marked as complete in P_S. */
+ DBUG_ASSERT(thd->m_transaction_psi == NULL);
trans_track_end_trx(thd);
@@ -431,7 +467,7 @@ bool trans_commit_stmt(THD *thd)
thd->merge_unsafe_rollback_flags();
- if (thd->transaction.stmt.ha_list)
+ if (thd->transaction->stmt.ha_list)
{
res= ha_commit_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
@@ -462,7 +498,11 @@ bool trans_commit_stmt(THD *thd)
#endif
}
- thd->transaction.stmt.reset();
+ /* In autocommit=1 mode the transaction should be marked as complete in P_S */
+ DBUG_ASSERT(thd->in_active_multi_stmt_transaction() ||
+ thd->m_transaction_psi == NULL);
+
+ thd->transaction->stmt.reset();
DBUG_RETURN(MY_TEST(res));
}
@@ -490,7 +530,7 @@ bool trans_rollback_stmt(THD *thd)
thd->merge_unsafe_rollback_flags();
- if (thd->transaction.stmt.ha_list)
+ if (thd->transaction->stmt.ha_list)
{
ha_rollback_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
@@ -501,7 +541,11 @@ bool trans_rollback_stmt(THD *thd)
repl_semisync_master.wait_after_rollback(thd, FALSE);
#endif
- thd->transaction.stmt.reset();
+ /* In autocommit=1 mode the transaction should be marked as complete in P_S */
+ DBUG_ASSERT(thd->in_active_multi_stmt_transaction() ||
+ thd->m_transaction_psi == NULL);
+
+ thd->transaction->stmt.reset();
DBUG_RETURN(FALSE);
}
@@ -510,11 +554,12 @@ bool trans_rollback_stmt(THD *thd)
static SAVEPOINT **
find_savepoint(THD *thd, LEX_CSTRING name)
{
- SAVEPOINT **sv= &thd->transaction.savepoints;
+ SAVEPOINT **sv= &thd->transaction->savepoints;
while (*sv)
{
- if (my_strnncoll(system_charset_info, (uchar *) name.str, name.length,
+ if (system_charset_info->strnncoll(
+ (uchar *) name.str, name.length,
(uchar *) (*sv)->name, (*sv)->length) == 0)
break;
sv= &(*sv)->prev;
@@ -543,7 +588,7 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name)
!opt_using_transactions)
DBUG_RETURN(FALSE);
- if (thd->transaction.xid_state.check_has_uncommitted_xa())
+ if (thd->transaction->xid_state.check_has_uncommitted_xa())
DBUG_RETURN(TRUE);
sv= find_savepoint(thd, name);
@@ -554,14 +599,14 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name)
ha_release_savepoint(thd, *sv);
*sv= (*sv)->prev;
}
- else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
+ else if ((newsv= (SAVEPOINT *) alloc_root(&thd->transaction->mem_root,
savepoint_alloc_size)) == NULL)
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
DBUG_RETURN(TRUE);
}
- newsv->name= strmake_root(&thd->transaction.mem_root, name.str, name.length);
+ newsv->name= strmake_root(&thd->transaction->mem_root, name.str, name.length);
newsv->length= (uint)name.length;
/*
@@ -572,8 +617,8 @@ bool trans_savepoint(THD *thd, LEX_CSTRING name)
if (unlikely(ha_savepoint(thd, newsv)))
DBUG_RETURN(TRUE);
- newsv->prev= thd->transaction.savepoints;
- thd->transaction.savepoints= newsv;
+ newsv->prev= thd->transaction->savepoints;
+ thd->transaction->savepoints= newsv;
/*
Remember locks acquired before the savepoint was set.
@@ -619,7 +664,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name)
DBUG_RETURN(TRUE);
}
- if (thd->transaction.xid_state.check_has_uncommitted_xa())
+ if (thd->transaction->xid_state.check_has_uncommitted_xa())
DBUG_RETURN(TRUE);
/**
@@ -652,13 +697,13 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name)
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->transaction->all.modified_non_trans_table) &&
!thd->slave_thread)
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER_THD(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK));
- thd->transaction.savepoints= sv;
+ thd->transaction->savepoints= sv;
if (!res && mdl_can_safely_rollback_to_savepoint)
thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
@@ -696,7 +741,7 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
if (ha_release_savepoint(thd, sv))
res= TRUE;
- thd->transaction.savepoints= sv->prev;
+ thd->transaction->savepoints= sv->prev;
DBUG_RETURN(MY_TEST(res));
}
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 05b8389a521..8e08a201e88 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -1543,6 +1543,8 @@ tz_init_table_list(TABLE_LIST *tz_tabs)
}
}
+static PSI_memory_key key_memory_tz_storage;
+
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_tz_LOCK;
@@ -1551,6 +1553,11 @@ static PSI_mutex_info all_tz_mutexes[]=
{ & key_tz_LOCK, "tz_LOCK", PSI_FLAG_GLOBAL}
};
+static PSI_memory_info all_tz_memory[]=
+{
+ { &key_memory_tz_storage, "tz_storage", PSI_FLAG_GLOBAL}
+};
+
static void init_tz_psi_keys(void)
{
const char* category= "sql";
@@ -1561,6 +1568,9 @@ static void init_tz_psi_keys(void)
count= array_elements(all_tz_mutexes);
PSI_server->register_mutex(category, all_tz_mutexes, count);
+
+ count= array_elements(all_tz_memory);
+ mysql_memory_register(category, all_tz_memory, count);
}
#endif /* HAVE_PSI_INTERFACE */
@@ -1615,20 +1625,20 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
thd->store_globals();
/* Init all memory structures that require explicit destruction */
- if (my_hash_init(&tz_names, &my_charset_latin1, 20,
- 0, 0, (my_hash_get_key) my_tz_names_get_key, 0, 0))
+ if (my_hash_init(key_memory_tz_storage, &tz_names, &my_charset_latin1, 20, 0,
+ 0, (my_hash_get_key) my_tz_names_get_key, 0, 0))
{
sql_print_error("Fatal error: OOM while initializing time zones");
goto end;
}
- if (my_hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
- (my_hash_get_key)my_offset_tzs_get_key, 0, 0))
+ if (my_hash_init(key_memory_tz_storage, &offset_tzs, &my_charset_latin1, 26,
+ 0, 0, (my_hash_get_key)my_offset_tzs_get_key, 0, 0))
{
sql_print_error("Fatal error: OOM while initializing time zones");
my_hash_free(&tz_names);
goto end;
}
- init_sql_alloc(&tz_storage, "timezone_storage", 32 * 1024, 0, MYF(0));
+ init_sql_alloc(key_memory_tz_storage, &tz_storage, 32 * 1024, 0, MYF(0));
mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
tz_inited= 1;
@@ -1890,7 +1900,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
Most probably user has mistyped time zone name, so no need to bark here
unless we need it for debugging.
*/
- sql_print_error("Can't find description of time zone '%.*s'",
+ sql_print_error("Can't find description of time zone '%.*b'",
tz_name->length(), tz_name->ptr());
#endif
goto end;
@@ -2320,16 +2330,21 @@ my_tz_find(THD *thd, const String *name)
else if (time_zone_tables_exist)
{
TABLE_LIST tz_tables[MY_TZ_TABLES_COUNT];
- Open_tables_backup open_tables_state_backup;
+ /*
+ Allocate start_new_trans with malloc as it's > 4000 bytes and this
+ function can be called deep inside a stored procedure
+ */
+ start_new_trans *new_trans= new start_new_trans(thd);
tz_init_table_list(tz_tables);
init_mdl_requests(tz_tables);
- if (!open_system_tables_for_read(thd, tz_tables,
- &open_tables_state_backup))
+ if (!open_system_tables_for_read(thd, tz_tables))
{
result_tz= tz_load_from_open_tables(name, tz_tables);
- close_system_tables(thd, &open_tables_state_backup);
+ thd->commit_whole_transaction_and_close_tables();
}
+ new_trans->restore_old_transaction();
+ delete new_trans;
}
}
@@ -2568,8 +2583,8 @@ 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, "timezone_storage", 32768, 0,
- MYF(MY_THREAD_SPECIFIC));
+ init_alloc_root(PSI_INSTRUMENT_ME, &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
@@ -2641,8 +2656,7 @@ static struct my_option my_long_options[] =
C_MODE_START
-static my_bool get_one_option(int optid, const struct my_option *,
- char *argument);
+static my_bool get_one_option(const struct my_option *, char *, const char *);
C_MODE_END
static void print_version(void)
@@ -2664,9 +2678,9 @@ static void print_usage(void)
static my_bool
-get_one_option(int optid, const struct my_option *opt, char *argument)
+get_one_option(const struct my_option *opt, char *argument, const char *)
{
- switch(optid) {
+ switch(opt->id) {
case '#':
#ifndef DBUG_OFF
DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysq_tzinfo_to_sql.trace");
@@ -2763,7 +2777,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, "timezone_storage", 32768, 0, MYF(0));
+ init_alloc_root(PSI_INSTRUMENT_ME, &tz_storage, 32768, 0, MYF(0));
if (tz_load(argv[0], &tz_info, &tz_storage))
{
diff --git a/sql/tztime.h b/sql/tztime.h
index d24a379e634..9e5d469925f 100644
--- a/sql/tztime.h
+++ b/sql/tztime.h
@@ -73,7 +73,7 @@ protected:
};
extern Time_zone * my_tz_UTC;
-extern Time_zone * my_tz_SYSTEM;
+extern MYSQL_PLUGIN_IMPORT Time_zone * my_tz_SYSTEM;
extern Time_zone * my_tz_OFFSET0;
extern Time_zone * my_tz_find(THD *thd, const String *name);
extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 1b8e91f2566..a0cebe3e4dd 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2001, 2010, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+ Copyright (c) 2010, 2020, 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
@@ -39,7 +39,6 @@
#include "my_tree.h" // element_count
#include "uniques.h" // Unique
#include "sql_sort.h"
-#include "myisamchk.h" // BUFFPEK
int unique_write_to_file(uchar* key, element_count count, Unique *unique)
{
@@ -94,8 +93,8 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
init_tree(&tree, (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,
- MYF(MY_THREAD_SPECIFIC));
+ my_init_dynamic_array(PSI_INSTRUMENT_ME, &file_ptrs, sizeof(Merge_chunk), 16,
+ 16, MYF(MY_THREAD_SPECIFIC));
/*
If you change the following, change it in get_max_elements function, too.
*/
@@ -162,7 +161,7 @@ inline double log2_n_fact(double x)
static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
uint *first, uint *last,
- uint compare_factor)
+ double compare_factor)
{
uint total_buf_elems= 0;
for (uint *pbuf= first; pbuf <= last; pbuf++)
@@ -207,7 +206,7 @@ static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
static double get_merge_many_buffs_cost(uint *buffer,
uint maxbuffer, uint max_n_elems,
uint last_n_elems, int elem_size,
- uint compare_factor)
+ double compare_factor)
{
int i;
double total_cost= 0.0;
@@ -307,7 +306,7 @@ static double get_merge_many_buffs_cost(uint *buffer,
double Unique::get_use_cost(uint *buffer, size_t nkeys, uint key_size,
size_t max_in_memory_size,
- uint compare_factor,
+ double compare_factor,
bool intersect_fl, bool *in_memory)
{
size_t max_elements_in_tree;
@@ -378,10 +377,10 @@ Unique::~Unique()
/* Write tree to disk; clear tree */
bool Unique::flush()
{
- BUFFPEK file_ptr;
+ Merge_chunk file_ptr;
elements+= tree.elements_in_tree;
- file_ptr.count=tree.elements_in_tree;
- file_ptr.file_pos=my_b_tell(&file);
+ file_ptr.set_rowcount(tree.elements_in_tree);
+ file_ptr.set_file_position(my_b_tell(&file));
tree_walk_action action= min_dupl_count ?
(tree_walk_action) unique_write_to_file_with_count :
@@ -493,7 +492,7 @@ void put_counter_into_merged_element(void *ptr, uint ofs, element_count cnt)
*/
static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
- uint key_length, BUFFPEK *begin, BUFFPEK *end,
+ uint key_length, Merge_chunk *begin, Merge_chunk *end,
tree_walk_action walk_action, void *walk_action_arg,
qsort_cmp2 compare, void *compare_arg,
IO_CACHE *file, bool with_counters)
@@ -502,7 +501,8 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
QUEUE queue;
if (end <= begin ||
merge_buffer_size < (size_t) (key_length * (end - begin + 1)) ||
- init_queue(&queue, (uint) (end - begin), offsetof(BUFFPEK, key), 0,
+ init_queue(&queue, (uint) (end - begin),
+ offsetof(Merge_chunk, m_current_key), 0,
buffpek_compare, &compare_context, 0, 0))
return 1;
/* we need space for one key when a piece of merge buffer is re-read */
@@ -513,10 +513,16 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
/* if piece_size is aligned reuse_freed_buffer will always hit */
uint piece_size= max_key_count_per_piece * key_length;
ulong bytes_read; /* to hold return value of read_to_buffer */
- BUFFPEK *top;
+ Merge_chunk *top;
int res= 1;
uint cnt_ofs= key_length - (with_counters ? sizeof(element_count) : 0);
element_count cnt;
+
+ // read_to_buffer() needs only rec_length.
+ Sort_param sort_param;
+ sort_param.rec_length= key_length;
+ DBUG_ASSERT(!sort_param.using_addon_fields());
+
/*
Invariant: queue must contain top element from each tree, until a tree
is not completely walked through.
@@ -525,15 +531,16 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
*/
for (top= begin; top != end; ++top)
{
- top->base= merge_buffer + (top - begin) * piece_size;
- top->max_keys= max_key_count_per_piece;
- bytes_read= read_to_buffer(file, top, key_length);
+ top->set_buffer(merge_buffer + (top - begin) * piece_size,
+ merge_buffer + (top - begin) * piece_size + piece_size);
+ top->set_max_keys(max_key_count_per_piece);
+ bytes_read= read_to_buffer(file, top, &sort_param, false);
if (unlikely(bytes_read == (ulong) -1))
goto end;
DBUG_ASSERT(bytes_read);
queue_insert(&queue, (uchar *) top);
}
- top= (BUFFPEK *) queue_top(&queue);
+ top= (Merge_chunk *) queue_top(&queue);
while (queue.elements > 1)
{
/*
@@ -543,20 +550,21 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
elements in each tree are unique. Action is applied only to unique
elements.
*/
- void *old_key= top->key;
+ void *old_key= top->current_key();
/*
read next key from the cache or from the file and push it to the
queue; this gives new top.
*/
- top->key+= key_length;
- if (--top->mem_count)
+ top->advance_current_key(key_length);
+ top->decrement_mem_count();
+ if (top->mem_count())
queue_replace_top(&queue);
else /* next piece should be read */
{
/* save old_key not to overwrite it in read_to_buffer */
memcpy(save_key_buff, old_key, key_length);
old_key= save_key_buff;
- bytes_read= read_to_buffer(file, top, key_length);
+ bytes_read= read_to_buffer(file, top, &sort_param, false);
if (unlikely(bytes_read == (ulong) -1))
goto end;
else if (bytes_read) /* top->key, top->mem_count are reset */
@@ -571,9 +579,9 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
reuse_freed_buff(&queue, top, key_length);
}
}
- top= (BUFFPEK *) queue_top(&queue);
+ top= (Merge_chunk *) queue_top(&queue);
/* new top has been obtained; if old top is unique, apply the action */
- if (compare(compare_arg, old_key, top->key))
+ if (compare(compare_arg, old_key, top->current_key()))
{
cnt= with_counters ?
get_counter_from_merged_element(old_key, cnt_ofs) : 1;
@@ -582,9 +590,9 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
}
else if (with_counters)
{
- cnt= get_counter_from_merged_element(top->key, cnt_ofs);
+ cnt= get_counter_from_merged_element(top->current_key(), cnt_ofs);
cnt+= get_counter_from_merged_element(old_key, cnt_ofs);
- put_counter_into_merged_element(top->key, cnt_ofs, cnt);
+ put_counter_into_merged_element(top->current_key(), cnt_ofs, cnt);
}
}
/*
@@ -598,13 +606,13 @@ static bool merge_walk(uchar *merge_buffer, size_t merge_buffer_size,
{
cnt= with_counters ?
- get_counter_from_merged_element(top->key, cnt_ofs) : 1;
- if (walk_action(top->key, cnt, walk_action_arg))
+ get_counter_from_merged_element(top->current_key(), cnt_ofs) : 1;
+ if (walk_action(top->current_key(), cnt, walk_action_arg))
goto end;
- top->key+= key_length;
+ top->advance_current_key(key_length);
}
- while (--top->mem_count);
- bytes_read= read_to_buffer(file, top, key_length);
+ while (top->decrement_mem_count());
+ bytes_read= read_to_buffer(file, top, &sort_param, false);
if (unlikely(bytes_read == (ulong) -1))
goto end;
}
@@ -657,16 +665,18 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
is needed when a piece of merge buffer is re-read, see merge_walk()
*/
size_t buff_sz= MY_MAX(MERGEBUFF2+1, 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(key_memory_Unique_merge_buffer,
+ buff_sz, MYF(MY_THREAD_SPECIFIC|MY_WME))))
return 1;
if (buff_sz < full_size * (file_ptrs.elements + 1UL))
- res= merge(table, merge_buffer, buff_sz >= full_size * MERGEBUFF2) ;
+ res= merge(table, merge_buffer, buff_sz,
+ buff_sz >= full_size * MERGEBUFF2) ;
if (!res)
{
res= merge_walk(merge_buffer, buff_sz, full_size,
- (BUFFPEK *) file_ptrs.buffer,
- (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
+ (Merge_chunk *) file_ptrs.buffer,
+ (Merge_chunk *) file_ptrs.buffer + file_ptrs.elements,
action, walk_action_arg,
tree.compare, tree.custom_arg, &file, with_counters);
}
@@ -687,16 +697,18 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
All params are 'IN':
table the parameter to access sort context
buff merge buffer
+ buff_size size of merge buffer
without_last_merge TRUE <=> do not perform the last merge
RETURN VALUE
0 OK
<> 0 error
*/
-bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
+bool Unique::merge(TABLE *table, uchar *buff, size_t buff_size,
+ bool without_last_merge)
{
IO_CACHE *outfile= &sort.io_cache;
- BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer;
+ Merge_chunk *file_ptr= (Merge_chunk*) file_ptrs.buffer;
uint maxbuffer= file_ptrs.elements - 1;
my_off_t save_pos;
bool error= 1;
@@ -726,8 +738,17 @@ bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
sort_param.cmp_context.key_compare= tree.compare;
sort_param.cmp_context.key_compare_arg= tree.custom_arg;
+ /*
+ We need to remove the size allocated for the unique buffer.
+ The sort_buffer_size is:
+ MY_MAX(MERGEBUFF2+1, max_in_memory_size/full_size+1) * full_size;
+ */
+ buff_size-= full_size;
+
/* Merge the buffers to one file, removing duplicates */
- if (merge_many_buff(&sort_param,buff,file_ptr,&maxbuffer,&file))
+ if (merge_many_buff(&sort_param,
+ Bounds_checked_array<uchar>(buff, buff_size),
+ file_ptr,&maxbuffer,&file))
goto err;
if (flush_io_cache(&file) ||
reinit_io_cache(&file,READ_CACHE,0L,0,0))
@@ -739,7 +760,8 @@ bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
file_ptrs.elements= maxbuffer+1;
return 0;
}
- if (merge_index(&sort_param, buff, file_ptr, maxbuffer, &file, outfile))
+ if (merge_index(&sort_param, Bounds_checked_array<uchar>(buff, buff_size),
+ file_ptr, maxbuffer, &file, outfile))
goto err;
error= 0;
err:
@@ -771,7 +793,8 @@ bool Unique::get(TABLE *table)
{
/* Whole tree is in memory; Don't use disk if you don't need to */
if ((sort.record_pointers= (uchar*)
- my_malloc(size * tree.elements_in_tree, MYF(MY_THREAD_SPECIFIC))))
+ my_malloc(key_memory_Filesort_info_record_pointers,
+ size * tree.elements_in_tree, MYF(MY_THREAD_SPECIFIC))))
{
uchar *save_record_pointers= sort.record_pointers;
tree_walk_action action= min_dupl_count ?
@@ -795,11 +818,12 @@ bool Unique::get(TABLE *table)
one key for Sort_param::unique_buff
*/
size_t buff_sz= MY_MAX(MERGEBUFF2+1, max_in_memory_size/full_size+1) * full_size;
- if (!(sort_buffer= (uchar*) my_malloc(buff_sz,
+
+ if (!(sort_buffer= (uchar*) my_malloc(key_memory_Unique_sort_buffer, buff_sz,
MYF(MY_THREAD_SPECIFIC|MY_WME))))
DBUG_RETURN(1);
- if (merge(table, sort_buffer, FALSE))
+ if (merge(table, sort_buffer, buff_sz, FALSE))
goto err;
rc= 0;
diff --git a/sql/uniques.h b/sql/uniques.h
index a4f5b378fb0..7e12a391fbd 100644
--- a/sql/uniques.h
+++ b/sql/uniques.h
@@ -45,7 +45,7 @@ class Unique :public Sql_alloc
always 0 for unions, > 0 for intersections */
bool with_counters;
- bool merge(TABLE *table, uchar *buff, bool without_last_merge);
+ bool merge(TABLE *table, uchar *buff, size_t size, bool without_last_merge);
bool flush();
public:
@@ -72,13 +72,14 @@ public:
bool get(TABLE *table);
/* Cost of searching for an element in the tree */
- inline static double get_search_cost(ulonglong tree_elems, uint compare_factor)
+ inline static double get_search_cost(ulonglong tree_elems,
+ double compare_factor)
{
return log((double) tree_elems) / (compare_factor * M_LN2);
}
static double get_use_cost(uint *buffer, size_t nkeys, uint key_size,
- size_t max_in_memory_size, uint compare_factor,
+ size_t max_in_memory_size, double compare_factor,
bool intersect_fl, bool *in_memory);
inline static int get_cost_calc_buff_size(size_t nkeys, uint key_size,
size_t max_in_memory_size)
diff --git a/sql/unireg.cc b/sql/unireg.cc
index beb754ed852..c299549843f 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -86,6 +86,13 @@ static uchar* extra2_write_str(uchar *pos, const LEX_CSTRING &str)
return pos + str.length;
}
+static uchar* extra2_write_str(uchar *pos, const Binary_string *str)
+{
+ pos= extra2_write_len(pos, str->length());
+ memcpy(pos, str->ptr(), str->length());
+ return pos + str->length();
+}
+
static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
const LEX_CSTRING &str)
{
@@ -154,11 +161,79 @@ bool has_extra2_field_flags(List<Create_field> &create_fields)
return false;
}
-static size_t extra2_str_size(size_t len)
+static uint gis_field_options_image(uchar *buff,
+ List<Create_field> &create_fields)
{
- return (len > 255 ? 3 : 1) + len;
+ uint image_size= 0;
+ List_iterator<Create_field> it(create_fields);
+ Create_field *field;
+ while ((field= it++))
+ {
+ if (field->real_field_type() != MYSQL_TYPE_GEOMETRY)
+ continue;
+ uchar *cbuf= buff ? buff + image_size : NULL;
+ image_size+= field->type_handler()->
+ Column_definition_gis_options_image(cbuf, *field);
+ }
+ return image_size;
}
+
+class Field_data_type_info_image: public BinaryStringBuffer<512>
+{
+ static uchar *store_length(uchar *pos, ulonglong length)
+ {
+ return net_store_length(pos, length);
+ }
+ static uchar *store_string(uchar *pos, const Binary_string *str)
+ {
+ pos= store_length(pos, str->length());
+ memcpy(pos, str->ptr(), str->length());
+ return pos + str->length();
+ }
+ static uint store_length_required_length(ulonglong length)
+ {
+ return net_length_size(length);
+ }
+public:
+ Field_data_type_info_image() { }
+ bool append(uint fieldnr, const Column_definition &def)
+ {
+ BinaryStringBuffer<64> type_info;
+ if (def.type_handler()->
+ Column_definition_data_type_info_image(&type_info, def) ||
+ type_info.length() > 0xFFFF/*Some reasonable limit*/)
+ return true; // Error
+ if (!type_info.length())
+ return false;
+ size_t need_length= store_length_required_length(fieldnr) +
+ store_length_required_length(type_info.length()) +
+ type_info.length();
+ if (reserve(need_length))
+ return true; // Error
+ uchar *pos= (uchar *) end();
+ pos= store_length(pos, fieldnr);
+ pos= store_string(pos, &type_info);
+ size_t new_length= (const char *) pos - ptr();
+ DBUG_ASSERT(new_length < alloced_length());
+ length((uint32) new_length);
+ return false;
+ }
+ bool append(List<Create_field> &fields)
+ {
+ uint fieldnr= 0;
+ Create_field *field;
+ List_iterator<Create_field> it(fields);
+ for (field= it++; field; field= it++, fieldnr++)
+ {
+ if (append(fieldnr, *field))
+ return true; // Error
+ }
+ return false;
+ }
+};
+
+
/**
Create a frm (table definition) file
@@ -191,6 +266,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
+ extra2_str_size(create_info->period_info.constr->name.length)
+ 2 * frm_fieldno_size
: 0;
+ size_t without_overlaps_len= frm_keyno_size * (create_info->period_info.unique_keys + 1);
uint e_unique_hash_extra_parts= 0;
uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE];
const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0);
@@ -198,6 +274,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
uchar *frm_ptr, *pos;
LEX_CUSTRING frm= {0,0};
StringBuffer<MAX_FIELD_WIDTH> vcols;
+ Field_data_type_info_image field_data_type_info_image;
DBUG_ENTER("build_frm_image");
/* If fixed row records, we need one bit to check for deleted rows */
@@ -248,11 +325,25 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
options_len= engine_table_options_frm_length(create_info->option_list,
create_fields,
keys, key_info);
-#ifdef HAVE_SPATIAL
gis_extra2_len= gis_field_options_image(NULL, create_fields);
-#endif /*HAVE_SPATIAL*/
DBUG_PRINT("info", ("Options length: %u", options_len));
+ if (field_data_type_info_image.append(create_fields))
+ {
+ my_printf_error(ER_CANT_CREATE_TABLE,
+ "Cannot create table %`s: "
+ "Building the field data type info image failed.",
+ MYF(0), table.str);
+ DBUG_RETURN(frm);
+ }
+ DBUG_PRINT("info", ("Field data type info length: %u",
+ (uint) field_data_type_info_image.length()));
+ DBUG_EXECUTE_IF("frm_data_type_info",
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_UNKNOWN_ERROR,
+ "build_frm_image: Field data type info length: %u",
+ (uint) field_data_type_info_image.length()););
+
if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN,
ER_TOO_LONG_TABLE_COMMENT, table.str))
DBUG_RETURN(frm);
@@ -298,6 +389,9 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
if (gis_extra2_len)
extra2_size+= 1 + extra2_str_size(gis_extra2_len);
+ if (field_data_type_info_image.length())
+ extra2_size+= 1 + extra2_str_size(field_data_type_info_image.length());
+
if (create_info->versioned())
{
extra2_size+= 1 + extra2_str_size(2 * frm_fieldno_size);
@@ -305,7 +399,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
if (create_info->period_info.name)
{
- extra2_size+= 1 + extra2_str_size(period_info_len);
+ extra2_size+= 2 + extra2_str_size(period_info_len)
+ + extra2_str_size(without_overlaps_len);
}
bool has_extra2_field_flags_= has_extra2_field_flags(create_fields);
@@ -340,8 +435,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
DBUG_RETURN(frm);
}
- frm_ptr= (uchar*) my_malloc(frm.length, MYF(MY_WME | MY_ZEROFILL |
- MY_THREAD_SPECIFIC));
+ frm_ptr= (uchar*) my_malloc(PSI_INSTRUMENT_ME, frm.length,
+ MYF(MY_WME | MY_ZEROFILL | MY_THREAD_SPECIFIC));
if (!frm_ptr)
DBUG_RETURN(frm);
@@ -363,14 +458,28 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
create_fields, keys, key_info);
}
-#ifdef HAVE_SPATIAL
if (gis_extra2_len)
{
*pos= EXTRA2_GIS;
pos= extra2_write_len(pos+1, gis_extra2_len);
pos+= gis_field_options_image(pos, create_fields);
}
-#endif /*HAVE_SPATIAL*/
+
+ if (field_data_type_info_image.length())
+ {
+ if (field_data_type_info_image.length() > 0xFFFF)
+ {
+ my_printf_error(ER_CANT_CREATE_TABLE,
+ "Cannot create table %`s: "
+ "field data type info image is too large. "
+ "Decrease the number of columns with "
+ "extended data types.",
+ MYF(0), table.str);
+ goto err;
+ }
+ *pos= EXTRA2_FIELD_DATA_TYPE_INFO;
+ pos= extra2_write_str(pos + 1, &field_data_type_info_image);
+ }
// PERIOD
if (create_info->period_info.is_set())
@@ -386,6 +495,19 @@ LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING &table,
store_frm_fieldno(pos, get_fieldno_by_name(create_info, create_fields,
create_info->period_info.period.end));
pos+= frm_fieldno_size;
+
+ *pos++= EXTRA2_PERIOD_WITHOUT_OVERLAPS;
+ pos= extra2_write_len(pos, without_overlaps_len);
+ store_frm_keyno(pos, create_info->period_info.unique_keys);
+ pos+= frm_keyno_size;
+ for (uint key= 0; key < keys; key++)
+ {
+ if (key_info[key].without_overlaps)
+ {
+ store_frm_keyno(pos, key);
+ pos+= frm_keyno_size;
+ }
+ }
}
if (create_info->versioned())
@@ -742,6 +864,7 @@ static bool pack_header(THD *thd, uchar *forminfo,
if (field->charset->mbminlen > 1)
{
+ TYPELIB *tmpint;
/*
Escape UCS2 intervals using HEX notation to avoid
problems with delimiters between enum elements.
@@ -750,16 +873,17 @@ static bool pack_header(THD *thd, uchar *forminfo,
filled with default values it is saved in save_interval
The HEX representation is created from this copy.
*/
+ uint count= field->interval->count;
field->save_interval= field->interval;
- field->interval= (TYPELIB*) thd->alloc(sizeof(TYPELIB));
- *field->interval= *field->save_interval;
- field->interval->type_names=
- (const char **) thd->alloc(sizeof(char*) *
- (field->interval->count+1));
- field->interval->type_names[field->interval->count]= 0;
- field->interval->type_lengths=
- (uint *) thd->alloc(sizeof(uint) * field->interval->count);
-
+ field->interval= tmpint= (TYPELIB*) thd->alloc(sizeof(TYPELIB));
+ *tmpint= *field->save_interval;
+ tmpint->type_names=
+ (const char **) thd->alloc(sizeof(char*) *
+ (count + 1));
+ tmpint->type_lengths= (uint *) thd->alloc(sizeof(uint) * (count + 1));
+ tmpint->type_names[count]= 0;
+ tmpint->type_lengths[count]= 0;
+
for (uint pos= 0; pos < field->interval->count; pos++)
{
char *dst;
@@ -767,9 +891,8 @@ static bool pack_header(THD *thd, uchar *forminfo,
size_t hex_length;
length= field->save_interval->type_lengths[pos];
hex_length= length * 2;
- field->interval->type_lengths[pos]= (uint)hex_length;
- field->interval->type_names[pos]= dst=
- (char*) thd->alloc(hex_length + 1);
+ tmpint->type_lengths[pos]= (uint) hex_length;
+ tmpint->type_names[pos]= dst= (char*) thd->alloc(hex_length + 1);
octet2hex(dst, src, length);
}
}
@@ -832,7 +955,7 @@ static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
{
List_iterator<Create_field> it(create_fields);
Create_field *field;
- TYPELIB *interval=last_field->interval;
+ const TYPELIB *interval= last_field->interval;
while ((field=it++) != last_field)
{
diff --git a/sql/unireg.h b/sql/unireg.h
index 8e9fa27ea6a..873d6f681fc 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -176,7 +176,9 @@ enum extra2_frm_value_type {
#define EXTRA2_ENGINE_IMPORTANT 128
EXTRA2_ENGINE_TABLEOPTS=128,
- EXTRA2_FIELD_FLAGS=129
+ EXTRA2_FIELD_FLAGS=129,
+ EXTRA2_FIELD_DATA_TYPE_INFO=130,
+ EXTRA2_PERIOD_WITHOUT_OVERLAPS=131,
};
enum extra2_field_flags {
diff --git a/sql/upgrade_conf_file.cc b/sql/upgrade_conf_file.cc
index 4e167f0263f..a08fd087172 100644
--- a/sql/upgrade_conf_file.cc
+++ b/sql/upgrade_conf_file.cc
@@ -54,6 +54,7 @@ static const char *removed_variables[] =
"innodb_buffer_pool_shm_checksum",
"innodb_buffer_pool_shm_key",
"innodb_checkpoint_age_target",
+"innodb_checksums",
"innodb_cleaner_eviction_factor",
"innodb_cleaner_flush_chunk_size",
"innodb_cleaner_free_list_lwm",
@@ -81,11 +82,13 @@ static const char *removed_variables[] =
"innodb_large_prefix",
"innodb_lazy_drop_table",
"innodb_locking_fake_changes",
+"innodb_locks_unsafe_for_binlog",
"innodb_log_arch_dir",
"innodb_log_arch_expire_sec",
"innodb_log_archive",
"innodb_log_block_size",
"innodb_log_checksum_algorithm",
+"innodb_rollback_segments",
"innodb_max_bitmap_file_size",
"innodb_max_changed_pages",
"innodb_merge_sort_block_size",
@@ -101,6 +104,7 @@ static const char *removed_variables[] =
"innodb_show_locks_held",
"innodb_show_verbose_locks",
"innodb_stats_auto_update",
+"innodb_stats_sample_pages",
"innodb_stats_update_need_lock",
"innodb_support_xa",
"innodb_thread_concurrency_timer_based",
@@ -115,10 +119,15 @@ static const char *removed_variables[] =
"innodb_use_trim",
"log",
"log_slow_queries",
+"max_long_data_size",
+"multi_range_count",
"rpl_recovery_rank",
+"skip_bdb",
"sql_big_tables",
"sql_low_priority_updates",
-"sql_max_join_size"
+"sql_max_join_size",
+"thread_concurrency",
+"timed_mutexes"
};
diff --git a/sql/vers_string.h b/sql/vers_string.h
index 2349cc0cac1..4e173f86e6e 100644
--- a/sql/vers_string.h
+++ b/sql/vers_string.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2018, MariaDB Corporation.
+ Copyright (c) 2018, 2020, MariaDB Corporation.
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,6 +17,8 @@
#ifndef VERS_STRING_INCLUDED
#define VERS_STRING_INCLUDED
+#include "lex_string.h"
+
/*
LEX_CSTRING with comparison semantics.
*/
@@ -28,9 +30,8 @@ struct Compare_table_names
{
DBUG_ASSERT(a.str[a.length] == 0);
DBUG_ASSERT(b.str[b.length] == 0);
- return my_strnncoll(table_alias_charset,
- (uchar*)a.str, a.length,
- (uchar*)b.str, b.length);
+ return table_alias_charset->strnncoll(a.str, a.length,
+ b.str, b.length);
}
};
@@ -45,31 +46,6 @@ struct Compare_identifiers
}
};
-class Lex_cstring : public LEX_CSTRING
-{
- public:
- Lex_cstring()
- {
- str= NULL;
- length= 0;
- }
- Lex_cstring(const char *_str, size_t _len)
- {
- str= _str;
- length= _len;
- }
- Lex_cstring(const char *start, const char *end)
- {
- DBUG_ASSERT(start <= end);
- str= start;
- length= end - start;
- }
- void set(const char *_str, size_t _len)
- {
- str= _str;
- length= _len;
- }
-};
template <class Compare>
struct Lex_cstring_with_compare : public Lex_cstring
diff --git a/sql/vers_utils.h b/sql/vers_utils.h
deleted file mode 100644
index 2bea191da9e..00000000000
--- a/sql/vers_utils.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef VERS_UTILS_INCLUDED
-#define VERS_UTILS_INCLUDED
-
-#include "table.h"
-#include "sql_class.h"
-#include "vers_string.h"
-
-#endif // VERS_UTILS_INCLUDED
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index fd51dbf9439..4005de22e72 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013-2015 Codership Oy <info@codership.com>
+/* Copyright (C) 2013-2019 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
@@ -24,7 +24,6 @@
#include "wsrep_trans_observer.h"
#include "slave.h" // opt_log_slave_updates
-#include "log_event.h" // class THD, EVENT_LEN_OFFSET, etc.
#include "debug_sync.h"
/*
@@ -60,7 +59,6 @@ static Log_event* wsrep_read_log_event(
}
#include "transaction.h" // trans_commit(), trans_rollback()
-#include "rpl_rli.h" // class Relay_log_info;
void wsrep_set_apply_format(THD* thd, Format_description_log_event* ev)
{
@@ -84,7 +82,7 @@ wsrep_get_apply_format(THD* thd)
return thd->wsrep_rgi->rli->relay_log.description_event_for_exec;
}
-void wsrep_apply_error::store(const THD* const thd)
+void wsrep_store_error(const THD* const thd, wsrep::mutable_buffer& dst)
{
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
@@ -92,27 +90,10 @@ void wsrep_apply_error::store(const THD* const thd)
static size_t const max_len= 2*MAX_SLAVE_ERRMSG; // 2x so that we have enough
- if (NULL == str_)
- {
- // this must be freeable by standard free()
- str_= static_cast<char*>(malloc(max_len));
- if (NULL == str_)
- {
- WSREP_ERROR("Failed to allocate %zu bytes for error buffer.", max_len);
- len_= 0;
- return;
- }
- }
- else
- {
- /* This is possible when we invoke rollback after failed applying.
- * In this situation DA should not be reset yet and should contain
- * all previous errors from applying and new ones from rollbacking,
- * so we just overwrite is from scratch */
- }
+ dst.resize(max_len);
- char* slider= str_;
- const char* const buf_end= str_ + max_len - 1; // -1: leave space for \0
+ char* slider= dst.data();
+ const char* const buf_end= slider + max_len - 1; // -1: leave space for \0
for (cond= it++; cond && slider < buf_end; cond= it++)
{
@@ -123,12 +104,17 @@ void wsrep_apply_error::store(const THD* const thd)
err_str, err_code);
}
- *slider= '\0';
- len_= slider - str_ + 1; // +1: add \0
+ if (slider != dst.data())
+ {
+ *slider= '\0';
+ slider++;
+ }
+
+ dst.resize(slider - dst.data());
- WSREP_DEBUG("Error buffer for thd %llu seqno %lld, %zu bytes: %s",
+ WSREP_DEBUG("Error buffer for thd %llu seqno %lld, %zu bytes: '%s'",
thd->thread_id, (long long)wsrep_thd_trx_seqno(thd),
- len_, str_ ? str_ : "(null)");
+ dst.size(), dst.size() ? dst.data() : "(null)");
}
int wsrep_apply_events(THD* thd,
@@ -145,6 +131,12 @@ int wsrep_apply_events(THD* thd,
if (!buf_len) WSREP_DEBUG("empty rbr buffer to apply: %lld",
(long long) wsrep_thd_trx_seqno(thd));
+ thd->variables.gtid_seq_no= 0;
+ if (wsrep_gtid_mode)
+ thd->variables.gtid_domain_id= wsrep_gtid_server.domain_id;
+ else
+ thd->variables.gtid_domain_id= global_system_variables.gtid_domain_id;
+
while (buf_len)
{
int exec_res;
@@ -164,26 +156,42 @@ int wsrep_apply_events(THD* thd,
case FORMAT_DESCRIPTION_EVENT:
wsrep_set_apply_format(thd, (Format_description_log_event*)ev);
continue;
-#ifdef GTID_SUPPORT
- case GTID_LOG_EVENT:
- {
- Gtid_log_event* gev= (Gtid_log_event*)ev;
- if (gev->get_gno() == 0)
+ case GTID_EVENT:
{
- /* Skip GTID log event to make binlog to generate LTID on commit */
+ Gtid_log_event *gtid_ev= (Gtid_log_event*)ev;
+ thd->variables.server_id= gtid_ev->server_id;
+ thd->variables.gtid_domain_id= gtid_ev->domain_id;
+ if ((gtid_ev->server_id == wsrep_gtid_server.server_id) &&
+ (gtid_ev->domain_id == wsrep_gtid_server.domain_id))
+ {
+ thd->variables.wsrep_gtid_seq_no= gtid_ev->seq_no;
+ }
+ else
+ {
+ thd->variables.gtid_seq_no= gtid_ev->seq_no;
+ }
delete ev;
- continue;
}
- }
-#endif /* GTID_SUPPORT */
+ continue;
default:
break;
}
+
+ if (!thd->variables.gtid_seq_no && wsrep_thd_is_toi(thd) &&
+ (ev->get_type_code() == QUERY_EVENT))
+ {
+ uint64 seqno= wsrep_gtid_server.seqno_inc();
+ thd->wsrep_current_gtid_seqno= seqno;
+ if (mysql_bin_log.is_open() && wsrep_gtid_mode)
+ {
+ thd->variables.gtid_seq_no= seqno;
+ }
+ }
/* Use the original server id for logging. */
thd->set_server_id(ev->server_id);
thd->set_time(); // time the query
- thd->transaction.start_time.reset(thd);
+ thd->transaction->start_time.reset(thd);
thd->lex->current_select= 0;
if (!ev->when)
{
diff --git a/sql/wsrep_applier.h b/sql/wsrep_applier.h
index 70361987cc7..fefca306a70 100644
--- a/sql/wsrep_applier.h
+++ b/sql/wsrep_applier.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2015 Codership Oy <http://www.codership.com>
+/* Copyright 2013-2019 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
@@ -16,16 +16,15 @@
#ifndef WSREP_APPLIER_H
#define WSREP_APPLIER_H
-#include <my_config.h>
-
#include "sql_class.h" // THD class
+#include "rpl_rli.h" // Relay_log_info
+#include "log_event.h" // Format_description_log_event
int wsrep_apply_events(THD* thd,
Relay_log_info* rli,
const void* events_buf,
size_t buf_len);
-
/* Applier error codes, when nothing better is available. */
#define WSREP_RET_SUCCESS 0 // Success
#define WSREP_ERR_GENERIC 1 // When in doubt (MySQL default error code)
@@ -36,38 +35,10 @@ int wsrep_apply_events(THD* thd,
#define WSREP_ERR_FAILED 6 // Operation failed for some internal reason
#define WSREP_ERR_ABORTED 7 // Operation was aborted externally
-class wsrep_apply_error
-{
-public:
- wsrep_apply_error() : str_(NULL), len_(0) {};
- ~wsrep_apply_error() { ::free(str_); }
- /* stores the current THD error info from the diagnostic area. Works only
- * once, subsequent invocations are ignored in order to preserve the original
- * condition. */
- void store(const THD* thd);
- const char* c_str() const { return str_; }
- size_t length() const { return len_; }
- bool is_null() const { return (c_str() == NULL && length() == 0); }
- wsrep_buf_t get_buf() const
- {
- wsrep_buf_t ret= { c_str(), length() };
- return ret;
- }
-private:
- char* str_;
- size_t len_;
-};
+void wsrep_store_error(const THD* thd, wsrep::mutable_buffer& buf);
class Format_description_log_event;
void wsrep_set_apply_format(THD*, Format_description_log_event*);
Format_description_log_event* wsrep_get_apply_format(THD* thd);
-int wsrep_apply(void* ctx,
- uint32_t flags,
- const wsrep_buf_t* buf,
- const wsrep_trx_meta_t* meta,
- wsrep_apply_error& err);
-
-wsrep_cb_status_t wsrep_unordered_cb(void* ctx,
- const wsrep_buf_t* data);
#endif /* WSREP_APPLIER_H */
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 787ebc042ae..da899321ba8 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -64,7 +64,7 @@ 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,
+ uchar* tmp= (uchar *)my_realloc(PSI_INSTRUMENT_ME, *buf, total_length,
MYF(MY_ALLOW_ZERO_PTR));
if (!tmp)
{
@@ -381,7 +381,9 @@ void wsrep_register_for_group_commit(THD *thd)
void wsrep_unregister_from_group_commit(THD *thd)
{
- DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_ordered_commit);
+ DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_ordered_commit||
+ // ordered_commit() failure results in s_aborting state
+ thd->wsrep_trx().state() == wsrep::transaction::s_aborting);
wait_for_commit *wfc= thd->wait_for_commit_ptr;
if (wfc)
diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc
index 20bb7748a95..b9c59623e6c 100644
--- a/sql/wsrep_client_service.cc
+++ b/sql/wsrep_client_service.cc
@@ -15,7 +15,6 @@
#include "wsrep_client_service.h"
#include "wsrep_high_priority_service.h"
-#include "wsrep_applier.h" /* wsrep_apply_events() */
#include "wsrep_binlog.h" /* wsrep_dump_rbr_buf() */
#include "wsrep_schema.h" /* remove_fragments() */
#include "wsrep_thd.h"
@@ -321,7 +320,8 @@ int Wsrep_client_service::bf_rollback()
int ret= (trans_rollback_stmt(m_thd) || trans_rollback(m_thd));
if (m_thd->locked_tables_mode && m_thd->lock)
{
- m_thd->locked_tables_list.unlock_locked_tables(m_thd);
+ if (m_thd->locked_tables_list.unlock_locked_tables(m_thd))
+ ret= 1;
m_thd->variables.option_bits&= ~OPTION_TABLE_LOCK;
}
if (m_thd->global_read_lock.is_acquired())
diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc
index c4dbd8c450f..8ea9ca79697 100644
--- a/sql/wsrep_dummy.cc
+++ b/sql/wsrep_dummy.cc
@@ -143,6 +143,13 @@ void wsrep_log(void (*)(const char *, ...), const char *, ...)
my_bool wsrep_thd_is_applying(const THD*)
{ return 0;}
+my_bool wsrep_thd_has_ignored_error(const THD*)
+{ return 0;}
+
+void wsrep_thd_set_ignored_error(THD*, my_bool)
+{ }
+ulong wsrep_OSU_method_get(const THD*)
+{ return 0;}
bool wsrep_thd_set_wsrep_aborter(THD*, THD*)
{ return 0;}
diff --git a/sql/wsrep_high_priority_service.cc b/sql/wsrep_high_priority_service.cc
index 0aeedbd7516..5961a9574eb 100644
--- a/sql/wsrep_high_priority_service.cc
+++ b/sql/wsrep_high_priority_service.cc
@@ -119,6 +119,23 @@ static void wsrep_setup_uk_and_fk_checks(THD* thd)
thd->variables.option_bits&= ~OPTION_NO_FOREIGN_KEY_CHECKS;
}
+static int apply_events(THD* thd,
+ Relay_log_info* rli,
+ const wsrep::const_buffer& data,
+ wsrep::mutable_buffer& err)
+{
+ int const ret= wsrep_apply_events(thd, rli, data.data(), data.size());
+ if (ret || wsrep_thd_has_ignored_error(thd))
+ {
+ if (ret)
+ {
+ wsrep_store_error(thd, err);
+ }
+ wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
+ }
+ return ret;
+}
+
/****************************************************************************
High priority service
*****************************************************************************/
@@ -254,8 +271,8 @@ int Wsrep_high_priority_service::append_fragment_and_commit(
common utility function to deal with commit.
*/
const bool do_binlog_commit= (opt_log_slave_updates &&
- wsrep_gtid_mode &&
- m_thd->variables.gtid_seq_no);
+ wsrep_gtid_mode &&
+ m_thd->variables.gtid_seq_no);
/*
Write skip event into binlog if gtid_mode is on. This is to
maintain gtid continuity.
@@ -272,8 +289,7 @@ int Wsrep_high_priority_service::append_fragment_and_commit(
}
ret= ret || trans_commit(m_thd);
-
- m_thd->wsrep_cs().after_applying();
+ ret= ret || (m_thd->wsrep_cs().after_applying(), 0);
m_thd->mdl_context.release_transactional_locks();
free_root(m_thd->mem_root, MYF(MY_KEEP_PREALLOC));
@@ -305,7 +321,7 @@ int Wsrep_high_priority_service::commit(const wsrep::ws_handle& ws_handle,
const bool is_ordered= !ws_meta.seqno().is_undefined();
- if (!thd->transaction.stmt.is_empty())
+ if (!thd->transaction->stmt.is_empty())
ret= trans_commit_stmt(thd);
if (ret == 0)
@@ -352,7 +368,15 @@ int Wsrep_high_priority_service::rollback(const wsrep::ws_handle& ws_handle,
const wsrep::ws_meta& ws_meta)
{
DBUG_ENTER("Wsrep_high_priority_service::rollback");
- m_thd->wsrep_cs().prepare_for_ordering(ws_handle, ws_meta, false);
+ if (ws_meta.ordered())
+ {
+ m_thd->wsrep_cs().prepare_for_ordering(ws_handle, ws_meta, false);
+ }
+ else
+ {
+ assert(ws_meta == wsrep::ws_meta());
+ assert(ws_handle == wsrep::ws_handle());
+ }
int ret= (trans_rollback_stmt(m_thd) || trans_rollback(m_thd));
m_thd->mdl_context.release_transactional_locks();
m_thd->mdl_context.release_explicit_locks();
@@ -364,7 +388,7 @@ int Wsrep_high_priority_service::rollback(const wsrep::ws_handle& ws_handle,
int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta,
const wsrep::const_buffer& data,
- wsrep::mutable_buffer&)
+ wsrep::mutable_buffer& err)
{
DBUG_ENTER("Wsrep_high_priority_service::apply_toi");
THD* thd= m_thd;
@@ -378,19 +402,15 @@ int Wsrep_high_priority_service::apply_toi(const wsrep::ws_meta& ws_meta,
WSREP_DEBUG("Wsrep_high_priority_service::apply_toi: %lld",
client_state.toi_meta().seqno().get());
- int ret= wsrep_apply_events(thd, m_rli, data.data(), data.size());
- if (ret != 0 || thd->wsrep_has_ignored_error)
- {
- wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
- thd->wsrep_has_ignored_error= false;
- /* todo: error voting */
- }
+ int ret= apply_events(thd, m_rli, data, err);
+ wsrep_thd_set_ignored_error(thd, false);
trans_commit(thd);
thd->close_temporary_tables();
thd->lex->sql_command= SQLCOM_END;
- wsrep_set_SE_checkpoint(client_state.toi_meta().gtid());
+ wsrep_gtid_server.signal_waiters(thd->wsrep_current_gtid_seqno, false);
+ wsrep_set_SE_checkpoint(client_state.toi_meta().gtid(), wsrep_gtid_server.gtid());
must_exit_= check_exit_status();
@@ -442,13 +462,18 @@ int Wsrep_high_priority_service::log_dummy_write_set(const wsrep::ws_handle& ws_
cs.before_rollback();
cs.after_rollback();
}
- wsrep_set_SE_checkpoint(ws_meta.gtid());
+ wsrep_set_SE_checkpoint(ws_meta.gtid(), wsrep_gtid_server.gtid());
ret= ret || cs.provider().commit_order_leave(ws_handle, ws_meta, err);
cs.after_applying();
}
DBUG_RETURN(ret);
}
+void Wsrep_high_priority_service::adopt_apply_error(wsrep::mutable_buffer& err)
+{
+ m_thd->wsrep_cs().adopt_apply_error(err);
+}
+
void Wsrep_high_priority_service::debug_crash(const char* crash_point)
{
DBUG_ASSERT(m_thd == current_thd);
@@ -484,7 +509,7 @@ Wsrep_applier_service::~Wsrep_applier_service()
int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
const wsrep::const_buffer& data,
- wsrep::mutable_buffer&)
+ wsrep::mutable_buffer& err)
{
DBUG_ENTER("Wsrep_applier_service::apply_write_set");
THD* thd= m_thd;
@@ -495,10 +520,12 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
DBUG_ASSERT(thd->wsrep_trx().state() == wsrep::transaction::s_executing);
thd_proc_info(thd, "applying write set");
+
/* moved dbug sync point here, after possible THD switch for SR transactions
has ben done
*/
/* Allow tests to block the applier thread using the DBUG facilities */
+#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("sync.wsrep_apply_cb",
{
const char act[]=
@@ -508,15 +535,10 @@ int Wsrep_applier_service::apply_write_set(const wsrep::ws_meta& ws_meta,
DBUG_ASSERT(!debug_sync_set_action(thd,
STRING_WITH_LEN(act)));
};);
+#endif /* ENABLED_DEBUG_SYNC */
wsrep_setup_uk_and_fk_checks(thd);
-
- int ret= wsrep_apply_events(thd, m_rli, data.data(), data.size());
-
- if (ret || thd->wsrep_has_ignored_error)
- {
- wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
- }
+ int ret= apply_events(thd, m_rli, data, err);
thd->close_temporary_tables();
if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
@@ -648,7 +670,7 @@ Wsrep_replayer_service::~Wsrep_replayer_service()
int Wsrep_replayer_service::apply_write_set(const wsrep::ws_meta& ws_meta,
const wsrep::const_buffer& data,
- wsrep::mutable_buffer&)
+ wsrep::mutable_buffer& err)
{
DBUG_ENTER("Wsrep_replayer_service::apply_write_set");
THD* thd= m_thd;
@@ -667,14 +689,7 @@ int Wsrep_replayer_service::apply_write_set(const wsrep::ws_meta& ws_meta,
ws_meta,
thd->wsrep_sr().fragments());
}
-
- ret= ret || wsrep_apply_events(thd, m_rli, data.data(), data.size());
-
- if (ret || thd->wsrep_has_ignored_error)
- {
- wsrep_dump_rbr_buf_with_header(thd, data.data(), data.size());
- }
-
+ ret= ret || apply_events(thd, m_rli, data, err);
thd->close_temporary_tables();
if (!ret && !(ws_meta.flags() & wsrep::provider::flag::commit))
{
diff --git a/sql/wsrep_high_priority_service.h b/sql/wsrep_high_priority_service.h
index 2c61f0126af..c275c352de2 100644
--- a/sql/wsrep_high_priority_service.h
+++ b/sql/wsrep_high_priority_service.h
@@ -17,7 +17,6 @@
#define WSREP_HIGH_PRIORITY_SERVICE_H
#include "wsrep/high_priority_service.hpp"
-#include "wsrep/client_state.hpp"
#include "my_global.h"
#include "sql_error.h" /* Diagnostics area */
#include "sql_class.h" /* rpl_group_info */
@@ -55,7 +54,7 @@ public:
int log_dummy_write_set(const wsrep::ws_handle&,
const wsrep::ws_meta&,
wsrep::mutable_buffer&);
- void adopt_apply_error(wsrep::mutable_buffer& err) {}
+ void adopt_apply_error(wsrep::mutable_buffer&);
virtual bool check_exit_status() const = 0;
void debug_crash(const char*);
@@ -76,7 +75,7 @@ protected:
my_hrtime_t user_time;
longlong row_count_func;
bool wsrep_applier;
-} m_shadow;
+ } m_shadow;
};
class Wsrep_applier_service : public Wsrep_high_priority_service
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index aaf17012118..a6b0991e82b 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -1,4 +1,5 @@
/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
+ Copyright (c) 2020, 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
@@ -46,17 +47,15 @@
#include <cstdlib>
#include <string>
#include "log_event.h"
+#include "sql_connect.h"
+#include "thread_cache.h"
#include <sstream>
/* wsrep-lib */
Wsrep_server_state* Wsrep_server_state::m_instance;
-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_emulate_bin_log= FALSE; // activating parts of binlog interface
my_bool wsrep_preordered_opt= FALSE;
/* Streaming Replication */
@@ -68,8 +67,6 @@ const char *wsrep_SR_store_types[]= { "none", "table", NullS };
*/
extern my_bool plugins_are_initialized;
-extern uint kill_cached_threads;
-extern mysql_cond_t COND_thread_cache;
/* System variables. */
const char *wsrep_provider;
@@ -100,16 +97,19 @@ my_bool wsrep_restart_slave; // Should mysql slave thread be
// restarted, when node joins back?
my_bool wsrep_desync; // De(re)synchronize the node from the
// cluster
+my_bool wsrep_strict_ddl; // Reject DDL to
+ // effected tables not
+ // supporting Galera replication
+bool wsrep_service_started; // If Galera was initialized
long wsrep_slave_threads; // No. of slave appliers threads
ulong wsrep_retry_autocommit; // Retry aborted autocommit trx
ulong wsrep_max_ws_size; // Max allowed ws (RBR buffer) size
ulong wsrep_max_ws_rows; // Max number of rows in ws
ulong wsrep_forced_binlog_format;
ulong wsrep_mysql_replication_bundle;
-bool wsrep_gtid_mode; // Use wsrep_gtid_domain_id
- // for galera transactions?
-uint32 wsrep_gtid_domain_id; // gtid_domain_id for galera
- // transactions
+
+bool wsrep_gtid_mode; // Enable WSREP native GTID support
+Wsrep_gtid_server wsrep_gtid_server;
/* Other configuration variables and their default values. */
my_bool wsrep_incremental_data_collection= 0; // Incremental data collection
@@ -144,6 +144,7 @@ mysql_mutex_t LOCK_wsrep_replaying;
mysql_cond_t COND_wsrep_replaying;
mysql_mutex_t LOCK_wsrep_slave_threads;
mysql_cond_t COND_wsrep_slave_threads;
+mysql_mutex_t LOCK_wsrep_gtid_wait_upto;
mysql_mutex_t LOCK_wsrep_cluster_config;
mysql_mutex_t LOCK_wsrep_desync;
mysql_mutex_t LOCK_wsrep_config_state;
@@ -167,7 +168,8 @@ ulong my_bind_addr;
PSI_mutex_key
key_LOCK_wsrep_replaying, key_LOCK_wsrep_ready, key_LOCK_wsrep_sst,
key_LOCK_wsrep_sst_thread, key_LOCK_wsrep_sst_init,
- key_LOCK_wsrep_slave_threads, key_LOCK_wsrep_desync,
+ key_LOCK_wsrep_slave_threads, key_LOCK_wsrep_gtid_wait_upto,
+ key_LOCK_wsrep_desync,
key_LOCK_wsrep_config_state, key_LOCK_wsrep_cluster_config,
key_LOCK_wsrep_group_commit,
key_LOCK_wsrep_SR_pool,
@@ -179,7 +181,7 @@ PSI_mutex_key
PSI_cond_key key_COND_wsrep_thd,
key_COND_wsrep_replaying, key_COND_wsrep_ready, key_COND_wsrep_sst,
key_COND_wsrep_sst_init, key_COND_wsrep_sst_thread,
- key_COND_wsrep_thd_queue, key_COND_wsrep_slave_threads,
+ key_COND_wsrep_thd_queue, key_COND_wsrep_slave_threads, key_COND_wsrep_gtid_wait_upto,
key_COND_wsrep_joiner_monitor, key_COND_wsrep_donor_monitor;
PSI_file_key key_file_wsrep_gra_log;
@@ -193,6 +195,7 @@ static PSI_mutex_info wsrep_mutexes[]=
{ &key_LOCK_wsrep_sst, "LOCK_wsrep_sst", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_replaying, "LOCK_wsrep_replaying", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_slave_threads, "LOCK_wsrep_slave_threads", PSI_FLAG_GLOBAL},
+ { &key_LOCK_wsrep_gtid_wait_upto, "LOCK_wsrep_gtid_wait_upto", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_cluster_config, "LOCK_wsrep_cluster_config", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_desync, "LOCK_wsrep_desync", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_config_state, "LOCK_wsrep_config_state", PSI_FLAG_GLOBAL},
@@ -212,6 +215,7 @@ static PSI_cond_info wsrep_conds[]=
{ &key_COND_wsrep_thd, "THD::COND_wsrep_thd", 0},
{ &key_COND_wsrep_replaying, "COND_wsrep_replaying", PSI_FLAG_GLOBAL},
{ &key_COND_wsrep_slave_threads, "COND_wsrep_wsrep_slave_threads", PSI_FLAG_GLOBAL},
+ { &key_COND_wsrep_gtid_wait_upto, "COND_wsrep_gtid_wait_upto", PSI_FLAG_GLOBAL},
{ &key_COND_wsrep_joiner_monitor, "COND_wsrep_joiner_monitor", PSI_FLAG_GLOBAL},
{ &key_COND_wsrep_donor_monitor, "COND_wsrep_donor_monitor", PSI_FLAG_GLOBAL}
};
@@ -288,7 +292,7 @@ void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...)
else
{
size_t dynbuf_size= std::max(n, 4096);
- char* dynbuf= (char*) my_malloc(dynbuf_size, MYF(0));
+ char* dynbuf= (char*) my_malloc(PSI_NOT_INSTRUMENTED, dynbuf_size, MYF(0));
if (dynbuf)
{
va_start(arglist, fmt);
@@ -331,7 +335,7 @@ static void wsrep_log_cb(wsrep::log::level level, const char *msg)
sql_print_warning("WSREP: %s", msg);
break;
case wsrep::log::error:
- sql_print_error("WSREP: %s", msg);
+ sql_print_error("WSREP: %s", msg);
break;
case wsrep::log::debug:
if (wsrep_debug) sql_print_information ("[Debug] WSREP: %s", msg);
@@ -341,6 +345,58 @@ static void wsrep_log_cb(wsrep::log::level level, const char *msg)
}
}
+void wsrep_init_gtid()
+{
+ wsrep_server_gtid_t stored_gtid= wsrep_get_SE_checkpoint<wsrep_server_gtid_t>();
+ if (stored_gtid.server_id == 0)
+ {
+ rpl_gtid wsrep_last_gtid;
+ stored_gtid.domain_id= wsrep_gtid_server.domain_id;
+ if (mysql_bin_log.is_open() &&
+ mysql_bin_log.lookup_domain_in_binlog_state(stored_gtid.domain_id,
+ &wsrep_last_gtid))
+ {
+ stored_gtid.server_id= wsrep_last_gtid.server_id;
+ stored_gtid.seqno= wsrep_last_gtid.seq_no;
+ }
+ else
+ {
+ stored_gtid.server_id= global_system_variables.server_id;
+ stored_gtid.seqno= 0;
+ }
+ }
+ wsrep_gtid_server.gtid(stored_gtid);
+}
+
+bool wsrep_get_binlog_gtid_seqno(wsrep_server_gtid_t& gtid)
+{
+ rpl_gtid binlog_gtid;
+ int ret= 0;
+ if (mysql_bin_log.is_open() &&
+ mysql_bin_log.find_in_binlog_state(gtid.domain_id,
+ gtid.server_id,
+ &binlog_gtid))
+ {
+ gtid.domain_id= binlog_gtid.domain_id;
+ gtid.server_id= binlog_gtid.server_id;
+ gtid.seqno= binlog_gtid.seq_no;
+ ret= 1;
+ }
+ return ret;
+}
+
+bool wsrep_check_gtid_seqno(const uint32& domain, const uint32& server,
+ uint64& seqno)
+{
+ if (domain == wsrep_gtid_server.domain_id &&
+ server == wsrep_gtid_server.server_id)
+ {
+ if (wsrep_gtid_server.seqno_committed() < seqno) return 1;
+ return 0;
+ }
+ return 0;
+}
+
void wsrep_init_sidno(const wsrep::id& uuid)
{
/*
@@ -731,6 +787,15 @@ int wsrep_init_server()
void wsrep_init_globals()
{
wsrep_init_sidno(Wsrep_server_state::instance().connected_gtid().id());
+ wsrep_init_gtid();
+ /* Recover last written wsrep gtid */
+ if (wsrep_new_cluster)
+ {
+ wsrep_server_gtid_t gtid= {wsrep_gtid_server.domain_id,
+ wsrep_gtid_server.server_id, 0};
+ wsrep_get_binlog_gtid_seqno(gtid);
+ wsrep_gtid_server.seqno(gtid.seqno);
+ }
wsrep_init_schema();
if (WSREP_ON)
@@ -768,10 +833,6 @@ int wsrep_init()
return err;
}
- global_system_variables.wsrep_on= 1;
-
- WSREP_ON_= wsrep_provider && strcmp(wsrep_provider, WSREP_NONE);
-
if (wsrep_gtid_mode && opt_bin_log && !opt_log_slave_updates)
{
WSREP_ERROR("Option --log-slave-updates is required if "
@@ -803,6 +864,11 @@ int wsrep_init()
return 1;
}
+ /* Now WSREP is fully initialized */
+ global_system_variables.wsrep_on= 1;
+ WSREP_ON_= wsrep_provider && strcmp(wsrep_provider, WSREP_NONE);
+ wsrep_service_started= 1;
+
wsrep_init_provider_status_variables();
wsrep_capabilities_export(Wsrep_server_state::instance().provider().capabilities(),
&wsrep_provider_capabilities);
@@ -835,6 +901,7 @@ void wsrep_thr_init()
mysql_cond_init(key_COND_wsrep_replaying, &COND_wsrep_replaying, NULL);
mysql_mutex_init(key_LOCK_wsrep_slave_threads, &LOCK_wsrep_slave_threads, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_wsrep_slave_threads, &COND_wsrep_slave_threads, NULL);
+ mysql_mutex_init(key_LOCK_wsrep_gtid_wait_upto, &LOCK_wsrep_gtid_wait_upto, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_wsrep_cluster_config, &LOCK_wsrep_cluster_config, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_wsrep_desync, &LOCK_wsrep_desync, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_wsrep_config_state, &LOCK_wsrep_config_state, MY_MUTEX_INIT_FAST);
@@ -945,6 +1012,7 @@ void wsrep_thr_deinit()
mysql_cond_destroy(&COND_wsrep_sst_init);
mysql_mutex_destroy(&LOCK_wsrep_replaying);
mysql_cond_destroy(&COND_wsrep_replaying);
+ mysql_mutex_destroy(&LOCK_wsrep_gtid_wait_upto);
mysql_mutex_destroy(&LOCK_wsrep_slave_threads);
mysql_cond_destroy(&COND_wsrep_slave_threads);
mysql_mutex_destroy(&LOCK_wsrep_cluster_config);
@@ -981,10 +1049,20 @@ void wsrep_recover()
uuid_str, (long long)local_seqno);
return;
}
- wsrep::gtid gtid= wsrep_get_SE_checkpoint();
+ wsrep::gtid gtid= wsrep_get_SE_checkpoint<wsrep::gtid>();
std::ostringstream oss;
oss << gtid;
- WSREP_INFO("Recovered position: %s", oss.str().c_str());
+ if (wsrep_gtid_mode)
+ {
+ wsrep_server_gtid_t server_gtid= wsrep_get_SE_checkpoint<wsrep_server_gtid_t>();
+ WSREP_INFO("Recovered position: %s,%d-%d-%llu", oss.str().c_str(), server_gtid.domain_id,
+ server_gtid.server_id, server_gtid.seqno);
+ }
+ else
+ {
+ WSREP_INFO("Recovered position: %s", oss.str().c_str());
+ }
+
}
@@ -1028,7 +1106,7 @@ void wsrep_shutdown_replication()
node_uuid= WSREP_UUID_UNDEFINED;
/* Undocking the thread specific data. */
- my_pthread_setspecific_ptr(THR_THD, NULL);
+ set_current_thd(nullptr);
}
bool wsrep_start_replication()
@@ -1054,7 +1132,6 @@ bool wsrep_start_replication()
}
bool const bootstrap(TRUE == wsrep_new_cluster);
- wsrep_new_cluster= FALSE;
WSREP_INFO("Start replication");
@@ -1088,18 +1165,21 @@ bool wsrep_start_replication()
bool wsrep_must_sync_wait (THD* thd, uint mask)
{
- bool ret;
- mysql_mutex_lock(&thd->LOCK_thd_data);
- ret= (thd->variables.wsrep_sync_wait & mask) &&
- thd->wsrep_client_thread &&
- WSREP_ON && thd->variables.wsrep_on &&
- !(thd->variables.wsrep_dirty_reads &&
- !is_update_query(thd->lex->sql_command)) &&
- !thd->in_active_multi_stmt_transaction() &&
- thd->wsrep_trx().state() !=
- wsrep::transaction::s_replaying &&
- thd->wsrep_cs().sync_wait_gtid().is_undefined();
- mysql_mutex_unlock(&thd->LOCK_thd_data);
+ bool ret= 0;
+ if (thd->variables.wsrep_on)
+ {
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ ret= (thd->variables.wsrep_sync_wait & mask) &&
+ thd->wsrep_client_thread &&
+ WSREP_ON &&
+ !(thd->variables.wsrep_dirty_reads &&
+ !is_update_query(thd->lex->sql_command)) &&
+ !thd->in_active_multi_stmt_transaction() &&
+ thd->wsrep_trx().state() !=
+ wsrep::transaction::s_replaying &&
+ thd->wsrep_cs().sync_wait_gtid().is_undefined();
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ }
return ret;
}
@@ -1238,7 +1318,7 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
wsrep_key_arr_t* ka)
{
wsrep_key_t* tmp;
- tmp= (wsrep_key_t*)my_realloc(ka->keys,
+ tmp= (wsrep_key_t*)my_realloc(PSI_INSTRUMENT_ME, ka->keys,
(ka->keys_len + 1) * sizeof(wsrep_key_t),
MYF(MY_ALLOW_ZERO_PTR));
if (!tmp)
@@ -1248,7 +1328,7 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
}
ka->keys= tmp;
if (!(ka->keys[ka->keys_len].key_parts= (wsrep_buf_t*)
- my_malloc(sizeof(wsrep_buf_t)*2, MYF(0))))
+ my_malloc(PSI_INSTRUMENT_ME, sizeof(wsrep_buf_t)*2, MYF(0))))
{
WSREP_ERROR("Can't allocate memory for key_parts");
return false;
@@ -1267,11 +1347,11 @@ static bool wsrep_prepare_key_for_isolation(const char* db,
}
static bool wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
- Alter_info* alter_info,
+ const Alter_info* alter_info,
wsrep_key_arr_t* ka)
{
Key *key;
- List_iterator<Key> key_iterator(alter_info->key_list);
+ List_iterator<Key> key_iterator(const_cast<Alter_info*>(alter_info)->key_list);
while ((key= key_iterator++))
{
if (key->type == Key::FOREIGN_KEY)
@@ -1407,12 +1487,12 @@ wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table,
wsrep::key_array
wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
- Alter_info* alter_info)
+ const Alter_info* alter_info)
{
wsrep::key_array ret;
Key *key;
- List_iterator<Key> key_iterator(alter_info->key_list);
+ List_iterator<Key> key_iterator(const_cast<Alter_info*>(alter_info)->key_list);
while ((key= key_iterator++))
{
if (key->type == Key::FOREIGN_KEY)
@@ -1434,7 +1514,7 @@ wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db,
wsrep::key_array wsrep_prepare_keys_for_toi(const char* db,
const char* table,
const TABLE_LIST* table_list,
- Alter_info* alter_info)
+ const Alter_info* alter_info)
{
wsrep::key_array ret;
if (db || table)
@@ -1487,16 +1567,36 @@ int wsrep_to_buf_helper(
if (!ret && writer.write(&gtid_ev)) ret= 1;
}
#endif /* GTID_SUPPORT */
- if (wsrep_gtid_mode && thd->variables.gtid_seq_no)
+ /*
+ * Check if this is applier thread, slave_thread or
+ * we have set manually WSREP GTID seqno. Add GTID event.
+ */
+ if (thd->slave_thread || wsrep_thd_is_applying(thd) ||
+ thd->variables.wsrep_gtid_seq_no)
{
- Gtid_log_event gtid_event(thd, thd->variables.gtid_seq_no,
- thd->variables.gtid_domain_id,
- true, LOG_EVENT_SUPPRESS_USE_F,
- true, 0);
- gtid_event.server_id= thd->variables.server_id;
+ uint64 seqno= thd->variables.gtid_seq_no;
+ uint32 domain_id= thd->variables.gtid_domain_id;
+ uint32 server_id= thd->variables.server_id;
+ if (!thd->variables.gtid_seq_no && thd->variables.wsrep_gtid_seq_no)
+ {
+ seqno= thd->variables.wsrep_gtid_seq_no;
+ domain_id= wsrep_gtid_server.domain_id;
+ server_id= wsrep_gtid_server.server_id;
+ }
+ Gtid_log_event gtid_event(thd, seqno, domain_id, true,
+ LOG_EVENT_SUPPRESS_USE_F, true, 0);
+ gtid_event.server_id= server_id;
if (!gtid_event.is_valid()) ret= 0;
ret= writer.write(&gtid_event);
}
+ /*
+ It's local DDL so in case of possible gtid seqno (SET gtid_seq_no=X)
+ manipulation, seqno value will be ignored.
+ */
+ else
+ {
+ thd->variables.gtid_seq_no= 0;
+ }
/* if there is prepare query, add event for it */
if (!ret && thd->wsrep_TOI_pre_query)
@@ -1510,6 +1610,9 @@ int wsrep_to_buf_helper(
/* continue to append the actual query */
Query_log_event ev(thd, query, query_len, FALSE, FALSE, FALSE, 0);
+ /* WSREP GTID mode, we need to change server_id */
+ if (wsrep_gtid_mode && !thd->variables.gtid_seq_no)
+ ev.server_id= wsrep_gtid_server.server_id;
ev.checksum_alg= current_binlog_check_alg;
if (!ret && writer.write(&ev)) ret= 1;
if (!ret && wsrep_write_cache_buf(&tmp_io_cache, buf, buf_len)) ret= 1;
@@ -1683,6 +1786,52 @@ static int wsrep_drop_table_query(THD* thd, uchar** buf, size_t* buf_len)
/* Forward declarations. */
int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
+bool wsrep_should_replicate_ddl_iterate(THD* thd, const TABLE_LIST* table_list)
+{
+ if (WSREP(thd))
+ {
+ for (const TABLE_LIST* it= table_list; it; it= it->next_global)
+ {
+ if (it->table &&
+ !wsrep_should_replicate_ddl(thd, it->table->s->db_type()->db_type))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool wsrep_should_replicate_ddl(THD* thd,
+ const enum legacy_db_type db_type)
+{
+ if (!wsrep_strict_ddl)
+ return true;
+
+ switch (db_type)
+ {
+ case DB_TYPE_INNODB:
+ return true;
+ break;
+ case DB_TYPE_MYISAM:
+ if (wsrep_replicate_myisam)
+ return true;
+ else
+ WSREP_DEBUG("wsrep OSU failed for %s", wsrep_thd_query(thd));
+ break;
+ case DB_TYPE_ARIA:
+ /* if (wsrep_replicate_aria) */
+ /* fallthrough */
+ default:
+ WSREP_DEBUG("wsrep OSU failed for %s", wsrep_thd_query(thd));
+ break;
+ }
+
+ /* STRICT, treat as error */
+ my_error(ER_GALERA_REPLICATION_NOT_SUPPORTED, MYF(0));
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_ILLEGAL_HA,
+ "WSREP: wsrep_strict_ddl=true and storage engine does not support Galera replication.");
+ return false;
+}
/*
Decide if statement should run in TOI.
@@ -1693,24 +1842,28 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
should be rewritten at later time for replication to contain only
non-temporary tables.
*/
-static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
- const TABLE_LIST *table_list)
+bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
+ const TABLE_LIST *table_list,
+ const HA_CREATE_INFO* create_info)
{
DBUG_ASSERT(!table || db);
DBUG_ASSERT(table_list || db);
LEX* lex= thd->lex;
SELECT_LEX* select_lex= lex->first_select_lex();
- TABLE_LIST* first_table= select_lex->table_list.first;
+ const TABLE_LIST* first_table= select_lex->table_list.first;
switch (lex->sql_command)
{
case SQLCOM_CREATE_TABLE:
- DBUG_ASSERT(!table_list);
if (thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
{
return false;
}
+ if (!wsrep_should_replicate_ddl(thd, create_info->db_type->db_type))
+ {
+ return false;
+ }
/*
If mariadb master has replicated a CTAS, we should not replicate the create table
part separately as TOI, but to replicate both create table and following inserts
@@ -1719,7 +1872,8 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
as TOI. We have to do relay log event lookup to see if row events follow the
create table event.
*/
- if (thd->slave_thread && !(thd->rgi_slave->gtid_ev_flags2 & Gtid_log_event::FL_STANDALONE))
+ if (thd->slave_thread &&
+ !(thd->rgi_slave->gtid_ev_flags2 & Gtid_log_event::FL_STANDALONE))
{
/* this is CTAS, either empty or populated table */
ulonglong event_size = 0;
@@ -1745,7 +1899,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
}
/* no next async replication event */
return true;
-
+ break;
case SQLCOM_CREATE_VIEW:
DBUG_ASSERT(!table_list);
@@ -1754,7 +1908,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
If any of the remaining tables refer to temporary table error
is returned to client, so TOI can be skipped
*/
- for (TABLE_LIST* it= first_table->next_global; it; it= it->next_global)
+ for (const TABLE_LIST* it= first_table->next_global; it; it= it->next_global)
{
if (thd->find_temporary_table(it))
{
@@ -1762,7 +1916,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
}
}
return true;
-
+ break;
case SQLCOM_CREATE_TRIGGER:
DBUG_ASSERT(first_table);
@@ -1772,7 +1926,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
return false;
}
return true;
-
+ break;
case SQLCOM_DROP_TRIGGER:
DBUG_ASSERT(table_list);
if (thd->find_temporary_table(table_list))
@@ -1780,7 +1934,12 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
return false;
}
return true;
-
+ break;
+ case SQLCOM_ALTER_TABLE:
+ if (create_info &&
+ !wsrep_should_replicate_ddl(thd, create_info->db_type->db_type))
+ return false;
+ /* fallthrough */
default:
if (table && !thd->find_temporary_table(db, table))
{
@@ -1789,7 +1948,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
if (table_list)
{
- for (TABLE_LIST* table= first_table; table; table= table->next_global)
+ for (const TABLE_LIST* table= first_table; table; table= table->next_global)
{
if (!thd->find_temporary_table(table->db.str, table->table_name.str))
{
@@ -1797,11 +1956,12 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table,
}
}
}
+
return !(table || table_list);
+ break;
}
}
-
static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
{
String log_query;
@@ -1813,10 +1973,10 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
log_query.set_charset(system_charset_info);
- if (sp->m_handler->type() == TYPE_ENUM_FUNCTION)
+ if (sp->m_handler->type() == SP_TYPE_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
- returns= retstr.lex_cstring();
+ retstr.get_value(&returns);
}
if (sp->m_handler->
show_create_sp(thd, &log_query,
@@ -1882,13 +2042,16 @@ static void wsrep_TOI_begin_failed(THD* thd, const wsrep_buf_t* /* const err */)
if (wsrep_emulate_bin_log) wsrep_thd_binlog_trx_reset(thd);
if (wsrep_write_dummy_event(thd, "TOI begin failed")) { goto fail; }
wsrep::client_state& cs(thd->wsrep_cs());
- int const ret= cs.leave_toi_local(wsrep::mutable_buffer());
+ std::string const err(wsrep::to_c_string(cs.current_error()));
+ wsrep::mutable_buffer err_buf;
+ err_buf.push_back(err);
+ int const ret= cs.leave_toi_local(err_buf);
if (ret)
{
WSREP_ERROR("Leaving critical section for failed TOI failed: thd: %lld, "
"schema: %s, SQL: %s, rcode: %d wsrep_error: %s",
(long long)thd->real_id, thd->db.str,
- thd->query(), ret, wsrep::to_c_string(cs.current_error()));
+ thd->query(), ret, err.c_str());
goto fail;
}
}
@@ -1907,14 +2070,16 @@ fail:
*/
static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
const TABLE_LIST* table_list,
- Alter_info* alter_info)
+ const Alter_info* alter_info,
+ const HA_CREATE_INFO* create_info)
{
- DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI);
+ DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_TOI);
- WSREP_DEBUG("TOI Begin");
- if (wsrep_can_run_in_toi(thd, db, table, table_list) == false)
+ WSREP_DEBUG("TOI Begin: %s", wsrep_thd_query(thd));
+
+ if (wsrep_can_run_in_toi(thd, db, table, table_list, create_info) == false)
{
- WSREP_DEBUG("No TOI for %s", WSREP_QUERY(thd));
+ WSREP_DEBUG("No TOI for %s", wsrep_thd_query(thd));
return 1;
}
@@ -1924,6 +2089,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
int rc;
buf_err= wsrep_TOI_event_buf(thd, &buf, &buf_len);
+
if (buf_err) {
WSREP_ERROR("Failed to create TOI event buf: %d", buf_err);
my_message(ER_UNKNOWN_ERROR,
@@ -1932,6 +2098,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
MYF(0));
return -1;
}
+
struct wsrep_buf buff= { buf, buf_len };
wsrep::key_array key_array=
@@ -1942,7 +2109,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
/* non replicated DDL, affecting temporary tables only */
WSREP_DEBUG("TO isolation skipped, sql: %s."
"Only temporary tables affected.",
- WSREP_QUERY(thd));
+ wsrep_thd_query(thd));
if (buf) my_free(buf);
return -1;
}
@@ -1950,6 +2117,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
thd_proc_info(thd, "acquiring total order isolation");
wsrep::client_state& cs(thd->wsrep_cs());
+
int ret= cs.enter_toi_local(key_array,
wsrep::const_buffer(buff.ptr, buff.len));
@@ -1957,7 +2125,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
{
DBUG_ASSERT(cs.current_error());
WSREP_DEBUG("to_execute_start() failed for %llu: %s, seqno: %lld",
- thd->thread_id, WSREP_QUERY(thd),
+ thd->thread_id, wsrep_thd_query(thd),
(long long)wsrep_thd_trx_seqno(thd));
/* jump to error handler in mysql_execute_command() */
@@ -1968,7 +2136,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
"Maximum size exceeded.",
ret,
(thd->db.str ? thd->db.str : "(null)"),
- WSREP_QUERY(thd));
+ wsrep_thd_query(thd));
my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED);
break;
default:
@@ -1976,7 +2144,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
"Check wsrep connection state and retry the query.",
ret,
(thd->db.str ? thd->db.str : "(null)"),
- WSREP_QUERY(thd));
+ wsrep_thd_query(thd));
if (!thd->is_error())
{
my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check "
@@ -1986,6 +2154,28 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table,
rc= -1;
}
else {
+ if (!thd->variables.gtid_seq_no)
+ {
+ uint64 seqno= 0;
+ if (thd->variables.wsrep_gtid_seq_no &&
+ thd->variables.wsrep_gtid_seq_no > wsrep_gtid_server.seqno())
+ {
+ seqno= thd->variables.wsrep_gtid_seq_no;
+ wsrep_gtid_server.seqno(thd->variables.wsrep_gtid_seq_no);
+ }
+ else
+ {
+ seqno= wsrep_gtid_server.seqno_inc();
+ }
+ thd->variables.wsrep_gtid_seq_no= 0;
+ thd->wsrep_current_gtid_seqno= seqno;
+ if (mysql_bin_log.is_open() && wsrep_gtid_mode)
+ {
+ thd->variables.gtid_seq_no= seqno;
+ thd->variables.gtid_domain_id= wsrep_gtid_server.domain_id;
+ thd->variables.server_id= wsrep_gtid_server.server_id;
+ }
+ }
++wsrep_to_isolation;
rc= 0;
}
@@ -2002,12 +2192,24 @@ static void wsrep_TOI_end(THD *thd) {
wsrep::client_state& client_state(thd->wsrep_cs());
DBUG_ASSERT(wsrep_thd_is_local_toi(thd));
WSREP_DEBUG("TO END: %lld: %s", client_state.toi_meta().seqno().get(),
- WSREP_QUERY(thd));
+ wsrep_thd_query(thd));
+
+ wsrep_gtid_server.signal_waiters(thd->wsrep_current_gtid_seqno, false);
if (wsrep_thd_is_local_toi(thd))
{
- wsrep_set_SE_checkpoint(client_state.toi_meta().gtid());
- int ret= client_state.leave_toi_local(wsrep::mutable_buffer());
+ wsrep::mutable_buffer err;
+
+ thd->wsrep_last_written_gtid_seqno= thd->wsrep_current_gtid_seqno;
+ wsrep_set_SE_checkpoint(client_state.toi_meta().gtid(), wsrep_gtid_server.gtid());
+
+ if (thd->is_error() && !wsrep_must_ignore_error(thd))
+ {
+ wsrep_store_error(thd, err);
+ }
+
+ int const ret= client_state.leave_toi_local(err);
+
if (!ret)
{
WSREP_DEBUG("TO END: %lld", client_state.toi_meta().seqno().get());
@@ -2015,7 +2217,7 @@ static void wsrep_TOI_end(THD *thd) {
else
{
WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s",
- ret, (thd->db.str ? thd->db.str : "(null)"), WSREP_QUERY(thd));
+ ret, (thd->db.str ? thd->db.str : "(null)"), wsrep_thd_query(thd));
}
}
}
@@ -2023,7 +2225,7 @@ static void wsrep_TOI_end(THD *thd) {
static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_)
{
WSREP_DEBUG("RSU BEGIN: %lld, : %s", wsrep_thd_trx_seqno(thd),
- WSREP_QUERY(thd));
+ wsrep_thd_query(thd));
if (thd->wsrep_cs().begin_rsu(5000))
{
WSREP_WARN("RSU begin failed");
@@ -2038,7 +2240,7 @@ static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_)
static void wsrep_RSU_end(THD *thd)
{
WSREP_DEBUG("RSU END: %lld : %s", wsrep_thd_trx_seqno(thd),
- WSREP_QUERY(thd));
+ wsrep_thd_query(thd));
if (thd->wsrep_cs().end_rsu())
{
WSREP_WARN("Failed to end RSU, server may need to be restarted");
@@ -2048,7 +2250,8 @@ static void wsrep_RSU_end(THD *thd)
int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
- Alter_info* alter_info)
+ const Alter_info* alter_info,
+ const HA_CREATE_INFO* create_info)
{
/*
No isolation for applier or replaying threads.
@@ -2074,13 +2277,15 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
{
my_message(ER_UNKNOWN_COM_ERROR,
"Aborting TOI: Global Read-Lock (FTWRL) in place.", MYF(0));
+ WSREP_DEBUG("Aborting TOI: Global Read-Lock (FTWRL) in place: %s %llu",
+ wsrep_thd_query(thd), thd->thread_id);
return -1;
}
if (wsrep_debug && thd->mdl_context.has_locks())
{
WSREP_DEBUG("thread holds MDL locks at TI begin: %s %llu",
- WSREP_QUERY(thd), thd->thread_id);
+ wsrep_thd_query(thd), thd->thread_id);
}
/*
@@ -2098,24 +2303,24 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
if (thd->variables.wsrep_on && wsrep_thd_is_local(thd))
{
- switch (thd->variables.wsrep_OSU_method) {
+ switch (wsrep_OSU_method_get(thd)) {
case WSREP_OSU_TOI:
- ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info);
+ ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, create_info);
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);
+ wsrep_OSU_method_get(thd));
ret= -1;
break;
}
switch (ret) {
- case 0: /* wsrep_TOI_begin sould set toi mode */ break;
+ case 0: /* wsrep_TOI_begin should set toi mode */ break;
case 1:
- /* TOI replication skipped, treat as success */
- ret= 0;
+ /* TOI replication skipped, treat as success */
+ ret= 0;
break;
case -1:
/* TOI replication failed, treat as error */
@@ -2132,12 +2337,12 @@ void wsrep_to_isolation_end(THD *thd)
wsrep_thd_is_in_rsu(thd));
if (wsrep_thd_is_local_toi(thd))
{
- DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI);
+ DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_TOI);
wsrep_TOI_end(thd);
}
else if (wsrep_thd_is_in_rsu(thd))
{
- DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_RSU);
+ DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_RSU);
wsrep_RSU_end(thd);
}
else
@@ -2172,7 +2377,7 @@ void wsrep_to_isolation_end(THD *thd)
*/
void wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
- MDL_ticket *ticket,
+ const MDL_ticket *ticket,
const MDL_key *key)
{
/* Fallback to the non-wsrep behaviour */
@@ -2402,8 +2607,7 @@ static my_bool kill_remaining_threads(THD *thd, THD *caller_thd)
void wsrep_close_client_connections(my_bool wait_to_end, THD* except_caller_thd)
{
/* Clear thread cache */
- kill_cached_threads++;
- flush_thread_cache();
+ thread_cache.final_flush();
/*
First signal all threads that it's time to die
@@ -2507,7 +2711,7 @@ int wsrep_must_ignore_error(THD* thd)
const uint flags= sql_command_flags[thd->lex->sql_command];
DBUG_ASSERT(error);
- DBUG_ASSERT(wsrep_thd_is_toi(thd) || wsrep_thd_is_applying(thd));
+ DBUG_ASSERT(wsrep_thd_is_toi(thd));
if ((wsrep_ignore_apply_errors & WSREP_IGNORE_ERRORS_ON_DDL))
goto ignore_error;
@@ -2589,17 +2793,17 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
if (create_info->tmp_table())
{
/* CREATE TEMPORARY TABLE LIKE must be skipped from replication */
- WSREP_DEBUG("CREATE TEMPORARY TABLE LIKE... skipped replication\n %s",
+ WSREP_DEBUG("CREATE TEMPORARY TABLE LIKE... skipped replication\n %s",
thd->query());
}
else if (!(thd->find_temporary_table(src_table)))
{
/* this is straight CREATE TABLE LIKE... with no tmp tables */
- WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL);
+ WSREP_TO_ISOLATION_BEGIN_CREATE(table->db.str, table->table_name.str, table, create_info);
}
else
{
- /* here we have CREATE TABLE LIKE <temporary table>
+ /* here we have CREATE TABLE LIKE <temporary table>
the temporary table definition will be needed in slaves to
enable the create to succeed
*/
@@ -2618,7 +2822,7 @@ bool wsrep_create_like_table(THD* thd, TABLE_LIST* table,
thd->wsrep_TOI_pre_query= query.ptr();
thd->wsrep_TOI_pre_query_len= query.length();
- WSREP_TO_ISOLATION_BEGIN(table->db.str, table->table_name.str, NULL);
+ WSREP_TO_ISOLATION_BEGIN_CREATE(table->db.str, table->table_name.str, table, create_info);
thd->wsrep_TOI_pre_query= NULL;
thd->wsrep_TOI_pre_query_len= 0;
@@ -2707,12 +2911,6 @@ void* start_wsrep_THD(void *arg)
statistic_increment(thread_created, &LOCK_status);
- if (wsrep_gtid_mode)
- {
- /* Adjust domain_id. */
- thd->variables.gtid_domain_id= wsrep_gtid_domain_id;
- }
-
thd->real_id=pthread_self(); // Keep purify happy
my_net_init(&thd->net,(st_vio*) 0, thd, MYF(0));
@@ -2725,20 +2923,13 @@ void* start_wsrep_THD(void *arg)
/* from bootstrap()... */
thd->bootstrap=1;
thd->max_client_packet_length= thd->net.max_packet;
- thd->security_ctx->master_access= ~(ulong)0;
+ thd->security_ctx->master_access= ALL_KNOWN_ACL;
/* from handle_one_connection... */
pthread_detach_this_thread();
mysql_thread_set_psi_id(thd->thread_id);
thd->thr_create_utime= microsecond_interval_timer();
- if (MYSQL_CALLBACK_ELSE(thread_scheduler, init_new_connection_thread, (), 0))
- {
- close_connection(thd, ER_OUT_OF_RESOURCES);
- statistic_increment(aborted_connects,&LOCK_status);
- MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
- goto error;
- }
// </5.1.17>
/*
@@ -2755,15 +2946,7 @@ void* start_wsrep_THD(void *arg)
thd->thread_stack= (char*) &thd;
wsrep_assign_from_threadvars(thd);
- if (wsrep_store_threadvars(thd))
- {
- close_connection(thd, ER_OUT_OF_RESOURCES);
- statistic_increment(aborted_connects,&LOCK_status);
- MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 0));
- delete thd;
- delete thd_args;
- goto error;
- }
+ wsrep_store_threadvars(thd);
thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
thd->security_ctx->skip_grants();
@@ -2835,7 +3018,7 @@ void* start_wsrep_THD(void *arg)
if (plugins_are_initialized)
{
net_end(&thd->net);
- MYSQL_CALLBACK(thread_scheduler, end_thread, (thd, 1));
+ unlink_thd(thd);
}
else
{
@@ -2844,9 +3027,9 @@ void* start_wsrep_THD(void *arg)
'Error in my_thread_global_end(): 2 threads didn't exit'
at server shutdown
*/
+ server_threads.erase(thd);
}
- server_threads.erase(thd);
delete thd;
my_thread_end();
return(NULL);
@@ -2885,3 +3068,55 @@ bool wsrep_consistency_check(THD *thd)
{
return thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING;
}
+
+
+/*
+ Commit an empty transaction.
+
+ If the transaction is real and the wsrep transaction is still active,
+ the transaction did not generate any rows or keys and is committed
+ as empty. Here the wsrep transaction is rolled back and after statement
+ step is performed to leave the wsrep transaction in the state as it
+ never existed.
+
+ This should not be an inline functions as it requires a lot of stack space
+ because of WSREP_DBUG() usage. It's also not a function that is
+ frequently called.
+*/
+
+void wsrep_commit_empty(THD* thd, bool all)
+{
+ DBUG_ENTER("wsrep_commit_empty");
+ WSREP_DEBUG("wsrep_commit_empty(%llu)", thd->thread_id);
+ if (wsrep_is_real(thd, all) &&
+ wsrep_thd_is_local(thd) &&
+ thd->wsrep_trx().active() &&
+ !thd->internal_transaction() &&
+ thd->wsrep_trx().state() != wsrep::transaction::s_committed)
+ {
+ /* @todo CTAS with STATEMENT binlog format and empty result set
+ seems to be committing empty. Figure out why and try to fix
+ elsewhere. */
+ DBUG_ASSERT(!wsrep_has_changes(thd) ||
+ (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
+ !thd->is_current_stmt_binlog_format_row()));
+ bool have_error= wsrep_current_error(thd);
+ int ret= wsrep_before_rollback(thd, all) ||
+ wsrep_after_rollback(thd, all) ||
+ wsrep_after_statement(thd);
+ /* The committing transaction was empty but it held some locks and
+ got BF aborted. As there were no certified changes in the
+ data, we ignore the deadlock error and rely on error reporting
+ by storage engine/server. */
+ if (!ret && !have_error && wsrep_current_error(thd))
+ {
+ DBUG_ASSERT(wsrep_current_error(thd) == wsrep::e_deadlock_error);
+ thd->wsrep_cs().reset_error();
+ }
+ if (ret)
+ {
+ WSREP_DEBUG("wsrep_commit_empty failed: %d", wsrep_current_error(thd));
+ }
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index a717caa836e..4461d73a928 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -1,4 +1,5 @@
/* Copyright 2008-2017 Codership Oy <http://www.codership.com>
+ Copyright (c) 2020, 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
@@ -39,6 +40,7 @@ typedef struct st_mysql_show_var SHOW_VAR;
#include "wsrep/streaming_context.hpp"
#include "wsrep_api.h"
#include <vector>
+#include <map>
#include "wsrep_server_state.h"
#define WSREP_UNDEFINED_TRX_ID ULONGLONG_MAX
@@ -98,7 +100,7 @@ extern ulong wsrep_running_applier_threads;
extern ulong wsrep_running_rollbacker_threads;
extern bool wsrep_new_cluster;
extern bool wsrep_gtid_mode;
-extern uint32 wsrep_gtid_domain_id;
+extern my_bool wsrep_strict_ddl;
enum enum_wsrep_reject_types {
WSREP_REJECT_NONE, /* nothing rejected */
@@ -109,7 +111,7 @@ enum enum_wsrep_reject_types {
enum enum_wsrep_OSU_method {
WSREP_OSU_TOI,
WSREP_OSU_RSU,
- WSREP_OSU_NONE,
+ WSREP_OSU_NONE
};
enum enum_wsrep_sync_wait {
@@ -186,6 +188,7 @@ void wsrep_recover_sr_from_storage(THD *);
// Other wsrep global variables
extern my_bool wsrep_inited; // whether wsrep is initialized ?
+extern bool wsrep_service_started;
extern "C" void wsrep_fire_rollbacker(THD *thd);
extern "C" uint32 wsrep_thd_wsrep_rand(THD *thd);
@@ -246,14 +249,14 @@ extern wsrep_seqno_t wsrep_locked_seqno;
void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...);
#define WSREP_DEBUG(...) \
- if (wsrep_debug) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
-#define WSREP_INFO(...) WSREP_LOG(sql_print_information, ##__VA_ARGS__)
-#define WSREP_WARN(...) WSREP_LOG(sql_print_warning, ##__VA_ARGS__)
-#define WSREP_ERROR(...) WSREP_LOG(sql_print_error, ##__VA_ARGS__)
+ if (wsrep_debug) sql_print_information( "WSREP: " __VA_ARGS__)
+#define WSREP_INFO(...) sql_print_information( "WSREP: " __VA_ARGS__)
+#define WSREP_WARN(...) sql_print_warning( "WSREP: " __VA_ARGS__)
+#define WSREP_ERROR(...) sql_print_error( "WSREP: " __VA_ARGS__)
#define WSREP_LOG_CONFLICT_THD(thd, role) \
- WSREP_LOG(sql_print_information, \
- "%s: \n " \
+ sql_print_information( \
+ "WSREP: %s: \n " \
" THD: %lu, mode: %s, state: %s, conflict: %s, seqno: %lld\n " \
" SQL: %s", \
role, \
@@ -268,12 +271,12 @@ void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...);
#define WSREP_LOG_CONFLICT(bf_thd, victim_thd, bf_abort) \
if (wsrep_debug || wsrep_log_conflicts) \
{ \
- WSREP_LOG(sql_print_information, "cluster conflict due to %s for threads:", \
+ sql_print_information( "WSREP: 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 (victim_thd) WSREP_LOG_CONFLICT_THD(victim_thd, "Victim thread"); \
- WSREP_LOG(sql_print_information, "context: %s:%d", __FILE__, __LINE__); \
+ sql_print_information("WSREP: context: %s:%d", __FILE__, __LINE__); \
}
#define WSREP_PROVIDER_EXISTS \
@@ -298,6 +301,7 @@ extern mysql_mutex_t LOCK_wsrep_replaying;
extern mysql_cond_t COND_wsrep_replaying;
extern mysql_mutex_t LOCK_wsrep_slave_threads;
extern mysql_cond_t COND_wsrep_slave_threads;
+extern mysql_mutex_t LOCK_wsrep_gtid_wait_upto;
extern mysql_mutex_t LOCK_wsrep_cluster_config;
extern mysql_mutex_t LOCK_wsrep_desync;
extern mysql_mutex_t LOCK_wsrep_SR_pool;
@@ -311,9 +315,6 @@ extern mysql_cond_t COND_wsrep_donor_monitor;
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
@@ -332,6 +333,8 @@ extern PSI_mutex_key key_LOCK_wsrep_replaying;
extern PSI_cond_key key_COND_wsrep_replaying;
extern PSI_mutex_key key_LOCK_wsrep_slave_threads;
extern PSI_cond_key key_COND_wsrep_slave_threads;
+extern PSI_mutex_key key_LOCK_wsrep_gtid_wait_upto;
+extern PSI_cond_key key_COND_wsrep_gtid_wait_upto;
extern PSI_mutex_key key_LOCK_wsrep_cluster_config;
extern PSI_mutex_key key_LOCK_wsrep_desync;
extern PSI_mutex_key key_LOCK_wsrep_SR_pool;
@@ -355,9 +358,15 @@ extern PSI_thread_key key_wsrep_sst_donor_monitor;
struct TABLE_LIST;
class Alter_info;
+struct HA_CREATE_INFO;
+
int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_,
const TABLE_LIST* table_list,
- Alter_info* alter_info= NULL);
+ const Alter_info* alter_info= NULL,
+ const HA_CREATE_INFO* create_info= NULL);
+
+bool wsrep_should_replicate_ddl(THD* thd, const enum legacy_db_type db_type);
+bool wsrep_should_replicate_ddl_iterate(THD* thd, const TABLE_LIST* table_list);
void wsrep_to_isolation_end(THD *thd);
@@ -382,7 +391,129 @@ class Log_event;
int wsrep_ignored_error_code(Log_event* ev, int error);
int wsrep_must_ignore_error(THD* thd);
-bool wsrep_replicate_GTID(THD* thd);
+struct wsrep_server_gtid_t
+{
+ uint32 domain_id;
+ uint32 server_id;
+ uint64 seqno;
+};
+class Wsrep_gtid_server
+{
+public:
+ uint32 domain_id;
+ uint32 server_id;
+ Wsrep_gtid_server()
+ : m_force_signal(false)
+ , m_seqno(0)
+ , m_committed_seqno(0)
+ { }
+ void gtid(const wsrep_server_gtid_t& gtid)
+ {
+ domain_id= gtid.domain_id;
+ server_id= gtid.server_id;
+ m_seqno= gtid.seqno;
+ }
+ wsrep_server_gtid_t gtid()
+ {
+ wsrep_server_gtid_t gtid;
+ gtid.domain_id= domain_id;
+ gtid.server_id= server_id;
+ gtid.seqno= m_seqno;
+ return gtid;
+ }
+ void seqno(const uint64 seqno) { m_seqno= seqno; }
+ uint64 seqno() const { return m_seqno; }
+ uint64 seqno_committed() const { return m_committed_seqno; }
+ uint64 seqno_inc()
+ {
+ m_seqno++;
+ return m_seqno;
+ }
+ const wsrep_server_gtid_t& undefined()
+ {
+ return m_undefined;
+ }
+ int wait_gtid_upto(const uint64_t seqno, uint timeout)
+ {
+ int wait_result= 0;
+ struct timespec wait_time;
+ int ret= 0;
+ mysql_cond_t wait_cond;
+ mysql_cond_init(key_COND_wsrep_gtid_wait_upto, &wait_cond, NULL);
+ set_timespec(wait_time, timeout);
+ mysql_mutex_lock(&LOCK_wsrep_gtid_wait_upto);
+ std::multimap<uint64, mysql_cond_t*>::iterator it;
+ if (seqno > m_seqno)
+ {
+ try
+ {
+ it= m_wait_map.insert(std::make_pair(seqno, &wait_cond));
+ }
+ catch (std::bad_alloc& e)
+ {
+ ret= ENOMEM;
+ }
+ while (!ret && (m_committed_seqno < seqno) && !m_force_signal)
+ {
+ wait_result= mysql_cond_timedwait(&wait_cond,
+ &LOCK_wsrep_gtid_wait_upto,
+ &wait_time);
+ if (wait_result == ETIMEDOUT || wait_result == ETIME)
+ {
+ ret= wait_result;
+ break;
+ }
+ }
+ if (ret != ENOMEM)
+ {
+ m_wait_map.erase(it);
+ }
+ }
+ mysql_mutex_unlock(&LOCK_wsrep_gtid_wait_upto);
+ mysql_cond_destroy(&wait_cond);
+ return ret;
+ }
+ void signal_waiters(uint64 seqno, bool signal_all)
+ {
+ mysql_mutex_lock(&LOCK_wsrep_gtid_wait_upto);
+ if (!signal_all && (m_committed_seqno >= seqno))
+ {
+ mysql_mutex_unlock(&LOCK_wsrep_gtid_wait_upto);
+ return;
+ }
+ m_force_signal= true;
+ std::multimap<uint64, mysql_cond_t*>::iterator it_end;
+ std::multimap<uint64, mysql_cond_t*>::iterator it_begin;
+ if (signal_all)
+ {
+ it_end= m_wait_map.end();
+ }
+ else
+ {
+ it_end= m_wait_map.upper_bound(seqno);
+ }
+ if (m_committed_seqno < seqno)
+ {
+ m_committed_seqno= seqno;
+ }
+ for (it_begin = m_wait_map.begin(); it_begin != it_end; ++it_begin)
+ {
+ mysql_cond_signal(it_begin->second);
+ }
+ m_force_signal= false;
+ mysql_mutex_unlock(&LOCK_wsrep_gtid_wait_upto);
+ }
+private:
+ const wsrep_server_gtid_t m_undefined= {0,0,0};
+ std::multimap<uint64, mysql_cond_t*> m_wait_map;
+ bool m_force_signal;
+ Atomic_counter<uint64_t> m_seqno;
+ Atomic_counter<uint64_t> m_committed_seqno;
+};
+extern Wsrep_gtid_server wsrep_gtid_server;
+void wsrep_init_gtid();
+bool wsrep_check_gtid_seqno(const uint32&, const uint32&, uint64&);
+bool wsrep_get_binlog_gtid_seqno(wsrep_server_gtid_t&);
typedef struct wsrep_key_arr
{
@@ -398,7 +529,7 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr);
extern void
wsrep_handle_mdl_conflict(MDL_context *requestor_ctx,
- MDL_ticket *ticket,
+ const MDL_ticket *ticket,
const MDL_key *key);
enum wsrep_thread_type {
@@ -497,6 +628,7 @@ enum wsrep::streaming_context::fragment_unit wsrep_fragment_unit(ulong unit);
#define wsrep_thr_deinit() do {} while(0)
#define wsrep_init_globals() do {} while(0)
#define wsrep_create_appliers(X) do {} while(0)
+#define wsrep_should_replicate_ddl(X,Y) (1)
#endif /* WITH_WSREP */
diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc
index b1ad718255d..9f568226079 100644
--- a/sql/wsrep_schema.cc
+++ b/sql/wsrep_schema.cc
@@ -139,6 +139,25 @@ private:
my_bool m_wsrep_on;
};
+class thd_server_status
+{
+public:
+ thd_server_status(THD* thd, uint server_status, bool condition)
+ : m_thd(thd)
+ , m_thd_server_status(thd->server_status)
+ {
+ if (condition)
+ thd->server_status= server_status;
+ }
+ ~thd_server_status()
+ {
+ m_thd->server_status= m_thd_server_status;
+ }
+private:
+ THD* m_thd;
+ uint m_thd_server_status;
+};
+
class thd_context_switch
{
public:
@@ -478,12 +497,11 @@ static int scan(TABLE* table, uint field, INTTYPE& val)
static int scan(TABLE* table, uint field, char* strbuf, uint strbuf_len)
{
- String str;
- (void)table->field[field]->val_str(&str);
- LEX_CSTRING tmp= str.lex_cstring();
- uint len = tmp.length;
- strncpy(strbuf, tmp.str, std::min(len, strbuf_len));
- strbuf[strbuf_len - 1]= '\0';
+ uint len;
+ StringBuffer<STRING_BUFFER_USUAL_SIZE> str;
+ (void) table->field[field]->val_str(&str);
+ len= str.length();
+ strmake(strbuf, str.ptr(), MY_MIN(len, strbuf_len-1));
return 0;
}
@@ -1099,6 +1117,9 @@ int Wsrep_schema::remove_fragments(THD* thd,
}
else
{
+ Wsrep_schema_impl::thd_server_status
+ thd_server_status(thd, thd->server_status | SERVER_STATUS_IN_TRANS,
+ thd->in_multi_stmt_transaction_mode());
Wsrep_schema_impl::finish_stmt(thd);
}
@@ -1286,7 +1307,7 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd)
goto out;
}
- while (true)
+ while (0 == error)
{
if ((error= Wsrep_schema_impl::next_record(frag_table)) == 0)
{
@@ -1336,19 +1357,23 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd)
}
applier->store_globals();
wsrep::mutable_buffer unused;
- applier->apply_write_set(ws_meta, data, unused);
- applier->after_apply();
+ if ((ret= applier->apply_write_set(ws_meta, data, unused)) != 0)
+ {
+ WSREP_ERROR("SR trx recovery applying returned %d", ret);
+ }
+ else
+ {
+ applier->after_apply();
+ }
storage_service.store_globals();
}
else if (error == HA_ERR_END_OF_FILE)
{
ret= 0;
- break;
}
else
{
WSREP_ERROR("SR table scan returned error %d", error);
- break;
}
}
Wsrep_schema_impl::end_scan(frag_table);
diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc
index 57b9c7fd626..7ba744b4d3c 100644
--- a/sql/wsrep_server_service.cc
+++ b/sql/wsrep_server_service.cc
@@ -153,7 +153,7 @@ void Wsrep_server_service::bootstrap()
wsrep::log_info()
<< "Bootstrapping a new cluster, setting initial position to "
<< wsrep::gtid::undefined();
- wsrep_set_SE_checkpoint(wsrep::gtid::undefined());
+ wsrep_set_SE_checkpoint(wsrep::gtid::undefined(), wsrep_gtid_server.undefined());
}
void Wsrep_server_service::log_message(enum wsrep::log::level level,
@@ -213,7 +213,7 @@ void Wsrep_server_service::log_view(
if (prev_view.state_id().id() != view.state_id().id())
{
WSREP_DEBUG("New cluster UUID was generated, resetting position info");
- wsrep_set_SE_checkpoint(wsrep::gtid::undefined());
+ wsrep_set_SE_checkpoint(wsrep::gtid::undefined(), wsrep_gtid_server.undefined());
checkpoint_was_reset= true;
}
@@ -264,9 +264,9 @@ void Wsrep_server_service::log_view(
Wsrep_server_state::instance().provider().last_committed_gtid().seqno();
if (checkpoint_was_reset || last_committed != view.state_id().seqno())
{
- wsrep_set_SE_checkpoint(view.state_id());
+ wsrep_set_SE_checkpoint(view.state_id(), wsrep_gtid_server.gtid());
}
- DBUG_ASSERT(wsrep_get_SE_checkpoint().id() == view.state_id().id());
+ DBUG_ASSERT(wsrep_get_SE_checkpoint<wsrep::gtid>().id() == view.state_id().id());
}
else
{
@@ -300,7 +300,7 @@ wsrep::view Wsrep_server_service::get_view(wsrep::client_service& c,
wsrep::gtid Wsrep_server_service::get_position(wsrep::client_service&)
{
- return wsrep_get_SE_checkpoint();
+ return wsrep_get_SE_checkpoint<wsrep::gtid>();
}
void Wsrep_server_service::set_position(wsrep::client_service& c WSREP_UNUSED,
@@ -318,7 +318,7 @@ void Wsrep_server_service::set_position(wsrep::client_service& c WSREP_UNUSED,
WSREP_WARN("Wait for gtid returned error %d while waiting for "
"prior transactions to commit before setting position", err);
}
- wsrep_set_SE_checkpoint(gtid);
+ wsrep_set_SE_checkpoint(gtid, wsrep_gtid_server.gtid());
}
void Wsrep_server_service::log_state_change(
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index c024f08dd22..6a7b16d243e 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -245,7 +245,7 @@ static bool sst_auth_real_set (const char* value)
if (value)
{
- v= my_strdup(value, MYF(0));
+ v= my_strdup(PSI_INSTRUMENT_ME, value, MYF(0));
}
else // its NULL
{
@@ -263,7 +263,7 @@ static bool sst_auth_real_set (const char* value)
if (strlen(sst_auth_real))
{
if (wsrep_sst_auth) { my_free((void*) wsrep_sst_auth); }
- wsrep_sst_auth= my_strdup(WSREP_SST_AUTH_MASK, MYF(0));
+ wsrep_sst_auth= my_strdup(PSI_INSTRUMENT_ME, WSREP_SST_AUTH_MASK, MYF(0));
}
return 0;
}
@@ -351,8 +351,8 @@ void wsrep_sst_received (THD* thd,
wsrep::seqno(seqno));
if (!wsrep_before_SE()) {
- wsrep_set_SE_checkpoint(wsrep::gtid::undefined());
- wsrep_set_SE_checkpoint(sst_gtid);
+ wsrep_set_SE_checkpoint(wsrep::gtid::undefined(), wsrep_gtid_server.undefined());
+ wsrep_set_SE_checkpoint(sst_gtid, wsrep_gtid_server.gtid());
}
wsrep_verify_SE_checkpoint(uuid, seqno);
@@ -364,7 +364,7 @@ void wsrep_sst_received (THD* thd,
wsrep_store_threadvars(thd);
}
else {
- my_pthread_setspecific_ptr(THR_THD, NULL);
+ set_current_thd(nullptr);
}
/* During sst WSREP(thd) is not yet set for joiner. */
@@ -415,7 +415,7 @@ static char* generate_name_value(const char* name, const char* value)
size_t name_len= strlen(name);
size_t value_len= strlen(value);
char* buf=
- (char*) my_malloc((name_len + value_len + 5) * sizeof(char), MYF(0));
+ (char*) my_malloc(PSI_INSTRUMENT_ME, (name_len + value_len + 5), MYF(0));
if (buf)
{
char* ref= buf;
@@ -450,11 +450,11 @@ static int generate_binlog_opt_val(char** ret)
*ret= strcmp(opt_bin_logname, "0") ?
generate_name_value(WSREP_SST_OPT_BINLOG,
opt_bin_logname) :
- my_strdup("", MYF(0));
+ my_strdup(PSI_INSTRUMENT_ME, "", MYF(0));
}
else
{
- *ret= my_strdup("", MYF(0));
+ *ret= my_strdup(PSI_INSTRUMENT_ME, "", MYF(0));
}
if (!*ret) return -ENOMEM;
return 0;
@@ -469,11 +469,11 @@ static int generate_binlog_index_opt_val(char** ret)
*ret= strcmp(opt_binlog_index_name, "0") ?
generate_name_value(WSREP_SST_OPT_BINLOG_INDEX,
opt_binlog_index_name) :
- my_strdup("", MYF(0));
+ my_strdup(PSI_INSTRUMENT_ME, "", MYF(0));
}
else
{
- *ret= my_strdup("", MYF(0));
+ *ret= my_strdup(PSI_INSTRUMENT_ME, "", MYF(0));
}
if (!*ret) return -ENOMEM;
return 0;
@@ -595,7 +595,7 @@ static void* sst_joiner_thread (void* a)
err= EINVAL;
goto err;
} else {
- wsrep_gtid_domain_id= (uint32) domain_id;
+ wsrep_gtid_server.domain_id= (uint32) domain_id;
}
}
}
@@ -1366,13 +1366,15 @@ static int sst_donate_mysqldump (const char* addr,
WSREP_SST_OPT_LPORT " '%u' "
WSREP_SST_OPT_SOCKET " '%s' "
"%s"
- WSREP_SST_OPT_GTID " '%s:%lld' "
+ WSREP_SST_OPT_GTID " '%s:%lld,%d-%d-%llu' "
WSREP_SST_OPT_GTID_DOMAIN_ID " '%d'"
"%s",
addr, port, mysqld_port, mysqld_unix_port,
wsrep_defaults_file,
uuid_oss.str().c_str(), gtid.seqno().get(),
- wsrep_gtid_domain_id,
+ wsrep_gtid_server.domain_id, wsrep_gtid_server.server_id,
+ wsrep_gtid_server.seqno(),
+ wsrep_gtid_server.domain_id,
bypass ? " " WSREP_SST_OPT_BYPASS : "");
if (ret < 0 || size_t(ret) >= cmd_len)
@@ -1539,7 +1541,7 @@ static int sst_flush_tables(THD* thd)
*/
char content[100];
snprintf(content, sizeof(content), "%s:%lld %d\n", wsrep_cluster_state_uuid,
- (long long)wsrep_locked_seqno, wsrep_gtid_domain_id);
+ (long long)wsrep_locked_seqno, wsrep_gtid_server.domain_id);
err= sst_create_file(flush_success, content);
const char base_name[]= "tables_flushed";
@@ -1564,7 +1566,7 @@ static int sst_flush_tables(THD* thd)
fprintf(file, "%s:%lld %u\n",
uuid_oss.str().c_str(), server_state.pause_seqno().get(),
- wsrep_gtid_domain_id);
+ wsrep_gtid_server.domain_id);
fsync(fileno(file));
fclose(file);
if (rename(tmp_name, real_name) == -1)
@@ -1785,7 +1787,7 @@ static int sst_donate_other (const char* method,
"%s",
method, addr, mysqld_unix_port, mysql_real_data_home,
wsrep_defaults_file,
- uuid_oss.str().c_str(), gtid.seqno().get(), wsrep_gtid_domain_id,
+ uuid_oss.str().c_str(), gtid.seqno().get(), wsrep_gtid_server.domain_id,
binlog_opt_val, binlog_index_opt_val,
bypass ? " " WSREP_SST_OPT_BYPASS : "");
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index 0f72c132d84..21a1c03d33e 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -454,13 +454,13 @@ void wsrep_restore_threadvars(const Wsrep_threadvars& globals)
pthread_setspecific(THR_KEY_mysys, globals.mysys_var);
}
-int wsrep_store_threadvars(THD *thd)
+void wsrep_store_threadvars(THD *thd)
{
if (thread_handling == SCHEDULER_TYPES_COUNT)
{
pthread_setspecific(THR_KEY_mysys, thd->mysys_var);
}
- return thd->store_globals();
+ thd->store_globals();
}
void wsrep_reset_threadvars(THD *thd)
diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h
index d24d8e6358f..c7350c79ee7 100644
--- a/sql/wsrep_thd.h
+++ b/sql/wsrep_thd.h
@@ -162,7 +162,7 @@ void wsrep_restore_threadvars(const Wsrep_threadvars&);
/**
Store variables into thread local storage.
*/
-int wsrep_store_threadvars(THD *);
+void wsrep_store_threadvars(THD *);
/**
Reset thread local storage.
diff --git a/sql/wsrep_trans_observer.h b/sql/wsrep_trans_observer.h
index 05970e8b12f..35b93cb4bf5 100644
--- a/sql/wsrep_trans_observer.h
+++ b/sql/wsrep_trans_observer.h
@@ -26,13 +26,16 @@
class THD;
+void wsrep_commit_empty(THD* thd, bool all);
+
/*
Return true if THD has active wsrep transaction.
*/
static inline bool wsrep_is_active(THD* thd)
{
return (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
- thd->wsrep_cs().transaction().active());
+ thd->wsrep_cs().transaction().active() &&
+ !thd->internal_transaction());
}
/*
@@ -80,7 +83,7 @@ static inline bool wsrep_not_committed(THD* thd)
*/
static inline bool wsrep_is_real(THD* thd, bool all)
{
- return (all || thd->transaction.all.ha_list == 0);
+ return (all || thd->transaction->all.ha_list == 0);
}
/*
@@ -123,7 +126,7 @@ static inline bool wsrep_streaming_enabled(THD* thd)
}
/*
- Return number of fragments succesfully certified for the
+ Return number of fragments successfully certified for the
current statement.
*/
static inline size_t wsrep_fragments_certified_for_stmt(THD* thd)
@@ -158,7 +161,7 @@ static inline int wsrep_start_trx_if_not_started(THD* thd)
Return zero on succes, non-zero on failure.
*/
-static inline int wsrep_after_row(THD* thd, bool)
+static inline int wsrep_after_row_internal(THD* thd)
{
if (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
wsrep_thd_is_local(thd))
@@ -233,7 +236,8 @@ static inline int wsrep_before_prepare(THD* thd, bool all)
{
DBUG_ASSERT(!thd->wsrep_trx().ws_meta().gtid().is_undefined());
wsrep_xid_init(&thd->wsrep_xid,
- thd->wsrep_trx().ws_meta().gtid());
+ thd->wsrep_trx().ws_meta().gtid(),
+ wsrep_gtid_server.gtid());
}
DBUG_RETURN(ret);
}
@@ -273,8 +277,33 @@ static inline int wsrep_before_commit(THD* thd, bool all)
if ((ret= thd->wsrep_cs().before_commit()) == 0)
{
DBUG_ASSERT(!thd->wsrep_trx().ws_meta().gtid().is_undefined());
+ if (!thd->variables.gtid_seq_no &&
+ (thd->wsrep_trx().ws_meta().flags() & wsrep::provider::flag::commit))
+ {
+ uint64 seqno= 0;
+ if (thd->variables.wsrep_gtid_seq_no &&
+ thd->variables.wsrep_gtid_seq_no > wsrep_gtid_server.seqno())
+ {
+ seqno= thd->variables.wsrep_gtid_seq_no;
+ wsrep_gtid_server.seqno(thd->variables.wsrep_gtid_seq_no);
+ }
+ else
+ {
+ seqno= wsrep_gtid_server.seqno_inc();
+ }
+ thd->variables.wsrep_gtid_seq_no= 0;
+ thd->wsrep_current_gtid_seqno= seqno;
+ if (mysql_bin_log.is_open() && wsrep_gtid_mode)
+ {
+ thd->variables.gtid_seq_no= seqno;
+ thd->variables.gtid_domain_id= wsrep_gtid_server.domain_id;
+ thd->variables.server_id= wsrep_gtid_server.server_id;
+ }
+ }
+
wsrep_xid_init(&thd->wsrep_xid,
- thd->wsrep_trx().ws_meta().gtid());
+ thd->wsrep_trx().ws_meta().gtid(),
+ wsrep_gtid_server.gtid());
wsrep_register_for_group_commit(thd);
}
DBUG_RETURN(ret);
@@ -292,9 +321,7 @@ static inline int wsrep_before_commit(THD* thd, bool all)
Return zero on succes, non-zero on failure.
*/
-static inline int wsrep_ordered_commit(THD* thd,
- bool all,
- const wsrep_apply_error&)
+static inline int wsrep_ordered_commit(THD* thd, bool all)
{
DBUG_ENTER("wsrep_ordered_commit");
WSREP_DEBUG("wsrep_ordered_commit: %d", wsrep_is_real(thd, all));
@@ -316,6 +343,8 @@ static inline int wsrep_after_commit(THD* thd, bool all)
(long long)wsrep_thd_trx_seqno(thd),
wsrep_has_changes(thd));
DBUG_ASSERT(wsrep_run_commit_hook(thd, all));
+ if (thd->internal_transaction())
+ DBUG_RETURN(0);
int ret= 0;
if (thd->wsrep_trx().state() == wsrep::transaction::s_committing)
{
@@ -385,7 +414,8 @@ static inline int wsrep_after_rollback(THD* thd, bool all)
static inline int wsrep_before_statement(THD* thd)
{
- return (thd->wsrep_cs().state() != wsrep::client_state::s_none ?
+ return (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction() ?
thd->wsrep_cs().before_statement() : 0);
}
@@ -393,7 +423,8 @@ static inline
int wsrep_after_statement(THD* thd)
{
DBUG_ENTER("wsrep_after_statement");
- DBUG_RETURN(thd->wsrep_cs().state() != wsrep::client_state::s_none ?
+ DBUG_RETURN(thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction() ?
thd->wsrep_cs().after_statement() : 0);
}
@@ -401,7 +432,8 @@ static inline void wsrep_after_apply(THD* thd)
{
DBUG_ASSERT(wsrep_thd_is_applying(thd));
WSREP_DEBUG("wsrep_after_apply %lld", thd->thread_id);
- thd->wsrep_cs().after_applying();
+ if (!thd->internal_transaction())
+ thd->wsrep_cs().after_applying();
}
static inline void wsrep_open(THD* thd)
@@ -424,7 +456,8 @@ static inline void wsrep_open(THD* thd)
static inline void wsrep_close(THD* thd)
{
DBUG_ENTER("wsrep_close");
- if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction())
{
thd->wsrep_cs().close();
}
@@ -435,7 +468,8 @@ static inline void
wsrep_wait_rollback_complete_and_acquire_ownership(THD *thd)
{
DBUG_ENTER("wsrep_wait_rollback_complete_and_acquire_ownership");
- if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction())
{
thd->wsrep_cs().wait_rollback_complete_and_acquire_ownership();
}
@@ -444,8 +478,9 @@ wsrep_wait_rollback_complete_and_acquire_ownership(THD *thd)
static inline int wsrep_before_command(THD* thd)
{
- return (thd->wsrep_cs().state() != wsrep::client_state::s_none ?
- thd->wsrep_cs().before_command() : 0);
+ return (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction() ?
+ thd->wsrep_cs().before_command() : 0);
}
/*
Called after each command.
@@ -454,7 +489,8 @@ static inline int wsrep_before_command(THD* thd)
*/
static inline void wsrep_after_command_before_result(THD* thd)
{
- if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction())
{
thd->wsrep_cs().after_command_before_result();
}
@@ -462,7 +498,8 @@ static inline void wsrep_after_command_before_result(THD* thd)
static inline void wsrep_after_command_after_result(THD* thd)
{
- if (thd->wsrep_cs().state() != wsrep::client_state::s_none)
+ if (thd->wsrep_cs().state() != wsrep::client_state::s_none &&
+ !thd->internal_transaction())
{
thd->wsrep_cs().after_command_after_result();
}
@@ -486,50 +523,4 @@ wsrep_current_error_status(THD* thd)
return thd->wsrep_cs().current_error_status();
}
-
-/*
- Commit an empty transaction.
-
- If the transaction is real and the wsrep transaction is still active,
- the transaction did not generate any rows or keys and is committed
- as empty. Here the wsrep transaction is rolled back and after statement
- step is performed to leave the wsrep transaction in the state as it
- never existed.
-*/
-static inline void wsrep_commit_empty(THD* thd, bool all)
-{
- DBUG_ENTER("wsrep_commit_empty");
- WSREP_DEBUG("wsrep_commit_empty(%llu)", thd->thread_id);
- if (wsrep_is_real(thd, all) &&
- wsrep_thd_is_local(thd) &&
- thd->wsrep_trx().active() &&
- thd->wsrep_trx().state() != wsrep::transaction::s_committed)
- {
- /* @todo CTAS with STATEMENT binlog format and empty result set
- seems to be committing empty. Figure out why and try to fix
- elsewhere. */
- DBUG_ASSERT(!wsrep_has_changes(thd) ||
- (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
- !thd->is_current_stmt_binlog_format_row()));
- bool have_error= wsrep_current_error(thd);
- int ret= wsrep_before_rollback(thd, all) ||
- wsrep_after_rollback(thd, all) ||
- wsrep_after_statement(thd);
- /* The committing transaction was empty but it held some locks and
- got BF aborted. As there were no certified changes in the
- data, we ignore the deadlock error and rely on error reporting
- by storage engine/server. */
- if (!ret && !have_error && wsrep_current_error(thd))
- {
- DBUG_ASSERT(wsrep_current_error(thd) == wsrep::e_deadlock_error);
- thd->wsrep_cs().reset_error();
- }
- if (ret)
- {
- WSREP_DEBUG("wsrep_commit_empty failed: %d", wsrep_current_error(thd));
- }
- }
- DBUG_VOID_RETURN;
-}
-
#endif /* WSREP_TRANS_OBSERVER */
diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc
index 4cf131fd974..a679304c40a 100644
--- a/sql/wsrep_utils.cc
+++ b/sql/wsrep_utils.cc
@@ -428,7 +428,7 @@ thd::thd (my_bool won, bool system_thread) : init(), ptr(new THD(0))
ptr->variables.wsrep_on= won;
if (system_thread)
ptr->system_thread= SYSTEM_THREAD_GENERIC;
- ptr->security_ctx->master_access= ~(ulong)0;
+ ptr->security_ctx->master_access= ALL_KNOWN_ACL;
lex_start(ptr);
}
}
@@ -438,7 +438,7 @@ thd::~thd ()
if (ptr)
{
delete ptr;
- my_pthread_setspecific_ptr (THR_THD, 0);
+ set_current_thd(nullptr);
}
}
diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
index 5336bc9f508..d894fa6d555 100644
--- a/sql/wsrep_var.cc
+++ b/sql/wsrep_var.cc
@@ -30,14 +30,17 @@ ulong wsrep_reject_queries;
int wsrep_init_vars()
{
- wsrep_provider = my_strdup(WSREP_NONE, MYF(MY_WME));
- wsrep_provider_options= my_strdup("", MYF(MY_WME));
- wsrep_cluster_address = my_strdup("", MYF(MY_WME));
- wsrep_cluster_name = my_strdup(WSREP_CLUSTER_NAME, MYF(MY_WME));
- wsrep_node_name = my_strdup("", MYF(MY_WME));
- wsrep_node_address = my_strdup("", MYF(MY_WME));
- wsrep_node_incoming_address= my_strdup(WSREP_NODE_INCOMING_AUTO, MYF(MY_WME));
- wsrep_start_position = my_strdup(WSREP_START_POSITION_ZERO, MYF(MY_WME));
+ wsrep_provider = my_strdup(PSI_INSTRUMENT_ME, WSREP_NONE, MYF(MY_WME));
+ wsrep_provider_options= my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
+ wsrep_cluster_address = my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
+ wsrep_cluster_name = my_strdup(PSI_INSTRUMENT_ME, WSREP_CLUSTER_NAME, MYF(MY_WME));
+ wsrep_node_name = my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
+ wsrep_node_address = my_strdup(PSI_INSTRUMENT_ME, "", MYF(MY_WME));
+ wsrep_node_incoming_address= my_strdup(PSI_INSTRUMENT_ME, WSREP_NODE_INCOMING_AUTO, MYF(MY_WME));
+ if (wsrep_gtid_mode)
+ wsrep_start_position = my_strdup(PSI_INSTRUMENT_ME, WSREP_START_POSITION_ZERO_GTID, MYF(MY_WME));
+ else
+ wsrep_start_position = my_strdup(PSI_INSTRUMENT_ME, WSREP_START_POSITION_ZERO, MYF(MY_WME));
return 0;
}
@@ -47,7 +50,7 @@ static int get_provider_option_value(const char* opts,
{
int ret= 1;
ulong opt_value_tmp;
- char *opt_value_str, *s, *opts_copy= my_strdup(opts, MYF(MY_WME));
+ char *opt_value_str, *s, *opts_copy= my_strdup(PSI_INSTRUMENT_ME, opts, MYF(MY_WME));
if ((opt_value_str= strstr(opts_copy, opt_name)) == NULL)
goto end;
@@ -179,6 +182,13 @@ bool wsrep_sync_wait_update (sys_var* self, THD* thd, enum_var_type var_type)
return false;
}
+template<typename T>
+static T parse_value(char** startptr, char** endptr)
+{
+ T val= strtoll(*startptr, *&endptr, 10);
+ *startptr= *endptr;
+ return val;
+}
/*
Verify the format of the given UUID:seqno.
@@ -212,8 +222,25 @@ bool wsrep_start_position_verify (const char* start_str)
return true;
char* endptr;
+ char* startptr= (char *)start_str + uuid_len + 1;
wsrep_seqno_t const seqno __attribute__((unused)) // to avoid GCC warnings
- (strtoll(&start_str[uuid_len + 1], &endptr, 10));
+ (parse_value<uint64_t>(&startptr, &endptr));
+
+ // Start parsing native GTID part
+ if (*startptr == ',')
+ {
+ startptr++;
+ uint32_t domain __attribute__((unused))
+ (parse_value<uint32_t>(&startptr, &endptr));
+ if (*endptr != '-') return true;
+ startptr++;
+ uint32_t server __attribute__((unused))
+ (parse_value<uint32_t>(&startptr, &endptr));
+ if (*endptr != '-') return true;
+ startptr++;
+ uint64_t seq __attribute__((unused))
+ (parse_value<uint64_t>(&startptr, &endptr));
+ }
// Remaining string was seqno.
if (*endptr == '\0') return false;
@@ -226,9 +253,22 @@ static
bool wsrep_set_local_position(THD* thd, const char* const value,
size_t length, bool const sst)
{
+ char* endptr;
+ char* startptr;
wsrep_uuid_t uuid;
size_t const uuid_len= wsrep_uuid_scan(value, length, &uuid);
- wsrep_seqno_t const seqno= strtoll(value + uuid_len + 1, NULL, 10);
+ startptr= (char *)value + uuid_len + 1;
+ wsrep_seqno_t const seqno= parse_value<uint64_t>(&startptr, &endptr);
+
+ if (*startptr == ',')
+ {
+ startptr++;
+ wsrep_gtid_server.domain_id= parse_value<uint32_t>(&startptr, &endptr);
+ startptr++;
+ wsrep_gtid_server.server_id= parse_value<uint32_t>(&startptr, &endptr);
+ startptr++;
+ wsrep_gtid_server.seqno(parse_value<uint64_t>(&startptr, &endptr));
+ }
if (sst) {
wsrep_sst_received (thd, uuid, seqno, NULL, 0);
@@ -411,7 +451,7 @@ void wsrep_provider_init (const char* value)
}
if (wsrep_provider) my_free((void *)wsrep_provider);
- wsrep_provider= my_strdup(value, MYF(0));
+ wsrep_provider= my_strdup(PSI_INSTRUMENT_MEM, value, MYF(0));
wsrep_set_wsrep_on();
}
@@ -442,7 +482,7 @@ void wsrep_provider_options_init(const char* value)
{
if (wsrep_provider_options && wsrep_provider_options != value)
my_free((void *)wsrep_provider_options);
- wsrep_provider_options= (value) ? my_strdup(value, MYF(0)) : NULL;
+ wsrep_provider_options= value ? my_strdup(PSI_INSTRUMENT_MEM, value, MYF(0)) : NULL;
}
bool wsrep_reject_queries_update(sys_var *self, THD* thd, enum_var_type type)
@@ -473,6 +513,15 @@ bool wsrep_debug_update(sys_var *self, THD* thd, enum_var_type type)
return false;
}
+bool
+wsrep_gtid_seq_no_check(sys_var *self, THD *thd, set_var *var)
+{
+ ulonglong new_wsrep_gtid_seq_no= var->save_result.ulonglong_value;
+ if (wsrep_gtid_mode && new_wsrep_gtid_seq_no > wsrep_gtid_server.seqno())
+ return false;
+ return true;
+}
+
static int wsrep_cluster_address_verify (const char* cluster_address_str)
{
/* There is no predefined address format, it depends on provider. */
@@ -545,8 +594,8 @@ void wsrep_cluster_address_init (const char* value)
(wsrep_cluster_address) ? wsrep_cluster_address : "null",
(value) ? value : "null");
- my_free((void*) wsrep_cluster_address);
- wsrep_cluster_address= my_strdup(value ? value : "", MYF(0));
+ my_free(const_cast<char*>(wsrep_cluster_address));
+ wsrep_cluster_address= my_strdup(PSI_INSTRUMENT_MEM, safe_str(value), MYF(0));
}
/* wsrep_cluster_name cannot be NULL or an empty string. */
@@ -619,7 +668,7 @@ void wsrep_node_address_init (const char* value)
if (wsrep_node_address && strcmp(wsrep_node_address, value))
my_free ((void*)wsrep_node_address);
- wsrep_node_address= (value) ? my_strdup(value, MYF(0)) : NULL;
+ wsrep_node_address= value ? my_strdup(PSI_INSTRUMENT_MEM, value, MYF(0)) : NULL;
}
static void wsrep_slave_count_change_update ()
@@ -900,6 +949,11 @@ int wsrep_show_status (THD *thd, SHOW_VAR *var, void *,
var->type= SHOW_ARRAY;
var->value= (char *) &mysql_status_vars;
}
+ else
+ {
+ var->type= SHOW_CHAR;
+ var->value= (char*) "0";
+ }
return 0;
}
diff --git a/sql/wsrep_var.h b/sql/wsrep_var.h
index 481df02f2d5..810ed4f3dd7 100644
--- a/sql/wsrep_var.h
+++ b/sql/wsrep_var.h
@@ -20,9 +20,10 @@
#ifdef WITH_WSREP
-#define WSREP_CLUSTER_NAME "my_wsrep_cluster"
-#define WSREP_NODE_INCOMING_AUTO "AUTO"
-#define WSREP_START_POSITION_ZERO "00000000-0000-0000-0000-000000000000:-1"
+#define WSREP_CLUSTER_NAME "my_wsrep_cluster"
+#define WSREP_NODE_INCOMING_AUTO "AUTO"
+#define WSREP_START_POSITION_ZERO "00000000-0000-0000-0000-000000000000:-1"
+#define WSREP_START_POSITION_ZERO_GTID "00000000-0000-0000-0000-000000000000:-1,0-0-0"
// MySQL variables funcs
@@ -102,6 +103,8 @@ extern bool wsrep_reject_queries_update UPDATE_ARGS;
extern bool wsrep_debug_update UPDATE_ARGS;
+extern bool wsrep_gtid_seq_no_check CHECK_ARGS;
+
#else /* WITH_WSREP */
#define wsrep_provider_init(X)
diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc
index d8f6e013820..34eafe9c46c 100644
--- a/sql/wsrep_xid.cc
+++ b/sql/wsrep_xid.cc
@@ -33,20 +33,24 @@
#define WSREP_XID_VERSION_OFFSET WSREP_XID_PREFIX_LEN
#define WSREP_XID_VERSION_1 'd'
#define WSREP_XID_VERSION_2 'e'
+#define WSREP_XID_VERSION_3 'f'
#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))
+#define WSREP_XID_GTRID_LEN_V_1_2 (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t))
+#define WSREP_XID_RPL_GTID_OFFSET (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t))
+#define WSREP_XID_GTRID_LEN_V_3 (WSREP_XID_RPL_GTID_OFFSET + sizeof(wsrep_server_gtid_t))
-void wsrep_xid_init(XID* xid, const wsrep::gtid& wsgtid)
+void wsrep_xid_init(XID* xid, const wsrep::gtid& wsgtid, const wsrep_server_gtid_t& gtid)
{
xid->formatID= 1;
- xid->gtrid_length= WSREP_XID_GTRID_LEN;
+ xid->gtrid_length= WSREP_XID_GTRID_LEN_V_3;
xid->bqual_length= 0;
memset(xid->data, 0, sizeof(xid->data));
memcpy(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN);
- xid->data[WSREP_XID_VERSION_OFFSET]= WSREP_XID_VERSION_2;
+ xid->data[WSREP_XID_VERSION_OFFSET]= WSREP_XID_VERSION_3;
memcpy(xid->data + WSREP_XID_UUID_OFFSET, wsgtid.id().data(),sizeof(wsrep::id));
int8store(xid->data + WSREP_XID_SEQNO_OFFSET, wsgtid.seqno().get());
+ memcpy(xid->data + WSREP_XID_RPL_GTID_OFFSET, &gtid, sizeof(wsrep_server_gtid_t));
}
extern "C"
@@ -54,11 +58,14 @@ int wsrep_is_wsrep_xid(const void* xid_ptr)
{
const XID* xid= static_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) &&
- (xid->data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_1 ||
- xid->data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_2));
+ xid->gtrid_length >= static_cast<long>(WSREP_XID_GTRID_LEN_V_1_2) &&
+ !memcmp(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN) &&
+ (((xid->data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_1 ||
+ xid->data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_2) &&
+ xid->gtrid_length == WSREP_XID_GTRID_LEN_V_1_2) ||
+ (xid->data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_3 &&
+ xid->gtrid_length == WSREP_XID_GTRID_LEN_V_3)));
}
const unsigned char* wsrep_xid_uuid(const xid_t* xid)
@@ -90,6 +97,7 @@ long long wsrep_xid_seqno(const xid_t* xid)
memcpy(&ret, xid->data + WSREP_XID_SEQNO_OFFSET, sizeof ret);
break;
case WSREP_XID_VERSION_2:
+ case WSREP_XID_VERSION_3:
ret= sint8korr(xid->data + WSREP_XID_SEQNO_OFFSET);
break;
default:
@@ -127,10 +135,10 @@ bool wsrep_set_SE_checkpoint(XID& xid)
&xid);
}
-bool wsrep_set_SE_checkpoint(const wsrep::gtid& wsgtid)
+bool wsrep_set_SE_checkpoint(const wsrep::gtid& wsgtid, const wsrep_server_gtid_t& gtid)
{
XID xid;
- wsrep_xid_init(&xid, wsgtid);
+ wsrep_xid_init(&xid, wsgtid, gtid);
return wsrep_set_SE_checkpoint(xid);
}
@@ -158,30 +166,61 @@ bool wsrep_get_SE_checkpoint(XID& xid)
&xid);
}
-wsrep::gtid wsrep_get_SE_checkpoint()
+static bool wsrep_get_SE_checkpoint_common(XID& xid)
{
- XID xid;
xid.null();
if (wsrep_get_SE_checkpoint(xid))
{
- return wsrep::gtid();
+ return FALSE;
}
if (xid.is_null())
{
- return wsrep::gtid();
+ return FALSE;
}
if (!wsrep_is_wsrep_xid(&xid))
{
WSREP_WARN("Read non-wsrep XID from storage engines.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+template<>
+wsrep::gtid wsrep_get_SE_checkpoint()
+{
+ XID xid;
+
+ if (!wsrep_get_SE_checkpoint_common(xid))
+ {
return wsrep::gtid();
}
return wsrep::gtid(wsrep_xid_uuid(xid),wsrep_xid_seqno(xid));
}
+template<>
+wsrep_server_gtid_t wsrep_get_SE_checkpoint()
+{
+ XID xid;
+ wsrep_server_gtid_t gtid= {0,0,0};
+
+ if (!wsrep_get_SE_checkpoint_common(xid))
+ {
+ return gtid;
+ }
+
+ if (xid.data[WSREP_XID_VERSION_OFFSET] == WSREP_XID_VERSION_3)
+ {
+ memcpy(&gtid, &xid.data[WSREP_XID_RPL_GTID_OFFSET], sizeof(wsrep_server_gtid_t));
+ }
+
+ return gtid;
+}
+
/*
Sort order for XIDs. Wsrep XIDs are sorted according to
seqno in ascending order. Non-wsrep XIDs are considered
diff --git a/sql/wsrep_xid.h b/sql/wsrep_xid.h
index a1b9afc1817..45ba6ffee6b 100644
--- a/sql/wsrep_xid.h
+++ b/sql/wsrep_xid.h
@@ -20,15 +20,16 @@
#ifdef WITH_WSREP
+#include "wsrep_mysqld.h"
#include "wsrep/gtid.hpp"
#include "handler.h" // XID typedef
-void wsrep_xid_init(xid_t*, const wsrep::gtid&);
+void wsrep_xid_init(xid_t*, const wsrep::gtid&, const wsrep_server_gtid_t&);
const wsrep::id& wsrep_xid_uuid(const XID&);
wsrep::seqno wsrep_xid_seqno(const XID&);
-wsrep::gtid wsrep_get_SE_checkpoint();
-bool wsrep_set_SE_checkpoint(const wsrep::gtid& gtid);
+template<typename T> T wsrep_get_SE_checkpoint();
+bool wsrep_set_SE_checkpoint(const wsrep::gtid& gtid, const wsrep_server_gtid_t&);
//void wsrep_get_SE_checkpoint(XID&); /* uncomment if needed */
//void wsrep_set_SE_checkpoint(XID&); /* uncomment if needed */
diff --git a/sql/xa.cc b/sql/xa.cc
index b08ab973cc5..15833377fb6 100644
--- a/sql/xa.cc
+++ b/sql/xa.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2009, 2019, MariaDB Corporation.
+ Copyright (c) 2009, 2020, MariaDB Corporation.
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,14 +20,14 @@
#include "sql_class.h"
#include "transaction.h"
#include "my_cpu.h"
+#include <pfs_transaction_provider.h>
+#include <mysql/psi/mysql_transaction.h>
+static bool slave_applier_reset_xa_trans(THD *thd);
/***************************************************************************
- Handling of XA id cacheing
+ Handling of XA id caching
***************************************************************************/
-enum xa_states { XA_ACTIVE= 0, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY };
-
-
struct XID_cache_insert_element
{
enum xa_states xa_state;
@@ -159,6 +159,12 @@ static LF_HASH xid_cache;
static bool xid_cache_inited;
+enum xa_states XID_STATE::get_state_code() const
+{
+ return xid_cache_element ? xid_cache_element->xa_state : XA_NO_STATE;
+}
+
+
bool THD::fix_xid_hash_pins()
{
if (!xid_hash_pins)
@@ -177,9 +183,8 @@ void XID_STATE::set_error(uint error)
void XID_STATE::er_xaer_rmfail() const
{
static const char *xa_state_names[]=
- { "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY" };
- my_error(ER_XAER_RMFAIL, MYF(0), is_explicit_XA() ?
- xa_state_names[xid_cache_element->xa_state] : "NON-EXISTING");
+ { "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY", "NON-EXISTING"};
+ my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[get_state_code()]);
}
@@ -381,7 +386,7 @@ static bool xa_trans_rolled_back(XID_cache_element *element)
@return TRUE if the rollback failed, FALSE otherwise.
*/
-static bool xa_trans_force_rollback(THD *thd)
+bool xa_trans_force_rollback(THD *thd)
{
bool rc= false;
@@ -390,13 +395,13 @@ static bool xa_trans_force_rollback(THD *thd)
my_error(ER_XAER_RMERR, MYF(0));
rc= true;
}
-
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.reset();
+ thd->variables.option_bits&=
+ ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_GTID_BEGIN);
+ thd->transaction->all.reset();
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
- xid_cache_delete(thd, &thd->transaction.xid_state);
+ xid_cache_delete(thd, &thd->transaction->xid_state);
trans_track_end_trx(thd);
@@ -417,29 +422,35 @@ bool trans_xa_start(THD *thd)
{
DBUG_ENTER("trans_xa_start");
- if (thd->transaction.xid_state.is_explicit_XA() &&
- thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ if (thd->transaction->xid_state.is_explicit_XA() &&
+ thd->transaction->xid_state.xid_cache_element->xa_state == XA_IDLE &&
thd->lex->xa_opt == XA_RESUME)
{
bool not_equal=
- !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid);
+ !thd->transaction->xid_state.xid_cache_element->xid.eq(thd->lex->xid);
if (not_equal)
my_error(ER_XAER_NOTA, MYF(0));
else
- thd->transaction.xid_state.xid_cache_element->xa_state= XA_ACTIVE;
+ {
+ thd->transaction->xid_state.xid_cache_element->xa_state= XA_ACTIVE;
+ MYSQL_SET_TRANSACTION_XA_STATE(thd->m_transaction_psi, XA_ACTIVE);
+ }
DBUG_RETURN(not_equal);
}
/* TODO: JOIN is not supported yet. */
if (thd->lex->xa_opt != XA_NONE)
my_error(ER_XAER_INVAL, MYF(0));
- else if (thd->transaction.xid_state.is_explicit_XA())
- thd->transaction.xid_state.er_xaer_rmfail();
+ else if (!thd->lex->xid->gtrid_length)
+ my_error(ER_XAER_INVAL, MYF(0));
+ else if (thd->transaction->xid_state.is_explicit_XA())
+ thd->transaction->xid_state.er_xaer_rmfail();
else if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
my_error(ER_XAER_OUTSIDE, MYF(0));
else if (!trans_begin(thd))
{
- if (xid_cache_insert(thd, &thd->transaction.xid_state, thd->lex->xid))
+ MYSQL_SET_TRANSACTION_XID(thd->m_transaction_psi, thd->lex->xid, XA_ACTIVE);
+ if (xid_cache_insert(thd, &thd->transaction->xid_state, thd->lex->xid))
{
trans_rollback(thd);
DBUG_RETURN(true);
@@ -467,16 +478,19 @@ bool trans_xa_end(THD *thd)
/* TODO: SUSPEND and FOR MIGRATE are not supported yet. */
if (thd->lex->xa_opt != XA_NONE)
my_error(ER_XAER_INVAL, MYF(0));
- else if (!thd->transaction.xid_state.is_explicit_XA() ||
- thd->transaction.xid_state.xid_cache_element->xa_state != XA_ACTIVE)
- thd->transaction.xid_state.er_xaer_rmfail();
- else if (!thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ else if (!thd->transaction->xid_state.is_explicit_XA() ||
+ thd->transaction->xid_state.xid_cache_element->xa_state != XA_ACTIVE)
+ thd->transaction->xid_state.er_xaer_rmfail();
+ else if (!thd->transaction->xid_state.xid_cache_element->xid.eq(thd->lex->xid))
my_error(ER_XAER_NOTA, MYF(0));
- else if (!xa_trans_rolled_back(thd->transaction.xid_state.xid_cache_element))
- thd->transaction.xid_state.xid_cache_element->xa_state= XA_IDLE;
+ else if (!xa_trans_rolled_back(thd->transaction->xid_state.xid_cache_element))
+ {
+ thd->transaction->xid_state.xid_cache_element->xa_state= XA_IDLE;
+ MYSQL_SET_TRANSACTION_XA_STATE(thd->m_transaction_psi, XA_IDLE);
+ }
DBUG_RETURN(thd->is_error() ||
- thd->transaction.xid_state.xid_cache_element->xa_state != XA_IDLE);
+ thd->transaction->xid_state.xid_cache_element->xa_state != XA_IDLE);
}
@@ -491,23 +505,50 @@ bool trans_xa_end(THD *thd)
bool trans_xa_prepare(THD *thd)
{
+ int res= 1;
+
DBUG_ENTER("trans_xa_prepare");
- if (!thd->transaction.xid_state.is_explicit_XA() ||
- thd->transaction.xid_state.xid_cache_element->xa_state != XA_IDLE)
- thd->transaction.xid_state.er_xaer_rmfail();
- else if (!thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ if (!thd->transaction->xid_state.is_explicit_XA() ||
+ thd->transaction->xid_state.xid_cache_element->xa_state != XA_IDLE)
+ thd->transaction->xid_state.er_xaer_rmfail();
+ else if (!thd->transaction->xid_state.xid_cache_element->xid.eq(thd->lex->xid))
my_error(ER_XAER_NOTA, MYF(0));
- else if (ha_prepare(thd))
+ else
{
- xid_cache_delete(thd, &thd->transaction.xid_state);
- my_error(ER_XA_RBROLLBACK, MYF(0));
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ MDL_request mdl_request;
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout) ||
+ ha_prepare(thd))
+ {
+ if (!mdl_request.ticket)
+ ha_rollback_trans(thd, TRUE);
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction->all.reset();
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ xid_cache_delete(thd, &thd->transaction->xid_state);
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ }
+ else
+ {
+ thd->transaction->xid_state.xid_cache_element->xa_state= XA_PREPARED;
+ MYSQL_SET_TRANSACTION_XA_STATE(thd->m_transaction_psi, XA_PREPARED);
+ res= thd->variables.pseudo_slave_mode || thd->slave_thread ?
+ slave_applier_reset_xa_trans(thd) : 0;
+ }
}
- else
- thd->transaction.xid_state.xid_cache_element->xa_state= XA_PREPARED;
- DBUG_RETURN(thd->is_error() ||
- thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED);
+ DBUG_RETURN(res);
}
@@ -522,12 +563,32 @@ bool trans_xa_prepare(THD *thd)
bool trans_xa_commit(THD *thd)
{
- bool res= TRUE;
+ bool res= true;
+ XID_STATE &xid_state= thd->transaction->xid_state;
+
DBUG_ENTER("trans_xa_commit");
- if (!thd->transaction.xid_state.is_explicit_XA() ||
- !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ if (!xid_state.is_explicit_XA() ||
+ !xid_state.xid_cache_element->xid.eq(thd->lex->xid))
{
+ if (thd->in_multi_stmt_transaction_mode())
+ {
+ /*
+ Not allow to commit from inside an not-"native" to xid
+ ongoing transaction: the commit effect can't be reversed.
+ */
+ my_error(ER_XAER_OUTSIDE, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (thd->lex->xa_opt != XA_NONE)
+ {
+ /*
+ Not allow to commit with one phase a prepared xa out of compatibility
+ with the native commit branch's error out.
+ */
+ my_error(ER_XAER_INVAL, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
if (thd->fix_xid_hash_pins())
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -537,7 +598,44 @@ bool trans_xa_commit(THD *thd)
if (auto xs= xid_cache_search(thd, thd->lex->xid))
{
res= xa_trans_rolled_back(xs);
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ MDL_request mdl_request;
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
+ DBUG_ASSERT(thd->is_error());
+
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
+ DBUG_ASSERT(!xid_state.xid_cache_element);
+
+ if (thd->wait_for_prior_commit())
+ {
+ DBUG_ASSERT(thd->is_error());
+
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
+
+ xid_state.xid_cache_element= xs;
ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
+ xid_state.xid_cache_element= 0;
+
+ res= res || thd->is_error();
xid_cache_delete(thd, xs);
}
else
@@ -545,22 +643,26 @@ bool trans_xa_commit(THD *thd)
DBUG_RETURN(res);
}
- if (xa_trans_rolled_back(thd->transaction.xid_state.xid_cache_element))
+ if (xa_trans_rolled_back(xid_state.xid_cache_element))
{
xa_trans_force_rollback(thd);
DBUG_RETURN(thd->is_error());
}
- else if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ else if (xid_state.xid_cache_element->xa_state == XA_IDLE &&
thd->lex->xa_opt == XA_ONE_PHASE)
{
int r= ha_commit_trans(thd, TRUE);
if ((res= MY_TEST(r)))
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
}
- else if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_PREPARED &&
- thd->lex->xa_opt == XA_NONE)
+ else if (thd->transaction->xid_state.xid_cache_element->xa_state == XA_PREPARED)
{
MDL_request mdl_request;
+ if (thd->lex->xa_opt != XA_NONE)
+ {
+ my_error(ER_XAER_INVAL, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
/*
Acquire metadata lock which will ensure that COMMIT is blocked
@@ -569,14 +671,19 @@ bool trans_xa_commit(THD *thd)
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
*/
- mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_TRANSACTION);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
- ha_rollback_trans(thd, TRUE);
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
my_error(ER_XAER_RMERR, MYF(0));
+ DBUG_RETURN(true);
}
else
{
@@ -585,23 +692,34 @@ bool trans_xa_commit(THD *thd)
res= MY_TEST(ha_commit_one_phase(thd, 1));
if (res)
my_error(ER_XAER_RMERR, MYF(0));
+ else
+ {
+ /*
+ Since we don't call ha_commit_trans() for prepared transactions,
+ we need to explicitly mark the transaction as committed.
+ */
+ MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi);
+ }
+
+ thd->m_transaction_psi= NULL;
}
}
else
{
- thd->transaction.xid_state.er_xaer_rmfail();
+ xid_state.er_xaer_rmfail();
DBUG_RETURN(TRUE);
}
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
- thd->transaction.all.reset();
+ thd->transaction->all.reset();
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
- xid_cache_delete(thd, &thd->transaction.xid_state);
+ xid_cache_delete(thd, &xid_state);
trans_track_end_trx(thd);
-
+ /* The transaction should be marked as complete in P_S. */
+ DBUG_ASSERT(thd->m_transaction_psi == NULL || res);
DBUG_RETURN(res);
}
@@ -617,11 +735,18 @@ bool trans_xa_commit(THD *thd)
bool trans_xa_rollback(THD *thd)
{
+ XID_STATE &xid_state= thd->transaction->xid_state;
+
DBUG_ENTER("trans_xa_rollback");
- if (!thd->transaction.xid_state.is_explicit_XA() ||
- !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ if (!xid_state.is_explicit_XA() ||
+ !xid_state.xid_cache_element->xid.eq(thd->lex->xid))
{
+ if (thd->in_multi_stmt_transaction_mode())
+ {
+ my_error(ER_XAER_OUTSIDE, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
if (thd->fix_xid_hash_pins())
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -630,8 +755,35 @@ bool trans_xa_rollback(THD *thd)
if (auto xs= xid_cache_search(thd, thd->lex->xid))
{
+ MDL_request mdl_request;
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
+ DBUG_ASSERT(thd->is_error());
+
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
xa_trans_rolled_back(xs);
+ DBUG_ASSERT(!xid_state.xid_cache_element);
+
+ if (thd->wait_for_prior_commit())
+ {
+ DBUG_ASSERT(thd->is_error());
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
+
+ xid_state.xid_cache_element= xs;
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ xid_state.xid_cache_element= 0;
xid_cache_delete(thd, xs);
}
else
@@ -639,29 +791,49 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(thd->get_stmt_da()->is_error());
}
- if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_ACTIVE)
+ if (xid_state.xid_cache_element->xa_state == XA_ACTIVE)
{
- thd->transaction.xid_state.er_xaer_rmfail();
+ xid_state.er_xaer_rmfail();
DBUG_RETURN(TRUE);
}
+
+ MDL_request mdl_request;
+ MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
+ my_error(ER_XAER_RMERR, MYF(0));
+ DBUG_RETURN(true);
+ }
+
DBUG_RETURN(xa_trans_force_rollback(thd));
}
bool trans_xa_detach(THD *thd)
{
- DBUG_ASSERT(thd->transaction.xid_state.is_explicit_XA());
-#if 1
- return xa_trans_force_rollback(thd);
-#else
- if (thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED)
+ DBUG_ASSERT(thd->transaction->xid_state.is_explicit_XA());
+
+ if (thd->transaction->xid_state.xid_cache_element->xa_state != XA_PREPARED)
return xa_trans_force_rollback(thd);
- thd->transaction.xid_state.xid_cache_element->acquired_to_recovered();
- thd->transaction.xid_state.xid_cache_element= 0;
- thd->transaction.cleanup();
+ else if (!thd->transaction->all.is_trx_read_write())
+ {
+ thd->transaction->xid_state.set_error(ER_XA_RBROLLBACK);
+ ha_rollback_trans(thd, true);
+ }
+
+ thd->transaction->xid_state.xid_cache_element->acquired_to_recovered();
+ thd->transaction->xid_state.xid_cache_element= 0;
+ thd->transaction->cleanup();
Ha_trx_info *ha_info, *ha_info_next;
- for (ha_info= thd->transaction.all.ha_list;
+ for (ha_info= thd->transaction->all.ha_list;
ha_info;
ha_info= ha_info_next)
{
@@ -669,10 +841,10 @@ bool trans_xa_detach(THD *thd)
ha_info->reset(); /* keep it conveniently zero-filled */
}
- thd->transaction.all.ha_list= 0;
- thd->transaction.all.no_2pc= 0;
+ thd->transaction->all.ha_list= 0;
+ thd->transaction->all.no_2pc= 0;
+ thd->m_transaction_psi= 0;
return false;
-#endif
}
@@ -815,7 +987,7 @@ static my_bool xa_recover_callback_verbose(XID_cache_element *xs,
char buf[SQL_XIDSIZE];
uint len= get_sql_xid(&xs->xid, buf);
return xa_recover_callback(xs, protocol, buf, len,
- &my_charset_utf8_general_ci);
+ &my_charset_utf8mb3_general_ci);
}
@@ -843,7 +1015,7 @@ bool mysql_xa_recover(THD *thd)
if (thd->lex->verbose)
{
len= SQL_XIDSIZE;
- cs= &my_charset_utf8_general_ci;
+ cs= &my_charset_utf8mb3_general_ci;
action= (my_hash_walk_action) xa_recover_callback_verbose;
}
else
@@ -866,3 +1038,45 @@ bool mysql_xa_recover(THD *thd)
my_eof(thd);
DBUG_RETURN(0);
}
+
+
+/**
+ This is a specific to (pseudo-) slave applier collection of standard cleanup
+ actions to reset XA transaction state sim to @c ha_commit_one_phase.
+ THD of the slave applier is dissociated from a transaction object in engine
+ that continues to exist there.
+
+ @param THD current thread
+ @return the value of is_error()
+*/
+
+static bool slave_applier_reset_xa_trans(THD *thd)
+{
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+
+ thd->transaction->xid_state.xid_cache_element->acquired_to_recovered();
+ thd->transaction->xid_state.xid_cache_element= 0;
+
+ for (Ha_trx_info *ha_info= thd->transaction->all.ha_list, *ha_info_next;
+ ha_info; ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset();
+ }
+ thd->transaction->all.ha_list= 0;
+
+ ha_close_connection(thd);
+ thd->transaction->cleanup();
+ thd->transaction->all.reset();
+
+ DBUG_ASSERT(!thd->transaction->all.ha_list);
+ DBUG_ASSERT(!thd->transaction->all.no_2pc);
+
+ thd->has_waiter= false;
+ MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi); // TODO/Fixme: commit?
+ thd->m_transaction_psi= NULL;
+ return thd->is_error();
+}
diff --git a/sql/xa.h b/sql/xa.h
index 7cf74efad35..0b2d0696642 100644
--- a/sql/xa.h
+++ b/sql/xa.h
@@ -1,3 +1,5 @@
+#ifndef XA_INCLUDED
+#define XA_INCLUDED
/*
Copyright (c) 2000, 2016, Oracle and/or its affiliates.
Copyright (c) 2009, 2019, MariaDB Corporation.
@@ -16,8 +18,15 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-
class XID_cache_element;
+enum xa_states
+{
+ XA_ACTIVE= 0,
+ XA_IDLE,
+ XA_PREPARED,
+ XA_ROLLBACK_ONLY,
+ XA_NO_STATE
+};
struct XID_STATE {
XID_cache_element *xid_cache_element;
@@ -27,6 +36,7 @@ struct XID_STATE {
void set_error(uint error);
void er_xaer_rmfail() const;
XID *get_xid() const;
+ enum xa_states get_state_code() const;
};
void xid_cache_init(void);
@@ -42,3 +52,5 @@ bool trans_xa_commit(THD *thd);
bool trans_xa_rollback(THD *thd);
bool trans_xa_detach(THD *thd);
bool mysql_xa_recover(THD *thd);
+
+#endif /* XA_INCLUDED */